feat: split non-pattern components (pavement, base, drains, 공동구) at pattern-run boundaries (Task 8)

- build_solids.py: PAV/BASE/DRNL/DRNR/HBOX/IBOX are now built per pattern run
  instead of 100 m chunks, so each solid carries its run's pattern and station range.
- Rebuilt GLB (326 geometries), meta and verify (320 solids, volume sum 12406.138 m3).

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A5Cun3RHQKna9qxE6ybZ4A
This commit is contained in:
Claude committed 2026-10-21 22:42:57 +00:00
1 parent b1bb768f09
commit 5e566cdb75
4 files changed
+2988 -298

No files matched your search

+13 -11
View File
@@ -197,19 +197,21 @@ for i, (a, b, pat) in enumerate(runs):
if MODE != 'parts_a' or not (RANGE[0] <= i < RANGE[1]): continue
add(loft(stations_of(a, b), lambda s: EXC_PTS), f"EXC_{i+1:02d}_{pat}", f"굴착 여유대 {pat} {a/1000:.3f}~{b/1000:.3f}",
"굴착면", (150, 120, 90), pat, a, b)
for (a, b) in chunks():
if MODE != 'parts_b': break
for i, (a, b, pat) in enumerate(runs): # 기타 구성요소도 지보패턴 구간(run) 경계에서 분할
if MODE != 'parts_b' or not (RANGE[0] <= i < RANGE[1]): continue
sts = stations_of(a, b)
add(loft(sts, lambda s: np.array(pav_corners(slope_pct(s) / 100.0))), f"PAV_{a}", f"노면 계획고 포장 {a}~{b}",
"노면(계획고)", COL["pav"], None, a, b)
add(loft(sts, lambda s: base_pts(slope_pct(s) / 100.0)), f"BASE_{a}", f"기층·인버트(가정) {a}~{b}",
"기층·인버트(가정)", COL["base"], None, a, b)
rid = f"{i+1:02d}_{pat}"
rng = f"{a/1000:.3f}~{b/1000:.3f}"
add(loft(sts, lambda s: np.array(pav_corners(slope_pct(s) / 100.0))), f"PAV_{rid}", f"노면 계획고 포장 {pat} {rng}",
"노면(계획고)", COL["pav"], pat, a, b)
add(loft(sts, lambda s: base_pts(slope_pct(s) / 100.0)), f"BASE_{rid}", f"기층·인버트(가정) {pat} {rng}",
"기층·인버트(가정)", COL["base"], pat, a, b)
for side, dpts, pipe in (("L", DRAIN_L_PTS, PIPE_L), ("R", DRAIN_R_PTS, PIPE_R)):
bx = loft(sts, lambda s, d=dpts: d)
pp = loft(sts, lambda s, p=pipe: PIPE_PTS(p[0], p[1], p[2]))
add(bx - pp, f"DRN{side}_{a}", f"측구 {side} {a}~{b}", "배수(측구)", COL["drain"], None, a, b)
add(loft(sts, lambda s: HBOX_PTS), f"HBOX_{a}", f"공동구 H(가정) {a}~{b}", "공동구(가정)", COL["hi"], None, a, b)
add(loft(sts, lambda s: IBOX_PTS), f"IBOX_{a}", f"공동구 I(가정) {a}~{b}", "공동구(가정)", COL["hi"], None, a, b)
add(bx - pp, f"DRN{side}_{rid}", f"측구 {side} {pat} {rng}", "배수(측구)", COL["drain"], pat, a, b)
add(loft(sts, lambda s: HBOX_PTS), f"HBOX_{rid}", f"공동구 H(가정) {pat} {rng}", "공동구(가정)", COL["hi"], pat, a, b)
add(loft(sts, lambda s: IBOX_PTS), f"IBOX_{rid}", f"공동구 I(가정) {pat} {rng}", "공동구(가정)", COL["hi"], pat, a, b)
# 시설한계 50 mm reference lines (wires, not solids)
clr = inner_poly.buffer(CLEAR_OFFSET).exterior
@@ -221,7 +223,7 @@ for k, ii in enumerate(idxs):
x0, z0 = clr_pts[ii]
pts3 = [world(s, x0, z0) for s in sts_all]
clear_lines.append(pts3)
if MODE != 'parts_b':
if MODE != 'parts_b' or RANGE[0] != 0:
continue
w = Wire.make_polygon([Vector(*p) for p in pts3], close=False)
w.label = f"REF_CLEAR_{k+1}"; w.color = Color(1.0, 0.44, 0.0)
@@ -230,7 +232,7 @@ for k, ii in enumerate(idxs):
"pattern": None, "from_sta": S0, "to_sta": S1, "volume_m3": 0, "color": list(COL["clear"]), "reference": True})
if MODE in ('parts_a', 'parts_b'):
tag = f'lin_exc_{RANGE[0]:02d}' if MODE == 'parts_a' else 'other'
tag = f'lin_exc_{RANGE[0]:02d}' if MODE == 'parts_a' else f'oth_{RANGE[0]:02d}'
export_step(Compound(children=solids + clear_wires, label=tag), f"{PARTS}/{tag}.step")
json.dump(meta_c, open(f"{PARTS}/{tag}.json", 'w', encoding='utf-8'), ensure_ascii=False, indent=1)
print('parts written', tag, len(solids), 'solids', len(clear_wires), 'ref wires')