Files

167 lines
6.6 KiB
Python

def _get_solid_objects(model_data):
"""Safely extracts unique 3D concrete solid meshes from model_data['objects']."""
objs = []
seen = set()
raw = model_data.get('objects', {})
if 'culvert' in raw and isinstance(raw['culvert'], dict):
c = raw['culvert']
objs.append(c)
seen.add(c.get('id', 'CULVERT'))
if 'wingwalls' in raw and isinstance(raw['wingwalls'], list):
for w in raw['wingwalls']:
if isinstance(w, dict) and w.get('id') not in seen and 'vertices' in w:
objs.append(w)
seen.add(w['id'])
else:
for k, v in raw.items():
if isinstance(v, dict) and v.get('id') and v['id'] not in seen and 'vertices' in v:
objs.append(v)
seen.add(v['id'])
return objs
def export_obj(model_data, include_rebars=True):
"""
Exports 3D model data (exterior concrete solids + 3D rebars) to Wavefront OBJ format.
"""
lines = ["# Wingwall & Culvert 3D Solid & Rebar Model", "# Generated by Antigravity CAD/BIM Modeler", ""]
v_offset = 1
solid_objects = _get_solid_objects(model_data)
for obj in solid_objects:
lines.append(f"o {obj['id']}")
lines.append(f"g {obj['name']}")
for v in obj['vertices']:
lines.append(f"v {v[0]:.4f} {v[1]:.4f} {v[2]:.4f}")
for f in obj['faces']:
lines.append(f"f {f[0] + v_offset} {f[1] + v_offset} {f[2] + v_offset}")
v_offset += len(obj['vertices'])
lines.append("")
if include_rebars and 'rebars' in model_data:
for ww_id, ww_rebar in model_data['rebars'].get('wingwalls', {}).items():
for grp in ww_rebar.get('groups', []):
lines.append(f"o REBAR_{ww_id}_{grp['mark']}_{grp['dia']}")
for poly in grp.get('polylines', []):
if len(poly) < 2:
continue
for pt in poly:
lines.append(f"v {pt[0]:.4f} {pt[1]:.4f} {pt[2]:.4f}")
idx_str = " ".join(str(v_offset + i) for i in range(len(poly)))
lines.append(f"l {idx_str}")
v_offset += len(poly)
lines.append("")
return "\n".join(lines)
def export_stl(model_data):
"""
Exports 3D model data to ASCII STL format.
"""
lines = ["solid WingwallCulvertModel"]
for obj in _get_solid_objects(model_data):
v = obj['vertices']
for f in obj['faces']:
p1 = v[f[0]]
p2 = v[f[1]]
p3 = v[f[2]]
v1 = [p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2]]
v2 = [p3[0]-p1[0], p3[1]-p1[1], p3[2]-p1[2]]
nx = v1[1]*v2[2] - v1[2]*v2[1]
ny = v1[2]*v2[0] - v1[0]*v2[2]
nz = v1[0]*v2[1] - v1[1]*v2[0]
mag = (nx*nx + ny*ny + nz*nz)**0.5
if mag > 1e-8:
nx, ny, nz = nx/mag, ny/mag, nz/mag
else:
nx, ny, nz = 0.0, 0.0, 1.0
lines.append(f" facet normal {nx:.4e} {ny:.4e} {nz:.4e}")
lines.append(" outer loop")
lines.append(f" vertex {p1[0]:.4f} {p1[1]:.4f} {p1[2]:.4f}")
lines.append(f" vertex {p2[0]:.4f} {p2[1]:.4f} {p2[2]:.4f}")
lines.append(f" vertex {p3[0]:.4f} {p3[1]:.4f} {p3[2]:.4f}")
lines.append(" endloop")
lines.append(" endfacet")
lines.append("endsolid WingwallCulvertModel")
return "\n".join(lines)
def export_dxf_3d(model_data, include_rebars=True):
"""
Exports 3D model data to DXF R2000 ASCII format containing 3DFACE entities for concrete
and 3D LINE entities for internal rebars.
"""
lines = [
"0", "SECTION",
"2", "ENTITIES"
]
for obj in _get_solid_objects(model_data):
layer = obj['id']
v = obj['vertices']
for f in obj['faces']:
p1 = v[f[0]]
p2 = v[f[1]]
p3 = v[f[2]]
lines.extend([
"0", "3DFACE",
"8", layer,
"10", f"{p1[0]:.4f}", "20", f"{p1[1]:.4f}", "30", f"{p1[2]:.4f}",
"11", f"{p2[0]:.4f}", "21", f"{p2[1]:.4f}", "31", f"{p2[2]:.4f}",
"12", f"{p3[0]:.4f}", "22", f"{p3[1]:.4f}", "32", f"{p3[2]:.4f}",
"13", f"{p3[0]:.4f}", "23", f"{p3[1]:.4f}", "33", f"{p3[2]:.4f}"
])
if include_rebars and 'rebars' in model_data:
color_by_dia = {'H19': '1', 'H16': '4', 'H13': '3'}
for ww_id, ww_rebar in model_data['rebars'].get('wingwalls', {}).items():
for grp in ww_rebar.get('groups', []):
layer = f"REBAR_{ww_id}_{grp['mark']}_{grp['dia']}"
c_code = color_by_dia.get(grp['dia'], '2')
for poly in grp.get('polylines', []):
for i in range(len(poly) - 1):
p1 = poly[i]
p2 = poly[i + 1]
lines.extend([
"0", "LINE",
"8", layer,
"62", c_code,
"10", f"{p1[0]:.4f}", "20", f"{p1[1]:.4f}", "30", f"{p1[2]:.4f}",
"11", f"{p2[0]:.4f}", "21", f"{p2[1]:.4f}", "31", f"{p2[2]:.4f}"
])
lines.extend([
"0", "ENDSEC",
"0", "EOF"
])
return "\n".join(lines)
def export_rebar_bom_csv(model_data):
"""
Exports 3단계 철근 재료표 (Bar Bending Schedule) to UTF-8 BOM CSV format.
"""
rows = [
"\ufeff구분(날개벽),기호(Mark),직경(Dia),배치부위,역할,간격,절곡치수,1본길이(m),수량(개),총길이(m),단위중량(kg/m),총중량(TON)"
]
if 'rebars' in model_data:
for ww_id, ww in model_data['rebars'].get('wingwalls', {}).items():
ww_name = ww.get('wingwall_name', ww_id)
for g in ww.get('groups', []):
shape_str = " / ".join(f"{k}={v}" for k, v in g.get('shape_params', {}).items())
rows.append(
f'"{ww_name}",{g["mark"]},{g["dia"]},"{g["zone"]}","{g["role"]}","{g["spacing"]}",'
f'"{shape_str}",{g["length_m"]:.3f},{g["count"]},{g["total_length_m"]:.3f},'
f'{g["unit_weight"]:.3f},{g["weight_ton"]:.4f}'
)
s = ww.get('summary', {})
rows.append(
f'"{ww_name} 소계",-,전체,-,-,-,-,-,{s.get("total_bars",0)},'
f'{s.get("total_length_m",0):.3f},-,{s.get("total_weight_ton",0):.3f} (할증3%: {s.get("total_surcharge_ton",0):.3f})'
)
return "\n".join(rows)