Files
Aislo/B08_Quantity/B08_Quantity_Engine_StructureFigure_Small.py
T
eomsangdonandClaude Opus 5 951e39a923 feat(b08): 구조물도 그림 — 기슭막이 · 골막이 · 떼흙막이 · 바닥막이 · 개거 까닭
- 기슭막이(돌쌓기형) — 돌쌓기 그림 그대로 · 기울기 판정 대상에 넣고 찰/메는 형태에서(표와 한 벌)
- 골막이 — 정면 사다리꼴 · 방수로 관 · 바닥파기 띠 + 옆 단면 · 치수는 표와 같이 쓰는 erosion_check_section 한 벌로 뽑음(값 불변)
- 떼흙막이 — 정본 평균 붙박이 치수(dims)로 · 그 치수로 표 두 줄(떼 1.39 · 바닥파기 0.17)이 다시 나오는지 시험
- 바닥막이 — 돌 켜 · 버림 · 기초잡석(산출 조건) · 터파기 · 표 사유에 새 한 줄 「터파기는 돌 두께만 — 버림·기초잡석은 판 깊이에 안 들어감」을 그림도 같이 씀
- 표가 안 선 장은 표 사유를 그대로 까닭으로 · 개거 둘은 원문끼리 치수가 달라서/단면 그림이 없어서 까닭
- 화면 확인: 검증 프로젝트 936be972 에 넷을 잠깐 놓았다 되돌림(구조물 5개 원본과 같음 · 단계 상태 그대로 · 내역 124,424,547 그대로)

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

214 lines
8.5 KiB
Python
Raw 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 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