Files
Aislo/B08_Quantity/B08_Quantity_Engine_StructureFigure_Small.py
eomsangdonandClaude Opus 5 7b6fe3873b feat(b08): 구조물도 그림 2조각 — 집수정(□형 Ø800) · 물넘이포장 · 날개벽·돌집수정은 까닭
- 집수정 □형 Ø800 — 관측 줄에 원문 치수(소광리 「집수정구조도(800)」 설계조건 칸·산출식)를 싣고 그 치수로 표 수량이 원문 식대로 다시 나오는지 시험
  원문 값으로: 콘크리트 2.84 → 2.83752 · 합판거푸집 21.28 → 21.2843 · 철근 D13 4.78 → 4.776 · 면목 12.67 → 12.6728(터파기·되메우기·잔토 그대로)
  그림: 긴 벽 정면(바닥기초·관·방수로·터파기) + 평면 · 관·방수로 자리는 원문 입체도 모식이라 적음
- 물넘이포장 — 슬래브 T(규격) · 와이어메쉬 2/3T(소광리 「포장」 그림) · 터파기 · 새 사유 「원문 둘이 다름: 소광리 포장 와이어매쉬 1.0·비닐 2㎡ ↔ 이 표(울진 1공구) 1.16·비닐 없음」을 표·그림이 같이 씀
- 날개벽 5종 — 원문 탭에 구조도 그림이 없고 날개 모양이 손으로 적은 수뿐이라 까닭 · 돌집수정 ㄷ·ㄴ형 — 집계표 관측값이라 치수 원문 없음 까닭
- 사유 문구(브레인 판정): 개거 150×200 「원문 둘이 다름(산출식 0.3×0.1 ↔ 그림 홈 150×200 + 밑 보 400×200)」 · 바닥막이 「옹벽은 버림·잡석까지 팜 — 바닥막이는 정본이 돌 두께만이라 다름」

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
2026-09-14 07:09:53 +09:00

322 lines
13 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""구조물도 상단 그림 — **골막이 · 떼흙막이 · 바닥막이** (정본 개소당·㎡당 구조물, PLAN 12장).
⚠ 치수는 **표가 쓴 그 한 벌**에서 읽음 — 골막이 `erosion_check_section` ·
떼흙막이 `SOIL_GUARD_SOD.dims` · 바닥막이 `BED_SILL_FORMS` + 산출 조건 기초잡석 두께.
⚠ 사유 글자도 표 사유와 같은 상수(`BED_SILL_TRENCH_NOTE` 등).
⚠ 좌표는 m · 위가 +y · 모양 목록 규약은 `B08_Quantity_Engine_StructureFigure` 와 같음.
"""
from __future__ import annotations
import math
from typing import Any
from B08_Quantity.B08_Quantity_Engine_StructureFigure import (
_LABEL_SIZE,
SCALE_MM_PER_M,
_path,
_text,
)
def _title(sheet: dict[str, Any], y: float) -> dict[str, Any]:
return _text(
f"{sheet.get('title') or ''} (축척 1/{int(1000 / SCALE_MM_PER_M)})",
(0.0, y),
"left",
_LABEL_SIZE,
)
def _front_with_trench(
top: float, bottom: float, height: float, depth: float, x0: float = 0.0
) -> list[dict[str, Any]]:
"""정면 사다리꼴(윗변 `top` · 밑변 `bottom` 가운데 정렬) + 사면 둘·밑변을 따라 판 띠(점선)."""
inset = (top - bottom) / 2
corners = [(x0, height), (x0 + inset, 0.0), (x0 + inset + bottom, 0.0), (x0 + top, height)]
shapes = [_path(corners + [corners[0]], "wall")]
if depth > 0:
# 사면 둘·밑변을 바깥으로 `depth` 만큼 민 선 — 이웃 변의 교점으로 잇음.
normals = []
for (ax, ay), (bx, by) in zip(corners[:3], corners[1:4]):
length = math.hypot(bx - ax, by - ay)
normals.append(((by - ay) / length, -(bx - ax) / length))
offset = []
for index, (px, py) in enumerate(corners):
used = [normals[i] for i in (index - 1, index) if 0 <= i < 3]
nx = sum(n[0] for n in used)
ny = sum(n[1] for n in used)
dot = used[0][0] * nx + used[0][1] * ny
offset.append((px + nx * depth / dot, py + ny * depth / dot))
shapes.append(_path(offset, "guide", dash=True))
return shapes
def _circle(cx: float, cy: float, r: float) -> list[tuple[float, float]]:
return [
(cx + r * math.cos(math.tau * i / 24), cy + r * math.sin(math.tau * i / 24))
for i in range(25)
]
def _rect(x0: float, y0: float, x1: float, y1: float) -> list[tuple[float, float]]:
return [(x0, y0), (x0, y1), (x1, y1), (x1, y0), (x0, y0)]
def inlet_basin_figure(sheet: dict[str, Any], entry: dict[str, Any]) -> list[dict[str, Any]]:
"""집수정(□형) — 긴 벽 정면(바닥기초 · 관 · 방수로 · 터파기) + 평면(바깥·안 벽).
치수는 관측 줄 `section`(표 수량이 나온 그 치수). 관·방수로 **자리**는 원문 입체도 모식.
"""
section = entry["section"]
inner, wall, base = section["inner"], section["wall_m"], section["base_m"]
spill, dig = section["spillway"], section["dig"]
length, width = inner["length_m"] + 2 * wall, inner["width_m"] + 2 * wall
height = inner["height_m"]
top = base + height
radius = section["pipe_d_m"] / 2
mid = length / 2
notch_half_top, notch_half_bottom = spill["top_m"] / 2, spill["bottom_m"] / 2
# 긴 벽 정면 — 윗변 가운데에 방수로 한 곳(모두 `count` 곳).
outline = [
(0.0, 0.0),
(0.0, top),
(mid - notch_half_top, top),
(mid - notch_half_bottom, top - spill["depth_m"]),
(mid + notch_half_bottom, top - spill["depth_m"]),
(mid + notch_half_top, top),
(length, top),
(length, 0.0),
(0.0, 0.0),
]
dig_len = length + dig["extra_length_m"]
shapes = [
_path(outline, "wall"),
_path([(0.0, base), (length, base)], "guide"),
_path(_circle(mid, base + radius, radius), "guide"),
_path(
_rect(-dig["extra_length_m"] / 2, 0.0, length + dig["extra_length_m"] / 2, top),
"guide",
dash=True,
),
]
# 평면 — 바깥 벽 · 안 벽 · 터파기 평면(점선).
px = length + 1.2
shapes += [
_path(_rect(px, 0.0, px + length, width), "wall"),
_path(_rect(px + wall, wall, px + length - wall, width - wall), "guide"),
_path(
_rect(
px - dig["extra_length_m"] / 2,
(width - dig["width_m"]) / 2,
px + length + dig["extra_length_m"] / 2,
(width + dig["width_m"]) / 2,
),
"guide",
dash=True,
),
]
right = px + length + 0.5
shapes += [
_title(sheet, max(top, dig["width_m"]) + 0.55),
_text("정면(긴 벽)", (mid, top + 0.15), "center"),
_text("평면", (px + length / 2, width + 0.15 + (dig["width_m"] - width) / 2), "center"),
_text(
f"안 {inner['length_m']:g}×{inner['width_m']:g}×{height:g} · 벽 {wall:g} · 바닥기초"
f" {length:g}×{width:g}×{base:g} m",
(right, top * 0.9),
"left",
),
_text(f"관 Ø{section['pipe_d_m']:g}", (right, top * 0.65), "left"),
_text(
f"방수로 {spill['count']}곳 (상장 {spill['top_m']:g} · 하장 {spill['bottom_m']:g}"
f" · 고 {spill['depth_m']:g})",
(right, top * 0.4),
"left",
),
_text(
f"터파기 {dig_len:g} × {dig['width_m']:g} × 깊이 {top:g} m",
(right, top * 0.15),
"left",
),
_text("관·방수로 자리는 원문 입체도 모식 — 치수는 산출식", (0.0, -0.3), "left"),
]
return shapes
def pavement_figure(sheet: dict[str, Any], entry: dict[str, Any]) -> list[dict[str, Any]]:
"""물넘이포장 — ㎡당 한 칸 단면: 슬래브 두께(규격) · 와이어메쉬(2/3T) · 터파기(두께만큼)."""
thickness = float(entry["spec"]["thickness_cm"]) / 100
mesh = thickness * entry["section"]["mesh_from_bottom_ratio"]
width = 1.0
shapes = [
_path(_rect(0.0, 0.0, width, thickness), "wall"),
_path([(0.05, mesh), (width - 0.05, mesh)], "guide", dash=True),
_path(_rect(-0.12, 0.0, width + 0.12, thickness), "guide", dash=True),
]
right = width + 0.4
shapes += [
_title(sheet, thickness + 0.45),
_text("폭 1 m 당(표는 ㎡당)", (width / 2, thickness + 0.12), "center"),
_text(f"콘크리트 T={thickness:g} m", (right, thickness * 0.8), "left"),
_text(f"와이어메쉬 — 밑에서 {mesh:.3f} m (2/3T)", (right, mesh - 0.02), "left"),
_text(f"터파기 깊이 {thickness:g} m (= 두께)", (right, 0.0), "left"),
]
for index, note in enumerate(entry.get("notes") or []):
shapes.append(_text(str(note), (0.0, -0.25 - index * 0.14), "left"))
return shapes
def erosion_check_figure(sheet: dict[str, Any]) -> list[dict[str, Any]]:
"""골막이 — 정면(사다리꼴 · 방수로 관 · 바닥파기 띠) + 옆 단면(상부·하부 두께)."""
from B08_Quantity.B08_Quantity_Engine_UnitQuantity_Revetment import erosion_check_section
geo = erosion_check_section(float(sheet.get("height_m") or 0.0), sheet.get("options") or {})
top, bottom, height = geo["top_m"], geo["bottom_m"], geo["height_m"]
shapes = _front_with_trench(top, bottom, height, geo["trench_depth"])
if geo["spillway"]:
shapes.append(_path(_circle(top / 2, geo["pipe_r"], geo["pipe_r"]), "guide"))
# 옆 단면 — 뒷면 수직 · 앞면이 하부 두께에서 상부 두께로 좁아짐(표의 두께식 그대로).
x0 = top + 1.2
shapes.append(
_path(
[
(x0, 0.0),
(x0, height),
(x0 + geo["top_t"], height),
(x0 + geo["bottom_t"], 0.0),
(x0, 0.0),
],
"wall",
)
)
right = x0 + geo["bottom_t"] + 0.3
shapes += [
_title(sheet, height + 0.55),
_text(f"상장 ⓐ {top:g} m", (top / 2, height + 0.14), "center"),
_text(f"하장 ⓑ {bottom:g} m", (top / 2, -geo["trench_depth"] - 0.18), "center"),
_text(f"H = {height:g} m", (-0.3 - geo["trench_depth"], height / 2), "right"),
_text(
f"상부 {geo['top_t']:.2f} · 하부 {geo['bottom_t']:.2f} · 평균 {geo['thickness']:.2f} m"
f" (뒷길이 {geo['back_cm']}㎝ + 0.1H / 0.4H)",
(right, height * 0.85),
"left",
),
_text(f"반수면 1:{geo['slope']:g}", (right, height * 0.62), "left"),
_text(
f"바닥파기 폭 {geo['trench_width']:.2f} × 깊이 {geo['trench_depth']:g} m"
f" (사면장 {geo['slant']:g} × 2 + 하장)",
(right, height * 0.39),
"left",
),
]
if geo["spillway"]:
shapes.append(
_text(
f"방수로 파형강관 Ø{geo['pipe_r'] * 2:g} — 정면적에서 뺌",
(right, height * 0.16),
"left",
)
)
return shapes
def soil_guard_figure(sheet: dict[str, Any]) -> list[dict[str, Any]]:
"""떼흙막이 — 정본 평균 붙박이 치수의 정면(떼 두께 · 바닥파기 띠)."""
from B08_Quantity.B08_Quantity_Engine_UnitQuantity_Revetment import SOIL_GUARD_SOD
d = SOIL_GUARD_SOD["dims"]
top, bottom, height = d["top_m"], d["bottom_m"], d["height_m"]
shapes = _front_with_trench(top, bottom, height, d["trench_depth_m"])
# 머리떼 — 윗변 위 떼 한 켜(두께 = 떼 폭 0.2 로 보임).
shapes.append(
_path(
[(0.0, height), (0.0, height + d["sod_m"]), (top, height + d["sod_m"]), (top, height)],
"guide",
)
)
right = top + 0.4
shapes += [
_title(sheet, height + d["sod_m"] + 0.45),
_text(f"상단 {top:g} m", (top / 2, height + d["sod_m"] + 0.12), "center"),
_text(f"하단 {bottom:g} m", (top / 2, -d["trench_depth_m"] - 0.15), "center"),
_text(f"H = {height:g} m", (-0.3, height / 2), "right"),
_text(
f"떼 20×20㎝ · 폭 {d['sod_m']:g} · 기슭(사면장) {d['slant_m']:g} m",
(right, height * 0.9),
"left",
),
_text(
f"바닥파기 폭 {d['trench_width_m']:g} × 깊이 {d['trench_depth_m']:g} m"
" (기슭 × 2 + 하단)",
(right, height * 0.45),
"left",
),
_text("치수 — 정본 「떼흙막이」 평균 붙박이(개소 제원을 안 봄)", (right, 0.0), "left"),
]
return shapes
def bed_sill_figure(
sheet: dict[str, Any], rubble_thickness_m: float | None
) -> list[dict[str, Any]]:
"""바닥막이(돌붙임) — ㎡당 한 칸의 단면: 돌 켜 · (찰) 버림 · 기초잡석 · 터파기(돌 두께만)."""
from B08_Quantity.B08_Quantity_Engine_UnitQuantity import RUBBLE_BASE_THICKNESS_M
from B08_Quantity.B08_Quantity_Engine_UnitQuantity_Revetment import (
BED_SILL_FORMS,
BED_SILL_TRENCH_NOTE,
)
form = str((sheet.get("options") or {}).get("form") or "").strip()
table = BED_SILL_FORMS[form]
back = table["back_len_m"]
blinding = table["blinding_m3_per_m2"] or 0.0 # ㎥/㎡ = 두께(m)
rubble = RUBBLE_BASE_THICKNESS_M if rubble_thickness_m is None else float(rubble_thickness_m)
rubble = rubble if blinding > 0 else 0.0 # 표도 버림이 선 곳에만 기초잡석을 세움
width = 1.0
shapes = [_path([(0.0, 0.0), (0.0, back), (width, back), (width, 0.0), (0.0, 0.0)], "wall")]
if blinding > 0:
shapes.append(
_path([(0.0, 0.0), (0.0, -blinding), (width, -blinding), (width, 0.0)], "guide")
)
if rubble > 0:
shapes.append(
_path(
[
(0.0, -blinding),
(0.0, -blinding - rubble),
(width, -blinding - rubble),
(width, -blinding),
],
"guide",
dash=True,
)
)
# 터파기 — 표 식 그대로 면적 × 돌 두께(윗면에서 돌 켜 밑까지).
shapes.append(
_path(
[(-0.15, back), (-0.15, 0.0), (width + 0.15, 0.0), (width + 0.15, back)],
"guide",
dash=True,
)
)
right = width + 0.4
shapes += [
_title(sheet, back + 0.4),
_text(
f"{form} — 두께(뒷길이) {back:g} m · {table['stone_spec']}",
(right, back * 0.75),
"left",
),
_text("폭 1 m 당(표는 ㎡당)", (width / 2, back + 0.12), "center"),
_text(f"터파기 깊이 {back:g} m (면적 × 두께)", (right, back * 0.3), "left"),
]
if blinding > 0:
shapes.append(_text(f"버림 T={blinding:g} m", (right, -blinding / 2), "left"))
if rubble > 0:
shapes.append(
_text(f"기초잡석 T={rubble:g} m — 산출 조건", (right, -blinding - rubble / 2), "left")
)
shapes.append(_text(BED_SILL_TRENCH_NOTE, (0.0, -blinding - rubble - 0.25), "left"))
return shapes