name: Convert PLY to STL on: push: paths: - '**/*.ply' workflow_dispatch: permissions: contents: write jobs: convert: runs-on: ubuntu-latest steps: # ------------------------------------------------------------ # Récupération du dépôt # ------------------------------------------------------------ - name: Checkout repository uses: actions/checkout@v6 # ------------------------------------------------------------ # Installation de Python # ------------------------------------------------------------ - name: Setup Python uses: actions/setup-python@v7 with: python-version: '3.13' # ------------------------------------------------------------ # Installation des bibliothèques # ------------------------------------------------------------ - name: Install dependencies run: | python -m pip install --upgrade pip pip install trimesh numpy fast-simplification # ------------------------------------------------------------ # Conversion PLY -> STL # Recherche automatique du meilleur compromis qualité/taille # ------------------------------------------------------------ - name: Convert PLY files to STL run: | python <<'PY' import trimesh import glob import os import tempfile # Taille maximale autorisée. # 50 MiB = 50 * 1024 * 1024 octets MAX_SIZE = 50 * 1024 * 1024 # Marge de sécurité : # on vise légèrement sous 50 MiB afin d'éviter # tout dépassement lié aux métadonnées ou différences # de calcul. TARGET_SIZE = 49 * 1024 * 1024 # Ratios testés, du meilleur au moins bon. # Le script s'arrête dès qu'il trouve un fichier # suffisamment petit. RATIOS = [ 1.00, 0.90, 0.80, 0.70, 0.60, 0.50, 0.40, 0.30, 0.20, 0.10 ] ply_files = glob.glob('**/*.ply', recursive=True) if not ply_files: print("Aucun fichier PLY trouvé.") exit(0) for ply in ply_files: print() print("=" * 70) print(f"Fichier : {ply}") print("=" * 70) # ---------------------------------------------------- # Chargement du maillage # ---------------------------------------------------- mesh = trimesh.load(ply, force='mesh') if mesh.is_empty: print("ERREUR : maillage vide") continue if not isinstance(mesh, trimesh.Trimesh): print("ERREUR : le fichier ne contient pas un maillage triangulaire.") continue original_faces = len(mesh.faces) print(f"Triangles initiaux : {original_faces:,}") # ---------------------------------------------------- # Fichier STL temporaire # ---------------------------------------------------- base = os.path.splitext(ply)[0] stl = base + '.stl' best_mesh = None best_size = None best_ratio = None # ---------------------------------------------------- # Essai des différents niveaux de simplification # ---------------------------------------------------- for ratio in RATIOS: target_faces = max( 1000, int(original_faces * ratio) ) print() print( f"Test : {ratio * 100:.0f}% " f"({target_faces:,} triangles)" ) # ------------------------------------------------ # Pas de simplification pour 100 % # ------------------------------------------------ if ratio >= 1.0: test_mesh = mesh.copy() else: try: test_mesh = mesh.simplify_quadric_decimation( face_count=target_faces ) except Exception as e: print( "Erreur lors de la simplification : " f"{e}" ) continue # ------------------------------------------------ # Export STL temporaire # ------------------------------------------------ with tempfile.NamedTemporaryFile( suffix='.stl', delete=False ) as tmp: temp_stl = tmp.name try: test_mesh.export(temp_stl) size = os.path.getsize(temp_stl) finally: if os.path.exists(temp_stl): os.remove(temp_stl) size_mb = size / (1024 * 1024) print( f" -> {len(test_mesh.faces):,} triangles" ) print( f" -> {size_mb:.2f} MiB" ) # ------------------------------------------------ # On conserve le premier résultat sous 49 MiB. # # Comme les ratios sont testés du plus grand # au plus petit, c'est le meilleur résultat # possible parmi les valeurs testées. # ------------------------------------------------ if size < TARGET_SIZE: best_mesh = test_mesh best_size = size best_ratio = ratio print(" -> Taille acceptable") break # ---------------------------------------------------- # Aucun résultat suffisamment petit # ---------------------------------------------------- if best_mesh is None: print() print( "ERREUR : impossible d'obtenir un STL " f"inférieur à {TARGET_SIZE / (1024 * 1024):.0f} MiB." ) continue # ---------------------------------------------------- # Export définitif # ---------------------------------------------------- best_mesh.export(stl) final_size = os.path.getsize(stl) final_size_mb = final_size / (1024 * 1024) print() print("-" * 70) print("RESULTAT") print("-" * 70) print(f"Fichier STL : {stl}") print(f"Triangles : {len(best_mesh.faces):,}") print(f"Ratio : {best_ratio * 100:.0f}%") print(f"Taille : {final_size_mb:.2f} MiB") print(f"Taille limite : 50.00 MiB") if final_size >= MAX_SIZE: print("ATTENTION : fichier supérieur à 50 MiB !") else: print("OK : fichier inférieur à 50 MiB.") print("-" * 70) PY # ------------------------------------------------------------ # Commit des STL générés # ------------------------------------------------------------ - name: Commit converted STL files uses: stefanzweifel/git-auto-commit-action@v7 with: commit_message: "Auto-convert PLY to reduced STL"