feat: strut constraint + construction layers for dome MCP #155
1 changed files with 99 additions and 0 deletions
|
|
@ -200,6 +200,97 @@ def analyze_structure(verts, edges, triangles, radius_meters=5.0):
|
|||
}
|
||||
|
||||
|
||||
def analyze_strut_constraint(struts, max_length=2.0, bar_length=6.0):
|
||||
"""Enforce 2m max strut length for FI12 rebar (6m bars → 3× 2m cuts)."""
|
||||
pieces_per_bar = int(bar_length / max_length) # 3 pieces of 2m
|
||||
over = []
|
||||
ok = []
|
||||
total_bars = 0
|
||||
total_waste_m = 0.0
|
||||
|
||||
for s in struts:
|
||||
L = s["length_m"]
|
||||
count = s["count"]
|
||||
if L > max_length:
|
||||
over.append({"length_m": L, "count": count, "excess_m": round(L - max_length, 2)})
|
||||
else:
|
||||
ok.append(s)
|
||||
|
||||
# Calculate rebar bars needed and waste
|
||||
total_pieces = sum(s["count"] for s in struts)
|
||||
total_bars = math.ceil(total_pieces / pieces_per_bar)
|
||||
total_cut_m = total_bars * bar_length
|
||||
total_strut_m_needed = sum(s["length_m"] * s["count"] for s in struts)
|
||||
total_waste_m = round(total_cut_m - total_strut_m_needed, 2)
|
||||
waste_pieces = total_bars * pieces_per_bar - total_pieces
|
||||
|
||||
return {
|
||||
"max_strut_length_m": max_length,
|
||||
"bar_length_m": bar_length,
|
||||
"pieces_per_bar": pieces_per_bar,
|
||||
"struts_over_limit": over,
|
||||
"struts_ok": len(ok),
|
||||
"total_pieces_needed": total_pieces,
|
||||
"rebar_bars_needed": total_bars,
|
||||
"total_bar_meters": round(total_bars * bar_length, 1),
|
||||
"waste_meters": total_waste_m,
|
||||
"waste_pieces": waste_pieces,
|
||||
"waste_use": "corner hardening gussets" if waste_pieces > 0 else "none",
|
||||
"compliant": len(over) == 0,
|
||||
}
|
||||
|
||||
|
||||
def construction_layers(verts, triangles, radius):
|
||||
"""Group triangles into construction layers ordered top-to-bottom.
|
||||
|
||||
Welding sequence: start at apex (highest Z), weld that layer,
|
||||
lift the completed section, weld the next layer underneath.
|
||||
"""
|
||||
# Compute centroid Z for each triangle
|
||||
tri_zs = []
|
||||
for idx, (v0, v1, v2) in enumerate(triangles):
|
||||
z = (verts[v0][2] + verts[v1][2] + verts[v2][2]) / 3 * radius
|
||||
tri_zs.append((z, idx))
|
||||
|
||||
# Sort top-to-bottom (descending Z)
|
||||
tri_zs.sort(key=lambda x: -x[0])
|
||||
|
||||
# Group into layers by Z proximity (every 0.5m drop = new layer)
|
||||
layers = []
|
||||
current_layer = []
|
||||
current_z = None
|
||||
layer_threshold = 0.5 # meters
|
||||
|
||||
for z, idx in tri_zs:
|
||||
if current_z is None:
|
||||
current_z = z
|
||||
if abs(z - current_z) > layer_threshold:
|
||||
layers.append({
|
||||
"layer": len(layers) + 1,
|
||||
"height_m": round(current_z, 2),
|
||||
"triangles": len(current_layer),
|
||||
"sequence": "weld → lift → next layer" if len(layers) > 0 else "start at apex",
|
||||
})
|
||||
current_layer = []
|
||||
current_z = z
|
||||
current_layer.append(idx)
|
||||
|
||||
if current_layer:
|
||||
layers.append({
|
||||
"layer": len(layers) + 1,
|
||||
"height_m": round(current_z, 2),
|
||||
"triangles": len(current_layer),
|
||||
"sequence": "weld → lift → next layer" if len(layers) > 0 else "start at apex",
|
||||
})
|
||||
|
||||
return {
|
||||
"total_layers": len(layers),
|
||||
"construction_method": "top-to-bottom welding, lift after each layer",
|
||||
"layer_threshold_m": layer_threshold,
|
||||
"layers": layers,
|
||||
}
|
||||
|
||||
|
||||
def render(verts, edges, size=2048, yaw=0.3, pitch=0.4,
|
||||
line_color="#00b4d8", bg_color="#0d1117", lw=1):
|
||||
sy, cy = math.sin(yaw), math.cos(yaw)
|
||||
|
|
@ -259,6 +350,14 @@ def generate(params):
|
|||
analysis["frequency"] = freq
|
||||
analysis["half_sphere"] = half
|
||||
|
||||
# ── Strut constraint: 2m max (FI12 rebar, 6m bars → 3× 2m cuts) ──
|
||||
strut_constraint = analyze_strut_constraint(analysis["struts"], max_length=2.0, bar_length=6.0)
|
||||
analysis["strut_constraint"] = strut_constraint
|
||||
|
||||
# ── Construction layers: order triangles top-to-bottom for welding ──
|
||||
layers = construction_layers(verts, triangles, radius)
|
||||
analysis["construction_layers"] = layers
|
||||
|
||||
result = {"success": True, "analysis": analysis}
|
||||
|
||||
if not analysis_only:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue