diff --git a/B07_DesignDetail/B07_DesignDetail_Cross_Overlay.ts b/B07_DesignDetail/B07_DesignDetail_Cross_Overlay.ts index 6d3b1488a..81a1400e1 100644 --- a/B07_DesignDetail/B07_DesignDetail_Cross_Overlay.ts +++ b/B07_DesignDetail/B07_DesignDetail_Cross_Overlay.ts @@ -29,6 +29,8 @@ const SVG_NS = "http://www.w3.org/2000/svg"; /** 구조물 엔티티가 들어갈 레이어·색 — 서버 도면이 이미 선언해 둔 그 레이어다. */ const STRUCTURE_LAYER_ID = "b08-structure"; const STRUCTURE_COLOR = "#f6d55c"; +/** 배수관 · 박스암거 색 — 구조물(노랑) · 설계선(보라) · 지반선(흰/검정)과 또렷이 다른 청록(268-2). */ +const PIPE_COLOR = "#22d3ee"; /** 설계선 도면층·색 — 서버 도면(`B07_DesignDetail_Engine_Cad`)이 쓰는 그 값이다. */ export const DESIGN_LAYER_ID = "b08-design"; const DESIGN_COLOR = "#b794f6"; @@ -49,6 +51,7 @@ interface Style { } const STRUCTURE_STYLE: Style = { layerId: STRUCTURE_LAYER_ID, color: STRUCTURE_COLOR }; +const PIPE_STYLE: Style = { layerId: STRUCTURE_LAYER_ID, color: PIPE_COLOR }; const DESIGN_STYLE: Style = { layerId: DESIGN_LAYER_ID, color: DESIGN_COLOR }; function baseEntity(style: Style, type: string, shapeData: unknown): Entity { @@ -494,9 +497,10 @@ function drawDesign( ); } -/** 구조물을 그린다. 그리는 순서는 B06 카드와 같다. */ +/** 구조물을 그린다. 그리는 순서는 B06 카드와 같다 — 박스암거 · 배수관은 pipeSvg 로 따로(268-2). */ function drawStructures( svg: SVGElement, + pipeSvg: SVGElement, section: CrossSection, layouts: Layouts, x: (offset: number) => number, @@ -504,12 +508,12 @@ function drawStructures( ): void { const { link, culvert, box, ford, own } = layouts; if (own) appendRevetmentOverlay(svg, own, x, y); - if (box) appendBoxOverlay(svg, box, x, y); + if (box) appendBoxOverlay(pipeSvg, box, x, y); if (ford) appendFordOverlay(svg, ford, x, y); if (culvert) { const linked = !section.culvert && !!link; appendCulvertOverlay( - svg, + pipeSvg, culvert, x, y, @@ -536,9 +540,11 @@ export function overlayEntities( const designSvg = document.createElementNS(SVG_NS, "svg"); drawDesign(designSvg, section, layouts, x, y); const structureSvg = document.createElementNS(SVG_NS, "svg"); - drawStructures(structureSvg, section, layouts, x, y); + const pipeSvg = document.createElementNS(SVG_NS, "svg"); + drawStructures(structureSvg, pipeSvg, section, layouts, x, y); return [ ...harvest(designSvg, DESIGN_STYLE, frame), ...harvest(structureSvg, STRUCTURE_STYLE, frame), + ...harvest(pipeSvg, PIPE_STYLE, frame), ]; } diff --git a/B07_DesignDetail/B07_DesignDetail_Engine_Cad.py b/B07_DesignDetail/B07_DesignDetail_Engine_Cad.py index 89ed9443c..ab66e7bfc 100644 --- a/B07_DesignDetail/B07_DesignDetail_Engine_Cad.py +++ b/B07_DesignDetail/B07_DesignDetail_Engine_Cad.py @@ -628,6 +628,12 @@ def build_cross_drawing( # 블록 테두리(종이 mm). 프론트가 자기 그림을 이 안으로 자르고, 갈아 끼울 # 서버 설계선을 이 안에서만 골라내는 데 쓴다. "frame": [frame_left, frame_bottom, frame_right, frame_top], + # 화면이 다시 그려 갈아 끼울 서버 선 — 설계선 + 측구 구간 구조물 선(268-1) + "design_ids": [ + e["id"] + for e in entities + if e["layerId"] in (DESIGN_LAYER_ID, STRUCTURE_LAYER_ID) + ], } ], "layers": [ diff --git a/B07_DesignDetail/B07_DesignDetail_Engine_Cad_Sheet.py b/B07_DesignDetail/B07_DesignDetail_Engine_Cad_Sheet.py index aecd97a15..1fa353f86 100644 --- a/B07_DesignDetail/B07_DesignDetail_Engine_Cad_Sheet.py +++ b/B07_DesignDetail/B07_DesignDetail_Engine_Cad_Sheet.py @@ -240,7 +240,9 @@ def build_cross_sheet(sheet: dict[str, Any], sections: list[dict[str, Any]]) -> layer_id = f"{STATION_LAYER_PREFIX}{section['chainage']}" title = section.get("title") or f"{section['chainage']}" station_layers.append(_layer(layer_id, f"횡단 {title}")) - design_ids = [e["id"] for e in placed["entities"] if e["layerId"] == DESIGN_LAYER_ID] + # 측구 구간 구조물 선(b08-structure)도 갈아 끼울 대상 — 화면 구조물 덧그림이 같은 자리를 그림(268-1) + replaceable = (DESIGN_LAYER_ID, STRUCTURE_LAYER_ID) + design_ids = [e["id"] for e in placed["entities"] if e["layerId"] in replaceable] for entity in placed["entities"]: _move_to_layer(entity, layer_id) for placement in placed.get("cross_placements") or []: diff --git a/resources/tester/test_268_b07_cross_ditch_pipe_color.py b/resources/tester/test_268_b07_cross_ditch_pipe_color.py new file mode 100644 index 000000000..d55a4a506 --- /dev/null +++ b/resources/tester/test_268_b07_cross_ditch_pipe_color.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- +"""268 B07 횡단도 — 노란 측구 덧그림 · 배수관 · 박스암거 색. + +못박는 것 + - 268-1 측구 구간 구조물 선(b08-structure)도 design_ids 에 실려 화면 덧그림이 갈아 끼울 때 걷힘 + (장 · 단일 도면 모두) · 설계선 · 측구 본 그림(구조물 선 자체)은 그대로 남음. + - 268-2 박스암거 · 배수관 덧그림은 구조물(노랑) · 설계선(보라) · 지반선과 다른 색 한 벌. +""" + +import re +from pathlib import Path + +from B07_DesignDetail.B07_DesignDetail_Engine_Cad import ( + DESIGN_COLOR, + DESIGN_LAYER_ID, + GROUND_COLOR, + ROCK_COLOR, + STRUCTURE_COLOR, + STRUCTURE_LAYER_ID, + build_cross_drawing, +) +from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Sheet import build_cross_sheet, plan_cross_sheets +from B07_DesignDetail.B07_DesignDetail_Engine_Template import use_project_templates + +ROOT = Path(__file__).resolve().parents[2] + +_DESIGN = { + "ditch_enabled": True, + "ditch_side": "left", + "ditch": {"type": "standard", "top_width_m": 1.0}, + "road_edges": {"left": {"offset_m": -1.0}}, +} +_LINE = [ + {"offset_m": x, "elevation_m": 100.0 + (0.0 if x >= -1.0 else -0.3)} + for x in (-3.0, -2.0, -1.0, 0.0, 3.0) +] + + +def _source(chainage: float) -> dict: + return { + "chainage_m": chainage, + "samples": [ + {"valid": True, "offset_m": float(x), "elevation_m": 100.0 + 0.1 * x} + for x in range(-5, 6) + ], + } + + +def _ditch_ids(entities: list[dict]) -> list[str]: + return [e["id"] for e in entities if e["lineColor"] == STRUCTURE_COLOR] + + +def test_측구_구조물_선_design_ids_장() -> None: + use_project_templates(None) + sheet = plan_cross_sheets([0.0, 20.0], {"default": [2, 1]})[0] + sections = [ + { + "chainage": c, + "source": _source(c), + "design": _DESIGN, + "design_line": _LINE, + "quantity_table": {"ground": 1.0, "planned": 0.5}, + "title": f"No.{int(c // 20)}", + } + for c in (0.0, 20.0) + ] + drawing = build_cross_sheet(sheet, sections) + by_id = {e["id"]: e for e in drawing["entities"]} + ditch = _ditch_ids(drawing["entities"]) + assert len(ditch) == 2 # 측점마다 측구 구간 선 하나 + for placement in drawing["cross_placements"]: + ids = placement["design_ids"] + assert len(set(ids) & set(ditch)) == 1 # 자기 측점 측구 선만 + assert all(by_id[i]["lineColor"] in (DESIGN_COLOR, STRUCTURE_COLOR) for i in ids) + # 걷기는 화면이 하고, 서버 도면은 측구 구조물 선을 그대로 가짐 + assert all(by_id[i]["lineColor"] == STRUCTURE_COLOR for i in ditch) + + +def test_측구_구조물_선_design_ids_단일_도면() -> None: + use_project_templates(None) + drawing = build_cross_drawing( + _source(0.0), "d", _LINE, _DESIGN, {"ground": 1.0, "planned": 0.5}, "No.0" + ) + ditch = [e for e in drawing["entities"] if e["layerId"] == STRUCTURE_LAYER_ID] + assert len(ditch) == 1 + design_ids = drawing["cross_placements"][0]["design_ids"] + assert ditch[0]["id"] in design_ids + assert all( + e["layerId"] in (DESIGN_LAYER_ID, STRUCTURE_LAYER_ID) + for e in drawing["entities"] + if e["id"] in design_ids + ) + + +def test_측구_없으면_design_ids_설계선만() -> None: + use_project_templates(None) + drawing = build_cross_drawing( + _source(0.0), "d", _LINE, None, {"ground": 1.0, "planned": 0.5}, "No.0" + ) + ids = drawing["cross_placements"][0]["design_ids"] + layers = {e["layerId"] for e in drawing["entities"] if e["id"] in ids} + assert layers == {DESIGN_LAYER_ID} + + +def test_배관_박스암거_색_따로() -> None: + src = (ROOT / "B07_DesignDetail/B07_DesignDetail_Cross_Overlay.ts").read_text(encoding="utf-8") + pipe = re.search(r'const PIPE_COLOR = "(#[0-9a-fA-F]{6})"', src) + assert pipe, "PIPE_COLOR 없음" + color = pipe.group(1).lower() + assert color not in {STRUCTURE_COLOR, DESIGN_COLOR, GROUND_COLOR, ROCK_COLOR} + assert color not in { + "#8ea0b5", + "#e8edf4", + "#ffe066", + "#ff4d4d", + } # 표 선 · 글 · 값 · 구조물 평면 색 + # 박스암거 · 배수관만 pipeSvg 로 — 구조물(돌붙임 · 여울)은 그대로 노랑 + assert "appendBoxOverlay(pipeSvg," in src and "appendCulvertOverlay(\n pipeSvg," in src + assert "appendRevetmentOverlay(svg," in src and "appendFordOverlay(svg," in src + assert "harvest(pipeSvg, PIPE_STYLE, frame)" in src