diff --git a/.github/workflows/ymlPLYtoSTL.yml b/.github/workflows/ymlPLYtoSTL.yml new file mode 100644 index 0000000..668651d --- /dev/null +++ b/.github/workflows/ymlPLYtoSTL.yml @@ -0,0 +1,248 @@ +```yaml +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" +```