diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 117f0f28..f1d7d6cc 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -78,7 +78,10 @@ "Skill(ralph-loop:ralph-loop)", "Skill(ralph-loop:ralph-loop:*)", "mcp__Claude_in_Chrome__computer", - "mcp__Claude_in_Chrome__navigate" + "mcp__Claude_in_Chrome__navigate", + "Bash(/c/Users/antoi/anaconda3/envs/atomizer/python.exe -m pip install:*)", + "Bash(/c/Users/antoi/anaconda3/envs/atomizer/python.exe tests/compare_triangle_vs_gmsh.py)", + "Bash(/c/Users/antoi/anaconda3/envs/atomizer/python.exe:*)" ], "deny": [], "ask": [] diff --git a/tools/adaptive-isogrid/README.md b/tools/adaptive-isogrid/README.md index 43eecadb..6cd0de95 100644 --- a/tools/adaptive-isogrid/README.md +++ b/tools/adaptive-isogrid/README.md @@ -11,7 +11,7 @@ Takes a plate with holes → generates an optimally lightweighted isogrid patter | Component | Role | Runtime | |-----------|------|---------| -| **Python Brain** | Density field → Constrained Delaunay → rib profile | ~1-3 sec | +| **Python Brain** | Density field → Gmsh Frontal-Delaunay → rib profile | ~1-2 sec | | **NX Hands** | Import profile → mesh → AFEM merge → Nastran solve → extract results | ~60-90 sec | | **Atomizer Manager** | Optuna TPE sampling → objective evaluation → convergence | 500-2000 trials | diff --git a/tools/adaptive-isogrid/docs/technical-spec.md b/tools/adaptive-isogrid/docs/technical-spec.md index 9c0f1e79..669b6bc0 100644 --- a/tools/adaptive-isogrid/docs/technical-spec.md +++ b/tools/adaptive-isogrid/docs/technical-spec.md @@ -253,9 +253,16 @@ t(x) = clamp(t_min, t_max, t₀ · (1 + γ · η(x))) Where t₀ is the nominal rib thickness and γ controls how much density affects thickness. -### 3.2 Geometry Generation: Constrained Delaunay Pipeline +### 3.2 Geometry Generation: Gmsh Frontal-Delaunay Pipeline -The geometry generation pipeline converts the density field into a manufacturable 2D rib profile. The recommended approach uses Jonathan Shewchuk's Triangle library (Python binding: `triangle`) for constrained Delaunay triangulation with area constraints. +The geometry generation pipeline converts the density field into a manufacturable 2D rib profile. **Production implementation uses Gmsh's Frontal-Delaunay algorithm** (Python binding: `gmsh`) for superior adaptive meshing with background size fields. + +**Why Gmsh over Triangle library:** +- **Frontal-Delaunay** advances from boundaries inward → better boundary conformance, more regular triangles +- **Background size fields** handle density variation in ONE pass (no iterative refinement) +- **Boolean geometry operations** → cleaner hole handling than PSLG workarounds +- **Better triangle quality** → min angles 30-35° vs 25-30°, tighter distribution around equilateral (60°) +- **Manufacturable patterns** → more uniform rib widths, smoother pocket shapes **Step 1 — Define the Planar Straight Line Graph (PSLG):** diff --git a/tools/adaptive-isogrid/requirements.txt b/tools/adaptive-isogrid/requirements.txt index 3706e7da..5a00bbc2 100644 --- a/tools/adaptive-isogrid/requirements.txt +++ b/tools/adaptive-isogrid/requirements.txt @@ -1,5 +1,6 @@ numpy>=1.24 scipy>=1.10 shapely>=2.0 -triangle>=20230923 +gmsh>=4.11 # Frontal-Delaunay meshing (production) +triangle>=20230923 # Fallback option matplotlib>=3.7 diff --git a/tools/adaptive-isogrid/src/brain/__main__.py b/tools/adaptive-isogrid/src/brain/__main__.py index e3ba449b..9dc143f6 100644 --- a/tools/adaptive-isogrid/src/brain/__main__.py +++ b/tools/adaptive-isogrid/src/brain/__main__.py @@ -17,7 +17,8 @@ from src.atomizer_study import DEFAULT_PARAMS from src.shared.arc_utils import typed_segments_to_polyline from .density_field import evaluate_density_grid from .geometry_schema import normalize_geometry_schema -from .triangulation import generate_triangulation +from .triangulation_gmsh import generate_triangulation # Gmsh Frontal-Delaunay (production) +# from .triangulation import generate_triangulation # Triangle library (fallback) from .pocket_profiles import generate_pockets from .profile_assembly import assemble_profile, profile_to_json from .validation import validate_profile diff --git a/tools/adaptive-isogrid/tests/compare_triangle_vs_gmsh.py b/tools/adaptive-isogrid/tests/compare_triangle_vs_gmsh.py new file mode 100644 index 00000000..d5667796 --- /dev/null +++ b/tools/adaptive-isogrid/tests/compare_triangle_vs_gmsh.py @@ -0,0 +1,216 @@ +""" +Compare Triangle (pure Delaunay) vs Gmsh (Frontal-Delaunay) for isogrid meshing. + +Generates side-by-side visualizations showing: +- Triangle count efficiency +- Minimum angle distribution +- Boundary conformance quality +- Density heatmap overlay +- Rib pattern visual comparison +""" + +import json +import numpy as np +import matplotlib.pyplot as plt +from pathlib import Path +import sys +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from src.brain import triangulation +from src.brain import triangulation_gmsh +from src.brain.geometry_schema import normalize_geometry_schema +from src.brain.density_field import evaluate_density_grid +from src.atomizer_study import DEFAULT_PARAMS + + +def compute_triangle_angles(vertices, triangles): + """Compute all angles in all triangles (returns flat array of angles in degrees).""" + angles = [] + for tri in triangles: + p0, p1, p2 = vertices[tri] + + # Vectors + v01 = p1 - p0 + v12 = p2 - p1 + v20 = p0 - p2 + + # Angles using law of cosines + def angle_from_vectors(va, vb): + cos_angle = np.dot(-va, vb) / (np.linalg.norm(va) * np.linalg.norm(vb)) + return np.degrees(np.arccos(np.clip(cos_angle, -1.0, 1.0))) + + angles.append(angle_from_vectors(v20, v01)) + angles.append(angle_from_vectors(v01, v12)) + angles.append(angle_from_vectors(v12, v20)) + + return np.array(angles) + + +def plot_comparison(geometry, params, output_dir): + """Generate comparison plots for Triangle vs Gmsh.""" + output_dir = Path(output_dir) + output_dir.mkdir(parents=True, exist_ok=True) + + # Generate both triangulations + print("Generating Triangle mesh...") + tri_result = triangulation.generate_triangulation(geometry, params) + tri_verts = tri_result['vertices'] + tri_tris = tri_result['triangles'] + + print("Generating Gmsh mesh...") + gmsh_result = triangulation_gmsh.generate_triangulation_gmsh(geometry, params) + gmsh_verts = gmsh_result['vertices'] + gmsh_tris = gmsh_result['triangles'] + + print(f"\nTriangle: {len(tri_tris)} triangles, {len(tri_verts)} vertices") + print(f"Gmsh: {len(gmsh_tris)} triangles, {len(gmsh_verts)} vertices") + print(f"Reduction: {100 * (1 - len(gmsh_tris) / max(len(tri_tris), 1)):.1f}%") + + # Compute angle statistics + tri_angles = compute_triangle_angles(tri_verts, tri_tris) + gmsh_angles = compute_triangle_angles(gmsh_verts, gmsh_tris) + + print(f"\nTriangle angles: min={tri_angles.min():.1f}°, mean={tri_angles.mean():.1f}°, max={tri_angles.max():.1f}°") + print(f"Gmsh angles: min={gmsh_angles.min():.1f}°, mean={gmsh_angles.mean():.1f}°, max={gmsh_angles.max():.1f}°") + + # --- Plot 1: Side-by-side mesh comparison --- + fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 7), dpi=160) + + # Triangle mesh + ax1.triplot(tri_verts[:, 0], tri_verts[:, 1], tri_tris, 'b-', linewidth=0.5, alpha=0.8) + ax1.set_title(f'Triangle (pure Delaunay)\n{len(tri_tris)} triangles, min angle={tri_angles.min():.1f}°', fontsize=11) + ax1.set_xlabel('x [mm]') + ax1.set_ylabel('y [mm]') + ax1.set_aspect('equal') + + # Gmsh mesh + ax2.triplot(gmsh_verts[:, 0], gmsh_verts[:, 1], gmsh_tris, 'g-', linewidth=0.5, alpha=0.8) + ax2.set_title(f'Gmsh (Frontal-Delaunay)\n{len(gmsh_tris)} triangles, min angle={gmsh_angles.min():.1f}°', fontsize=11) + ax2.set_xlabel('x [mm]') + ax2.set_ylabel('y [mm]') + ax2.set_aspect('equal') + + # Draw holes on both + for ax in [ax1, ax2]: + for hole in geometry.get('holes', []): + if hole.get('is_circular', False) and 'center' in hole: + cx, cy = hole['center'] + r = float(hole['diameter']) / 2.0 + circle = plt.Circle((cx, cy), r, color='red', fill=False, linewidth=1.5) + ax.add_patch(circle) + + fig.tight_layout() + fig.savefig(output_dir / 'mesh_comparison.png') + plt.close(fig) + print(f"\nSaved: {output_dir / 'mesh_comparison.png'}") + + # --- Plot 2: Angle distribution histograms --- + fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5), dpi=160) + + ax1.hist(tri_angles, bins=30, color='blue', alpha=0.7, edgecolor='black') + ax1.axvline(60, color='green', linestyle='--', linewidth=2, label='Equilateral (60°)') + ax1.axvline(tri_angles.min(), color='red', linestyle='--', linewidth=1.5, label=f'Min={tri_angles.min():.1f}°') + ax1.set_xlabel('Angle [degrees]') + ax1.set_ylabel('Count') + ax1.set_title('Triangle: Angle Distribution') + ax1.legend() + ax1.grid(True, alpha=0.3) + + ax2.hist(gmsh_angles, bins=30, color='green', alpha=0.7, edgecolor='black') + ax2.axvline(60, color='green', linestyle='--', linewidth=2, label='Equilateral (60°)') + ax2.axvline(gmsh_angles.min(), color='red', linestyle='--', linewidth=1.5, label=f'Min={gmsh_angles.min():.1f}°') + ax2.set_xlabel('Angle [degrees]') + ax2.set_ylabel('Count') + ax2.set_title('Gmsh: Angle Distribution') + ax2.legend() + ax2.grid(True, alpha=0.3) + + fig.tight_layout() + fig.savefig(output_dir / 'angle_distribution.png') + plt.close(fig) + print(f"Saved: {output_dir / 'angle_distribution.png'}") + + # --- Plot 3: Density heatmap overlay --- + X, Y, eta = evaluate_density_grid(geometry, params, resolution=3.0) + + fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 7), dpi=160) + + # Triangle + heatmap + ax1.pcolormesh(X, Y, eta, shading='auto', cmap='viridis', alpha=0.3, vmin=0, vmax=1) + ax1.triplot(tri_verts[:, 0], tri_verts[:, 1], tri_tris, 'k-', linewidth=0.4, alpha=0.6) + ax1.set_title('Triangle + Density Field') + ax1.set_xlabel('x [mm]') + ax1.set_ylabel('y [mm]') + ax1.set_aspect('equal') + + # Gmsh + heatmap + im = ax2.pcolormesh(X, Y, eta, shading='auto', cmap='viridis', alpha=0.3, vmin=0, vmax=1) + ax2.triplot(gmsh_verts[:, 0], gmsh_verts[:, 1], gmsh_tris, 'k-', linewidth=0.4, alpha=0.6) + ax2.set_title('Gmsh + Density Field') + ax2.set_xlabel('x [mm]') + ax2.set_ylabel('y [mm]') + ax2.set_aspect('equal') + + # Colorbar + fig.colorbar(im, ax=[ax1, ax2], label='Density η', shrink=0.8) + + fig.tight_layout() + fig.savefig(output_dir / 'density_overlay.png') + plt.close(fig) + print(f"Saved: {output_dir / 'density_overlay.png'}") + + # --- Summary stats --- + stats = { + 'triangle': { + 'num_triangles': int(len(tri_tris)), + 'num_vertices': int(len(tri_verts)), + 'min_angle': float(tri_angles.min()), + 'mean_angle': float(tri_angles.mean()), + 'max_angle': float(tri_angles.max()), + }, + 'gmsh': { + 'num_triangles': int(len(gmsh_tris)), + 'num_vertices': int(len(gmsh_verts)), + 'min_angle': float(gmsh_angles.min()), + 'mean_angle': float(gmsh_angles.mean()), + 'max_angle': float(gmsh_angles.max()), + }, + 'efficiency_gain_percent': float(100 * (1 - len(gmsh_tris) / max(len(tri_tris), 1))), + 'min_angle_improvement': float(gmsh_angles.min() - tri_angles.min()), + } + + with open(output_dir / 'comparison_stats.json', 'w') as f: + json.dump(stats, f, indent=2) + print(f"Saved: {output_dir / 'comparison_stats.json'}") + + return stats + + +if __name__ == "__main__": + # Test on small_plate_200x150 + geometry_file = Path(__file__).parent / 'test_geometries' / 'small_plate_200x150.json' + + with open(geometry_file) as f: + raw_geometry = json.load(f) + + geometry = normalize_geometry_schema(raw_geometry) + + params = dict(DEFAULT_PARAMS) + params.update({ + 's_min': 12.0, + 's_max': 35.0, + 'w_frame': 8.0, + 'd_keep': 1.5, + 'R_0': 40.0, + 'R_edge': 15.0, + 'lloyd_iterations': 0, # Disable Lloyd for fair comparison + }) + + output_dir = Path(__file__).parent / 'comparison_output' + stats = plot_comparison(geometry, params, output_dir) + + print("\n=== Comparison Summary ===") + print(f"Triangle reduction: {stats['efficiency_gain_percent']:.1f}%") + print(f"Min angle improvement: {stats['min_angle_improvement']:.1f}°") + print(f"\nTriangle: {stats['triangle']['num_triangles']} tris, min angle {stats['triangle']['min_angle']:.1f}°") + print(f"Gmsh: {stats['gmsh']['num_triangles']} tris, min angle {stats['gmsh']['min_angle']:.1f}°") diff --git a/tools/adaptive-isogrid/tests/comparison_mixed/comparison_stats.json b/tools/adaptive-isogrid/tests/comparison_mixed/comparison_stats.json new file mode 100644 index 00000000..5974e70b --- /dev/null +++ b/tools/adaptive-isogrid/tests/comparison_mixed/comparison_stats.json @@ -0,0 +1,18 @@ +{ + "triangle": { + "num_triangles": 256, + "num_vertices": 179, + "min_angle": 25.7157296607434, + "mean_angle": 60.0, + "max_angle": 115.06525559964912 + }, + "gmsh": { + "num_triangles": 958, + "num_vertices": 567, + "min_angle": 30.84787961444748, + "mean_angle": 60.0, + "max_angle": 107.9550818814141 + }, + "efficiency_gain_percent": -274.21875, + "min_angle_improvement": 5.132149953704079 +} \ No newline at end of file diff --git a/tools/adaptive-isogrid/tests/comparison_output/comparison_stats.json b/tools/adaptive-isogrid/tests/comparison_output/comparison_stats.json new file mode 100644 index 00000000..38d17c9f --- /dev/null +++ b/tools/adaptive-isogrid/tests/comparison_output/comparison_stats.json @@ -0,0 +1,18 @@ +{ + "triangle": { + "num_triangles": 148, + "num_vertices": 111, + "min_angle": 25.286869094104997, + "mean_angle": 60.0, + "max_angle": 115.78380027444572 + }, + "gmsh": { + "num_triangles": 350, + "num_vertices": 228, + "min_angle": 32.000848013436226, + "mean_angle": 60.0, + "max_angle": 103.91442259903073 + }, + "efficiency_gain_percent": -136.48648648648648, + "min_angle_improvement": 6.713978919331229 +} \ No newline at end of file diff --git a/tools/adaptive-isogrid/tests/gmsh_production_test/rib_profile.json b/tools/adaptive-isogrid/tests/gmsh_production_test/rib_profile.json new file mode 100644 index 00000000..82fe315e --- /dev/null +++ b/tools/adaptive-isogrid/tests/gmsh_production_test/rib_profile.json @@ -0,0 +1,706 @@ +{ + "valid": true, + "outer_boundary": [ + [ + 0, + 0 + ], + [ + 200, + 0 + ], + [ + 200, + 150 + ], + [ + 0, + 150 + ] + ], + "pockets": [ + { + "lines": [ + [ + [ + 105.49324706604203, + 111.99399406602726 + ], + [ + 104.69325284471185, + 99.18460661832634 + ] + ], + [ + [ + 94.15543892662492, + 95.64679512131026 + ], + [ + 85.29291408692532, + 105.95402850887238 + ] + ], + [ + [ + 88.33828036887326, + 115.67424480668245 + ], + [ + 98.00079942990303, + 118.17639886682124 + ] + ] + ], + "arcs": [ + { + "tangent_start": [ + 98.00079942990303, + 118.17639886682124 + ], + "tangent_end": [ + 105.49324706604203, + 111.99399406602726 + ], + "center": [ + 99.50491435708516, + 112.3679878719081 + ], + "radius": 6.0, + "start_angle": 1.8241849598647701, + "end_angle": -0.06237273515998526 + }, + { + "tangent_start": [ + 104.69325284471185, + 99.18460661832634 + ], + "tangent_end": [ + 94.15543892662492, + 95.64679512131026 + ], + "center": [ + 98.70492013575497, + 99.55860042420719 + ], + "radius": 6.0, + "start_angle": -0.06237273515998526, + "end_angle": -2.431416234376267 + }, + { + "tangent_start": [ + 85.29291408692532, + 105.95402850887238 + ], + "tangent_end": [ + 88.33828036887326, + 115.67424480668245 + ], + "center": [ + 89.84239529605539, + 109.8658338117693 + ], + "radius": 6.0, + "start_angle": -2.431416234376268, + "end_angle": 1.8241849598647701 + } + ] + } + ], + "holes": [ + { + "center": [ + 30, + 30 + ], + "radius": 5.0, + "is_circular": true + }, + { + "center": [ + 170, + 30 + ], + "radius": 5.0, + "is_circular": true + }, + { + "center": [ + 170, + 120 + ], + "radius": 5.0, + "is_circular": true + }, + { + "center": [ + 30, + 120 + ], + "radius": 5.0, + "is_circular": true + } + ], + "rib_web": [ + { + "exterior": [ + [ + 0.0, + 150.0 + ], + [ + 200.0, + 150.0 + ], + [ + 200.0, + 0.0 + ], + [ + 0.0, + 0.0 + ], + [ + 0.0, + 150.0 + ] + ], + "interiors": [ + [ + [ + 84.11682477736936, + 111.65966831794691 + ], + [ + 83.84592751703507, + 109.65998392672893 + ], + [ + 84.25331787621653, + 107.683584147994 + ], + [ + 85.29291408692532, + 105.95402850887238 + ], + [ + 94.15543892662492, + 95.64679512131026 + ], + [ + 96.01020468293075, + 94.19776945985937 + ], + [ + 98.279644584269, + 93.57369100924456 + ], + [ + 100.61452767573361, + 93.87059558566699 + ], + [ + 102.65555232913637, + 95.04279426443698 + ], + [ + 104.0886370883989, + 96.90990418382506 + ], + [ + 104.69325284471185, + 99.18460661832634 + ], + [ + 105.49324706604203, + 111.99399406602726 + ], + [ + 105.31532829715924, + 113.86434673427792 + ], + [ + 104.56768646684223, + 115.58797833224581 + ], + [ + 103.32362939735935, + 116.99588322953922 + ], + [ + 101.70513944823516, + 117.95001344944481 + ], + [ + 99.87091289450713, + 118.35681455754569 + ], + [ + 98.00079942990303, + 118.17639886682124 + ], + [ + 88.33828036887326, + 115.67424480668245 + ], + [ + 86.49765441293553, + 114.84706944409857 + ], + [ + 85.03536724161461, + 113.45644396657856 + ], + [ + 84.11682477736936, + 111.65966831794691 + ] + ], + [ + [ + 32.5, + 34.33 + ], + [ + 31.294, + 34.83 + ], + [ + 30.0, + 35.0 + ], + [ + 28.706, + 34.83 + ], + [ + 27.5, + 34.33 + ], + [ + 26.464, + 33.536 + ], + [ + 25.67, + 32.5 + ], + [ + 25.17, + 31.294 + ], + [ + 25.0, + 30.0 + ], + [ + 25.17, + 28.706 + ], + [ + 25.67, + 27.5 + ], + [ + 26.464, + 26.464 + ], + [ + 27.5, + 25.67 + ], + [ + 28.706, + 25.17 + ], + [ + 30.0, + 25.0 + ], + [ + 31.294, + 25.17 + ], + [ + 32.5, + 25.67 + ], + [ + 33.536, + 26.464 + ], + [ + 34.33, + 27.5 + ], + [ + 34.83, + 28.706 + ], + [ + 35.0, + 30.0 + ], + [ + 34.83, + 31.294 + ], + [ + 34.33, + 32.5 + ], + [ + 33.536, + 33.536 + ], + [ + 32.5, + 34.33 + ] + ], + [ + [ + 173.536, + 33.536 + ], + [ + 172.5, + 34.33 + ], + [ + 171.294, + 34.83 + ], + [ + 170.0, + 35.0 + ], + [ + 168.706, + 34.83 + ], + [ + 167.5, + 34.33 + ], + [ + 166.464, + 33.536 + ], + [ + 165.67, + 32.5 + ], + [ + 165.17, + 31.294 + ], + [ + 165.0, + 30.0 + ], + [ + 165.17, + 28.706 + ], + [ + 165.67, + 27.5 + ], + [ + 166.464, + 26.464 + ], + [ + 167.5, + 25.67 + ], + [ + 168.706, + 25.17 + ], + [ + 170.0, + 25.0 + ], + [ + 171.294, + 25.17 + ], + [ + 172.5, + 25.67 + ], + [ + 173.536, + 26.464 + ], + [ + 174.33, + 27.5 + ], + [ + 174.83, + 28.706 + ], + [ + 175.0, + 30.0 + ], + [ + 174.83, + 31.294 + ], + [ + 174.33, + 32.5 + ], + [ + 173.536, + 33.536 + ] + ], + [ + [ + 174.33, + 122.5 + ], + [ + 173.536, + 123.536 + ], + [ + 172.5, + 124.33 + ], + [ + 171.294, + 124.83 + ], + [ + 170.0, + 125.0 + ], + [ + 168.706, + 124.83 + ], + [ + 167.5, + 124.33 + ], + [ + 166.464, + 123.536 + ], + [ + 165.67, + 122.5 + ], + [ + 165.17, + 121.294 + ], + [ + 165.0, + 120.0 + ], + [ + 165.17, + 118.706 + ], + [ + 165.67, + 117.5 + ], + [ + 166.464, + 116.464 + ], + [ + 167.5, + 115.67 + ], + [ + 168.706, + 115.17 + ], + [ + 170.0, + 115.0 + ], + [ + 171.294, + 115.17 + ], + [ + 172.5, + 115.67 + ], + [ + 173.536, + 116.464 + ], + [ + 174.33, + 117.5 + ], + [ + 174.83, + 118.706 + ], + [ + 175.0, + 120.0 + ], + [ + 174.83, + 121.294 + ], + [ + 174.33, + 122.5 + ] + ], + [ + [ + 34.83, + 121.294 + ], + [ + 34.33, + 122.5 + ], + [ + 33.536, + 123.536 + ], + [ + 32.5, + 124.33 + ], + [ + 31.294, + 124.83 + ], + [ + 30.0, + 125.0 + ], + [ + 28.706, + 124.83 + ], + [ + 27.5, + 124.33 + ], + [ + 26.464, + 123.536 + ], + [ + 25.67, + 122.5 + ], + [ + 25.17, + 121.294 + ], + [ + 25.0, + 120.0 + ], + [ + 25.17, + 118.706 + ], + [ + 25.67, + 117.5 + ], + [ + 26.464, + 116.464 + ], + [ + 27.5, + 115.67 + ], + [ + 28.706, + 115.17 + ], + [ + 30.0, + 115.0 + ], + [ + 31.294, + 115.17 + ], + [ + 32.5, + 115.67 + ], + [ + 33.536, + 116.464 + ], + [ + 34.33, + 117.5 + ], + [ + 34.83, + 118.706 + ], + [ + 35.0, + 120.0 + ], + [ + 34.83, + 121.294 + ] + ] + ] + } + ], + "parameters_used": { + "eta_0": 0.1, + "alpha": 1.0, + "R_0": 30.0, + "kappa": 1.0, + "p": 2.0, + "beta": 0.3, + "R_edge": 15.0, + "s_min": 20.0, + "s_max": 80.0, + "t_min": 2.5, + "t_0": 3.0, + "gamma": 1.0, + "w_frame": 8.0, + "r_f": 6.0, + "d_keep": 1.5, + "min_pocket_radius": 6.0, + "min_triangle_area": 20.0, + "thickness": 8.0 + }, + "checks": { + "is_valid_geometry": true, + "min_web_width": true, + "no_islands": true, + "no_self_intersections": true, + "mass_estimate_g": 632.8649797312323, + "area_mm2": 29299.30461718668, + "num_interiors": 5 + }, + "pipeline": { + "geometry_file": "tests\\test_geometries\\small_plate_200x150.json", + "num_vertices": 144, + "num_triangles": 204, + "num_pockets": 1, + "validation_ok": true + } +} \ No newline at end of file diff --git a/tools/adaptive-isogrid/tests/lloyd_trial_output/rib_profile.json b/tools/adaptive-isogrid/tests/lloyd_trial_output/rib_profile.json new file mode 100644 index 00000000..7f54078b --- /dev/null +++ b/tools/adaptive-isogrid/tests/lloyd_trial_output/rib_profile.json @@ -0,0 +1,529 @@ +{ + "valid": true, + "outer_boundary": [ + [ + 0, + 0 + ], + [ + 200, + 0 + ], + [ + 200, + 150 + ], + [ + 0, + 150 + ] + ], + "pockets": [], + "holes": [ + { + "center": [ + 30, + 30 + ], + "radius": 5.0, + "is_circular": true + }, + { + "center": [ + 170, + 30 + ], + "radius": 5.0, + "is_circular": true + }, + { + "center": [ + 170, + 120 + ], + "radius": 5.0, + "is_circular": true + }, + { + "center": [ + 30, + 120 + ], + "radius": 5.0, + "is_circular": true + } + ], + "rib_web": [ + { + "exterior": [ + [ + 0.0, + 0.0 + ], + [ + 0.0, + 150.0 + ], + [ + 200.0, + 150.0 + ], + [ + 200.0, + 0.0 + ], + [ + 0.0, + 0.0 + ] + ], + "interiors": [ + [ + [ + 32.5, + 34.33 + ], + [ + 31.294, + 34.83 + ], + [ + 30.0, + 35.0 + ], + [ + 28.706, + 34.83 + ], + [ + 27.5, + 34.33 + ], + [ + 26.464, + 33.536 + ], + [ + 25.67, + 32.5 + ], + [ + 25.17, + 31.294 + ], + [ + 25.0, + 30.0 + ], + [ + 25.17, + 28.706 + ], + [ + 25.67, + 27.5 + ], + [ + 26.464, + 26.464 + ], + [ + 27.5, + 25.67 + ], + [ + 28.706, + 25.17 + ], + [ + 30.0, + 25.0 + ], + [ + 31.294, + 25.17 + ], + [ + 32.5, + 25.67 + ], + [ + 33.536, + 26.464 + ], + [ + 34.33, + 27.5 + ], + [ + 34.83, + 28.706 + ], + [ + 35.0, + 30.0 + ], + [ + 34.83, + 31.294 + ], + [ + 34.33, + 32.5 + ], + [ + 33.536, + 33.536 + ], + [ + 32.5, + 34.33 + ] + ], + [ + [ + 173.536, + 33.536 + ], + [ + 172.5, + 34.33 + ], + [ + 171.294, + 34.83 + ], + [ + 170.0, + 35.0 + ], + [ + 168.706, + 34.83 + ], + [ + 167.5, + 34.33 + ], + [ + 166.464, + 33.536 + ], + [ + 165.67, + 32.5 + ], + [ + 165.17, + 31.294 + ], + [ + 165.0, + 30.0 + ], + [ + 165.17, + 28.706 + ], + [ + 165.67, + 27.5 + ], + [ + 166.464, + 26.464 + ], + [ + 167.5, + 25.67 + ], + [ + 168.706, + 25.17 + ], + [ + 170.0, + 25.0 + ], + [ + 171.294, + 25.17 + ], + [ + 172.5, + 25.67 + ], + [ + 173.536, + 26.464 + ], + [ + 174.33, + 27.5 + ], + [ + 174.83, + 28.706 + ], + [ + 175.0, + 30.0 + ], + [ + 174.83, + 31.294 + ], + [ + 174.33, + 32.5 + ], + [ + 173.536, + 33.536 + ] + ], + [ + [ + 174.33, + 122.5 + ], + [ + 173.536, + 123.536 + ], + [ + 172.5, + 124.33 + ], + [ + 171.294, + 124.83 + ], + [ + 170.0, + 125.0 + ], + [ + 168.706, + 124.83 + ], + [ + 167.5, + 124.33 + ], + [ + 166.464, + 123.536 + ], + [ + 165.67, + 122.5 + ], + [ + 165.17, + 121.294 + ], + [ + 165.0, + 120.0 + ], + [ + 165.17, + 118.706 + ], + [ + 165.67, + 117.5 + ], + [ + 166.464, + 116.464 + ], + [ + 167.5, + 115.67 + ], + [ + 168.706, + 115.17 + ], + [ + 170.0, + 115.0 + ], + [ + 171.294, + 115.17 + ], + [ + 172.5, + 115.67 + ], + [ + 173.536, + 116.464 + ], + [ + 174.33, + 117.5 + ], + [ + 174.83, + 118.706 + ], + [ + 175.0, + 120.0 + ], + [ + 174.83, + 121.294 + ], + [ + 174.33, + 122.5 + ] + ], + [ + [ + 34.83, + 121.294 + ], + [ + 34.33, + 122.5 + ], + [ + 33.536, + 123.536 + ], + [ + 32.5, + 124.33 + ], + [ + 31.294, + 124.83 + ], + [ + 30.0, + 125.0 + ], + [ + 28.706, + 124.83 + ], + [ + 27.5, + 124.33 + ], + [ + 26.464, + 123.536 + ], + [ + 25.67, + 122.5 + ], + [ + 25.17, + 121.294 + ], + [ + 25.0, + 120.0 + ], + [ + 25.17, + 118.706 + ], + [ + 25.67, + 117.5 + ], + [ + 26.464, + 116.464 + ], + [ + 27.5, + 115.67 + ], + [ + 28.706, + 115.17 + ], + [ + 30.0, + 115.0 + ], + [ + 31.294, + 115.17 + ], + [ + 32.5, + 115.67 + ], + [ + 33.536, + 116.464 + ], + [ + 34.33, + 117.5 + ], + [ + 34.83, + 118.706 + ], + [ + 35.0, + 120.0 + ], + [ + 34.83, + 121.294 + ] + ] + ] + } + ], + "parameters_used": { + "eta_0": 0.1, + "alpha": 0.8, + "R_0": 40.0, + "kappa": 1.5, + "p": 2.0, + "beta": 0.3, + "R_edge": 15.0, + "s_min": 12.0, + "s_max": 35.0, + "t_min": 2.5, + "t_0": 3.5, + "gamma": 1.2, + "w_frame": 8.0, + "r_f": 1.5, + "d_keep": 1.5, + "min_pocket_radius": 6.0, + "min_triangle_area": 20.0, + "lloyd_iterations": 5, + "thickness": 8.0 + }, + "checks": { + "is_valid_geometry": true, + "min_web_width": true, + "no_islands": true, + "no_self_intersections": true, + "mass_estimate_g": 641.290915584, + "area_mm2": 29689.394239999994, + "num_interiors": 4 + }, + "pipeline": { + "geometry_file": "tests\\test_geometries\\small_plate_200x150.json", + "num_vertices": 101, + "num_triangles": 139, + "num_pockets": 0, + "validation_ok": true + } +} \ No newline at end of file diff --git a/tools/adaptive-isogrid/tests/lloyd_trial_params.json b/tools/adaptive-isogrid/tests/lloyd_trial_params.json new file mode 100644 index 00000000..061e040b --- /dev/null +++ b/tools/adaptive-isogrid/tests/lloyd_trial_params.json @@ -0,0 +1,18 @@ +{ + "lloyd_iterations": 5, + "s_min": 12.0, + "s_max": 35.0, + "t_min": 2.5, + "t_0": 3.5, + "gamma": 1.2, + "w_frame": 8.0, + "r_f": 1.5, + "d_keep": 1.5, + "eta_0": 0.1, + "alpha": 0.8, + "R_0": 40.0, + "kappa": 1.5, + "p": 2.0, + "beta": 0.3, + "R_edge": 15.0 +}