diff --git a/B07_DesignDetail/B07_DesignDetail_Engine_Cad_Sheet.py b/B07_DesignDetail/B07_DesignDetail_Engine_Cad_Sheet.py index d1d5e4b0c..460fe954c 100644 --- a/B07_DesignDetail/B07_DesignDetail_Engine_Cad_Sheet.py +++ b/B07_DesignDetail/B07_DesignDetail_Engine_Cad_Sheet.py @@ -3,19 +3,22 @@ 채우는 순서는 **좌하단부터 아래에서 위로, 그다음 오른쪽 열**이다 (2026-08-29 사용자 확정). 칸 = 도각 그림칸(usable_area)을 똑같이 나눈 자리 · 단면은 칸 가운데 · 작성 척도 1/100 그대로(큰 단면이 칸을 넘으면 겹침 — 사용자 배치로 고침). -칸 = 인쇄되는 표 격자선 · 블록 테두리 없음 · 칸보다 큰 단면은 `overflow_cells`(169-2). +칸 = 화면 안내선(`guide_cells` · 인쇄 · 저장 안 됨) · 칸선 · 블록 테두리 없음 · 칸보다 큰 단면은 +`overflow_cells`(169-2). 칸 안 = 표를 칸 아래 가운데에 먼저 · 그 위 글 · 그림(도로 중심 = 칸 가운데)(174-1). 측점마다 도면층 하나(「횡단 No.3」) = 원지반 · 계획선 · 구조물 · 암 그림 · 종류는 색 · 선 꼴 · 표 · 글은 수량표 층(212-2). """ import re +from collections.abc import Callable from typing import Any from uuid import uuid5 from B05_Profile.B05_Profile_Engine_Sections import cross_key from B07_DesignDetail.B07_DesignDetail_Engine_Cad import ( _ENTITY_NS, + _POLY_NS, CROSS_BORDER_PAD_MM, CROSS_TABLE_LAYER_ID, DESIGN_LAYER_ID, @@ -24,14 +27,12 @@ from B07_DesignDetail.B07_DesignDetail_Engine_Cad import ( GROUND_LAYER_ID, ROCK_LAYER_ID, STRUCTURE_LAYER_ID, - TABLE_LINE_COLOR, _clip_polyline, _layer, _section_window, build_cross_drawing, cross_mm, points_from_samples, - polyline_entity, ) from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Table import ( cross_note_block_height, @@ -162,28 +163,41 @@ def plan_cross_sheets( return sheets -def _grid_lines(sheet_id: str, cols: int, rows: int) -> list[dict[str, Any]]: - """장 칸 격자선(169-2) — 세로 열 + 1 · 가로 행 + 1 · 도각 층(인쇄 · DXF 에 나감).""" +def strip_grid_lines(sheet_id: str, entities: list[Any]) -> list[Any]: + """옛 저장본의 인쇄 칸선(169-2 · `:grid-v0` …) 걷음 — 칸은 이제 화면 안내선(`guide_cells`).""" + seeds = (f"{sheet_id}:{FRAME_LAYER_ID}:grid-{axis}{i}" for axis in "vh" for i in range(33)) + ids = {str(uuid5(_POLY_NS, seed)) for seed in seeds} + return [ + entity for entity in entities if not (isinstance(entity, dict) and entity.get("id") in ids) + ] + + +def sheet_cells(sheet: dict[str, Any], count: int | None = None) -> list[list[float]]: + """장 칸 [x0, y0, x1, y1] — 앞 `count` 개(없으면 측점 수) · 칸 가운데 = `slots`.""" + cols, rows = sheet.get("grid") or (4, 4) usable_w, usable_h = usable_area() - left, bottom = -usable_w / 2.0, -usable_h / 2.0 - lines = [ - ((left + usable_w * i / cols, bottom), (left + usable_w * i / cols, -bottom), f"v{i}") - for i in range(cols + 1) - ] + [ - ((left, bottom + usable_h * j / rows), (-left, bottom + usable_h * j / rows), f"h{j}") - for j in range(rows + 1) + cell_w, cell_h = usable_w / cols, usable_h / rows + slots = (sheet.get("slots") or [])[ + : len(sheet.get("chainages") or []) if count is None else count ] return [ - entity - for start, end, name in lines - if ( - entity := polyline_entity( - sheet_id, [start, end], FRAME_LAYER_ID, TABLE_LINE_COLOR, f":grid-{name}" - ) - ) + [x - cell_w / 2.0, y - cell_h / 2.0, x + cell_w / 2.0, y + cell_h / 2.0] for x, y in slots ] +def refresh_saved_sheet( + drawing_id: str, saved: dict[str, Any], plan: Callable[[], list[dict[str, Any]]] +) -> None: + """확정 · 저장본 장 — 옛 인쇄 칸선 걷고 칸 안내선은 지금 배치로(526-1) · 원본 없으면 칸 없음.""" + saved["entities"] = strip_grid_lines(drawing_id, saved.get("entities") or []) + try: + sheets = plan() + except FileNotFoundError: + sheets = [] + sheet = next((item for item in sheets if item["id"] == drawing_id), None) + saved["guide_cells"] = sheet_cells(sheet) if sheet else [] + + def _move_to_layer(entity: dict[str, Any], layer_id: str) -> None: """그림 종류 층 개체(자식 선까지)를 측점 층으로 — id 는 그대로(212-2).""" if entity.get("layerId") in _DRAWING_LAYER_IDS: @@ -198,22 +212,12 @@ def build_cross_sheet(sheet: dict[str, Any], sections: list[dict[str, Any]]) -> placements: list[dict[str, Any]] = [] overflow: list[list[float]] = [] station_layers: list[dict[str, Any]] = [] - slots = sheet.get("slots") or [] - cols, rows = sheet.get("grid") or (4, 4) + cells = sheet_cells(sheet, len(sections)) usable_w, usable_h = usable_area() - cell_w, cell_h = usable_w / cols, usable_h / rows - for index, section in enumerate(sections): - if index >= len(slots): - break + for section, cell in zip(sections, cells): seed_id = f"{sheet['id']}:{section['chainage']}" - center_x, center_y = slots[index] - cell = [ - center_x - cell_w / 2.0, - center_y - cell_h / 2.0, - center_x + cell_w / 2.0, - center_y + cell_h / 2.0, - ] + center_x = (cell[0] + cell[2]) / 2.0 # 표 아래선 = 칸 아래 + 여백 · 도로 중심 = 칸 가운데 · 글 · 그림은 위로(174-1) placed = build_cross_drawing( section["source"], @@ -254,7 +258,6 @@ def build_cross_sheet(sheet: dict[str, Any], sections: list[dict[str, Any]]) -> placements.extend(placed.get("cross_placements") or []) if entities: - entities.extend(_grid_lines(sheet["id"], cols, rows)) scale = drawing_scale("cross") # 가로 · 세로가 같으면 분모 하나 · 다르면 이름표(H · V)(485) scales = ( @@ -262,7 +265,7 @@ def build_cross_sheet(sheet: dict[str, Any], sections: list[dict[str, Any]]) -> if scale["x"] == scale["y"] else (("H", scale["x"]), ("V", scale["y"])) ) - # 도각 = 고정 그림칸 — 내용 크기와 무관하게 칸 격자와 같은 자리(140-3). + # 도각 = 고정 그림칸 — 내용 크기와 무관하게 칸과 같은 자리(140-3). entities.extend( frame_entities( sheet["id"], @@ -278,6 +281,8 @@ def build_cross_sheet(sheet: dict[str, Any], sections: list[dict[str, Any]]) -> "cross_placements": placements, # 칸보다 큰 단면의 칸 [x0, y0, x1, y1] — 라우터가 CAD 가이드 붉은 틀로 보냄(169-2) "overflow_cells": overflow, + # 단면마다 칸 — 라우터가 CAD 가이드 칸 점선으로 보냄(인쇄 칸선 대신 · 526-1) + "guide_cells": cells, "layers": [ *station_layers, _layer(CROSS_TABLE_LAYER_ID, "수량 산출표"), diff --git a/B07_DesignDetail/B07_DesignDetail_Router.py b/B07_DesignDetail/B07_DesignDetail_Router.py index 8f5494ae4..94e96618e 100644 --- a/B07_DesignDetail/B07_DesignDetail_Router.py +++ b/B07_DesignDetail/B07_DesignDetail_Router.py @@ -149,11 +149,14 @@ def _asset_data_url(relative_path: str | None) -> str: def _drawing_guide(drawing: dict[str, Any]) -> dict[str, Any] | None: - """CAD 가이드(169-1 · 169-2) — 작도 영역(도면 좌표) + 넘친 칸. 도각이 없으면 None.""" + """CAD 가이드(169-1 · 169-2 · 526-1) — 작도 영역 + 넘친 칸 + 횡단 칸. 도각 없으면 None.""" entities = drawing.get("entities") or [] # 구조물 도면(376) = M02 얹는 도각 층 area = frame_guide_area(entities) or frame_guide_area(entities, STRUCTURE_FRAME_LAYER) - return {"area": area, "marks": drawing.get("overflow_cells") or []} if area else None + if not area: + return None + marks = drawing.get("overflow_cells") or [] + return {"area": area, "marks": marks, "cells": drawing.get("guide_cells") or []} async def title_block_fields(project_id: UUID) -> dict[str, str]: diff --git a/B07_DesignDetail/B07_DesignDetail_Router_Support.py b/B07_DesignDetail/B07_DesignDetail_Router_Support.py index b77ce4c5c..24e207335 100644 --- a/B07_DesignDetail/B07_DesignDetail_Router_Support.py +++ b/B07_DesignDetail/B07_DesignDetail_Router_Support.py @@ -51,6 +51,7 @@ from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Sheet import ( STATION_LAYER_PREFIX, build_cross_sheet, plan_cross_sheets, + refresh_saved_sheet, ) from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Standard import ( STANDARD_LABEL, @@ -511,6 +512,9 @@ def _read_drawing( for layer in layers: if layer.get("id") == GROUND_LAYER_ID: layer["isLocked"] = False + if CROSS_SHEET_ID.fullmatch(drawing_id): + plan = lambda: _cross_sheet_plan(project_root, longitudinal_path) # noqa: E731 + refresh_saved_sheet(drawing_id, saved, plan) if kind == "cross" and manifest_entry.get("layout") != CROSS_SHEET_LAYOUT: saved["stale_layout"] = True # 화면이 [수정] → 새 배치로 다시 만들기를 안내 elif kind == "cross" and drawing_id.startswith("cross_s"): diff --git a/B07_DesignDetail/openwebcad/src/drawControllers/fit-view.ts b/B07_DesignDetail/openwebcad/src/drawControllers/fit-view.ts index 71dfdf113..d64915461 100644 --- a/B07_DesignDetail/openwebcad/src/drawControllers/fit-view.ts +++ b/B07_DesignDetail/openwebcad/src/drawControllers/fit-view.ts @@ -26,9 +26,22 @@ export const setFitMinHeight = (value: unknown): void => { fitMinHeight = typeof value === 'number' && value > 0 ? value : 0; }; -/** 개체 범위를 화면 가운데에 놓는 배율 · 화면 원점(월드 좌표) */ -export function fitView(entities: Entity[], canvasSize: Point): { zoom: number; offset: Point } { +/** + * 개체 범위를 화면 가운데에 놓는 배율 · 화면 원점(월드 좌표). + * `include` = 범위에 더할 [왼 x, 아래 y, 오른 x, 위 y] — 작도 영역(526-1 · 작은 그림이 칸 크기로 과확대되지 않게) + */ +export function fitView( + entities: Entity[], + canvasSize: Point, + include?: readonly [number, number, number, number] +): { zoom: number; offset: Point } { const boundingBox = { ...getBoundingBoxOfMultipleEntities(entities) }; + if (include) { + boundingBox.minX = Math.min(boundingBox.minX, include[0]); + boundingBox.minY = Math.min(boundingBox.minY, include[1]); + boundingBox.maxX = Math.max(boundingBox.maxX, include[2]); + boundingBox.maxY = Math.max(boundingBox.maxY, include[3]); + } // 글 몇 줄뿐인 도면이 칸 높이로 과확대되지 않게 — 범위를 위 끝에서 아래로 최소 높이까지 늘림 boundingBox.minY = Math.min(boundingBox.minY, boundingBox.maxY - fitMinHeight); const boundingWidth = boundingBox.maxX - boundingBox.minX; diff --git a/B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.ts b/B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.ts index b33539c5b..d2053fbc1 100644 --- a/B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.ts +++ b/B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.ts @@ -7,6 +7,7 @@ import { getGridEnabled, triggerReactUpdate } from '../state.ts'; import { paintColor, themeColor } from '../theme.ts'; import { DEFAULT_TEXT_OPTIONS, type DrawController } from './DrawController'; import { fitEntities, fitView } from './fit-view.ts'; +import { getGuide } from '../integration/guide-area.ts'; /** 토큰을 못 읽을 때만 쓰는 캔버스 배경 폴백. */ const CANVAS_BACKGROUND_FALLBACK = '#111'; @@ -157,7 +158,8 @@ export class ScreenCanvasDrawController implements DrawController { this.viewFitted = true; return; } - const { zoom, offset } = fitView(entities, this.getCanvasSize()); + // 작도 영역(가이드)까지 — 그림이 작아도 도면 한 장 안 자리가 보이게(526-1) + const { zoom, offset } = fitView(entities, this.getCanvasSize(), getGuide()?.area); this.setScreenScale(zoom); this.setScreenOffset(offset); this.viewFitted = true; diff --git a/B07_DesignDetail/openwebcad/src/integration/guide-area.test.ts b/B07_DesignDetail/openwebcad/src/integration/guide-area.test.ts index 9becd673b..c714bdf39 100644 --- a/B07_DesignDetail/openwebcad/src/integration/guide-area.test.ts +++ b/B07_DesignDetail/openwebcad/src/integration/guide-area.test.ts @@ -21,8 +21,21 @@ describe('작도 영역 가이드(169-1 · 169-3)', () => { expect(getGuide()).toBeNull(); setGuideArea(AREA); expect(getGuide()).toBeNull(); - setGuide({ area: AREA, marks: [[0, 0, 1, 1], 'x'], editable: true }); - expect(getGuide()).toEqual({ area: AREA, marks: [[0, 0, 1, 1]], editable: true }); + setGuide({ + area: AREA, + marks: [[0, 0, 1, 1], 'x'], + cells: [ + [0, 0, 2, 2], + [3, 3, 1, 1], + ], + editable: true, + }); + expect(getGuide()).toEqual({ + area: AREA, + marks: [[0, 0, 1, 1]], + cells: [[0, 0, 2, 2]], + editable: true, + }); setGuideArea([0, 0, 100, 50]); expect(getGuide()?.area).toEqual([0, 0, 100, 50]); setGuide(null); @@ -54,12 +67,14 @@ describe('작도 영역 가이드(169-1 · 169-3)', () => { drawLineScreen: lines, fillRectScreen: vi.fn(), } as unknown as ScreenCanvasDrawController; - const guide = { area: AREA, marks: [[0, 0, 5, 5] as GuideArea], editable: false }; + // 횡단 칸(526-1) 둘 — 단면마다 점선 4 변 + const cells = [[0, 0, 5, 5] as GuideArea, [5, 0, 10, 5] as GuideArea]; + const guide = { area: AREA, marks: [[0, 0, 5, 5] as GuideArea], cells, editable: false }; setGuide(guide); expect(isGuideVisible()).toBe(true); // 기본 = 보임 drawGuide(controller); - expect(lines).toHaveBeenCalledTimes(8); // 점선 영역 4 변 + 붉은 틀 4 변 + expect(lines).toHaveBeenCalledTimes(16); // 점선 영역 4 변 + 칸 2 × 4 변 + 붉은 틀 4 변 setGuideVisible(false); lines.mockClear(); @@ -74,7 +89,7 @@ describe('작도 영역 가이드(169-1 · 169-3)', () => { setGuideVisible(true); drawGuide(controller); - expect(lines).toHaveBeenCalledTimes(8); + expect(lines).toHaveBeenCalledTimes(16); setGuideVisible('x'); // 어긋난 값 = 보임 expect(isGuideVisible()).toBe(true); setGuide(null); diff --git a/B07_DesignDetail/openwebcad/src/integration/guide-area.ts b/B07_DesignDetail/openwebcad/src/integration/guide-area.ts index 1a8db4040..12e1c6773 100644 --- a/B07_DesignDetail/openwebcad/src/integration/guide-area.ts +++ b/B07_DesignDetail/openwebcad/src/integration/guide-area.ts @@ -18,6 +18,8 @@ export interface Guide { area: GuideArea; /** 넘친 칸(169-2) — 붉은 사각형으로 짚음 */ marks?: GuideArea[]; + /** 횡단 장 칸(526-1) — 단면마다 점선 사각형(인쇄 칸선 대신) */ + cells?: GuideArea[]; /** 끌어서 고칠 수 있나 — 도각 편집 · M02 양식만 */ editable?: boolean; } @@ -60,6 +62,7 @@ export function setGuide(next: unknown): void { ? { area: [...candidate.area] as GuideArea, marks: (candidate.marks ?? []).filter(isArea), + cells: (candidate.cells ?? []).filter(isArea), editable: candidate.editable === true, } : null; @@ -129,6 +132,8 @@ export function drawGuide(drawController: ScreenCanvasDrawController): void { } } } + drawController.setLineStyles(false, false, GUIDE_COLOR, 1, [4, 4]); + for (const cell of guide.cells ?? []) strokeRect(drawController, cell); drawController.setLineStyles(false, false, MARK_COLOR, 2, []); for (const mark of guide.marks ?? []) strokeRect(drawController, mark); drawController.setOpacity(1); diff --git a/resources/tester/test_169_drawing_area_guide.py b/resources/tester/test_169_drawing_area_guide.py index 1e2e28ffe..8d1ebb89c 100644 --- a/resources/tester/test_169_drawing_area_guide.py +++ b/resources/tester/test_169_drawing_area_guide.py @@ -4,7 +4,7 @@ 못박는 것 - 169-1 그림칸 = 작도 영역 그대로(여백 없음) · 도각 편집이 영역을 싣고 · 저장(어긋나면 옛 값). - 169-1 도면 좌표 가이드 영역 = 도각 bbox ↔ 양식 bbox 로 옮긴 영역 · 도각 아닌 것(블록 틀)이면 None. - - 169-2 횡단 장 = 칸 격자선(열 + 1 · 행 + 1 · 칸 경계) · 장 안 블록 테두리 없음 · 큰 단면 → 넘친 칸. + - 169-2 · 526-1 횡단 장 = 인쇄 칸선 · 블록 테두리 없음 · 칸 = 안내선(단면마다) · 큰 단면 → 넘친 칸. - 169-4 구조물 도각(외곽 좌하단 0,0) 응답에 영역. """ @@ -20,7 +20,11 @@ from B07_DesignDetail.B07_DesignDetail_Engine_Cad import ( FRAME_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_Cad_Sheet import ( + build_cross_sheet, + plan_cross_sheets, + refresh_saved_sheet, +) from B07_DesignDetail.B07_DesignDetail_Router import _drawing_guide from M02_MasterTemplete.M02_MasterTemplete_Router_Drawing import structure_frame @@ -69,37 +73,49 @@ def test_frame_edit_loads_and_saves_area(tmp_path): assert 'request.drawing.get("drawing_area")' in router and "area=area" in router -def test_cross_sheet_grid_lines_no_block_border_and_guide_area(): +def test_cross_sheet_no_grid_lines_no_block_border_and_guide_cells(): engine.use_project_templates(None) w, h = engine.usable_area() sources = [_source(i * 20.0, 0.05, 6) for i in range(3)] sheet, drawing = _sheet(sources, {"default": [3, 2]}) by_id = {e["id"]: e for e in drawing["entities"]} - def line(name: str) -> tuple[dict, dict]: - entity = by_id[str(uuid5(_POLY_NS, f"{sheet['id']}:{FRAME_LAYER_ID}:grid-{name}"))] - assert entity["layerId"] == FRAME_LAYER_ID - shape = entity["children"][0]["shapeData"] - return shape["startPoint"], shape["endPoint"] - - # 격자선 = 세로 열 + 1 · 가로 행 + 1 · 칸 경계 자리 · 그림칸 끝에서 끝까지 - for i in range(3 + 1): - a, b = line(f"v{i}") - assert a["x"] == b["x"] == pytest.approx(-w / 2 + w * i / 3) - assert (a["y"], b["y"]) == pytest.approx((-h / 2, h / 2)) - for j in range(2 + 1): - a, b = line(f"h{j}") - assert a["y"] == b["y"] == pytest.approx(-h / 2 + h * j / 2) - assert (a["x"], b["x"]) == pytest.approx((-w / 2, w / 2)) - assert str(uuid5(_POLY_NS, f"{sheet['id']}:{FRAME_LAYER_ID}:grid-v4")) not in by_id - # 장 안 블록 테두리 없음(측점 한 장 테두리와 같은 id 자리) + # 인쇄 칸선 없음(526-1 · 옛 169-2 `:grid-v0` …) · 장 안 블록 테두리 없음 + for name in ("v0", "v1", "h0", "h1"): + assert str(uuid5(_POLY_NS, f"{sheet['id']}:{FRAME_LAYER_ID}:grid-{name}")) not in by_id for source in sources: seed = f"{sheet['id']}:{source['chainage_m']}:{FRAME_LAYER_ID}" assert str(uuid5(_POLY_NS, seed)) not in by_id - # 가이드 영역(도면 좌표) = 그림칸 그대로 · 넘침 없음 + # 칸 = 화면 안내선 — 단면마다 하나 · 좌하단 → 위 → 오른쪽 열 + cw, ch = w / 3, h / 2 + expected = [ + [-w / 2, -h / 2, -w / 2 + cw, -h / 2 + ch], + [-w / 2, -h / 2 + ch, -w / 2 + cw, h / 2], + [-w / 2 + cw, -h / 2, -w / 2 + 2 * cw, -h / 2 + ch], + ] + assert drawing["guide_cells"] == [pytest.approx(cell) for cell in expected] + # 가이드 영역(도면 좌표) = 그림칸 그대로 · 넘침 없음 · 칸 = 단면 수 guide = _drawing_guide(drawing) assert guide["area"] == pytest.approx([-w / 2, -h / 2, w / 2, h / 2], abs=1e-3) assert guide["marks"] == [] and drawing["overflow_cells"] == [] + assert guide["cells"] == drawing["guide_cells"] + + +def test_saved_sheet_strips_grid_lines_and_gets_cells(): + # 옛 확정 · 저장본(칸선 품음) — 칸선만 걷고 칸 안내선은 지금 배치로(526-1) + engine.use_project_templates(None) + sources = [_source(i * 20.0, 0.05, 6) for i in range(2)] + sheet, drawing = _sheet(sources, {"default": [2, 2]}) + grid = [ + {"id": str(uuid5(_POLY_NS, f"{sheet['id']}:{FRAME_LAYER_ID}:grid-{name}"))} + for name in ("v0", "v2", "h1") + ] + saved = {"entities": drawing["entities"] + grid} + refresh_saved_sheet(sheet["id"], saved, lambda: [sheet]) + assert saved["entities"] == drawing["entities"] + assert saved["guide_cells"] == drawing["guide_cells"] and len(saved["guide_cells"]) == 2 + refresh_saved_sheet("cross_s99999m", saved, lambda: [sheet]) # 지금 배치에 없는 장 = 칸 없음 + assert saved["guide_cells"] == [] def test_big_section_marks_its_cell(): diff --git a/resources/tester/test_180_b07_cross_group.py b/resources/tester/test_180_b07_cross_group.py index d98296baa..ab0bd7798 100644 --- a/resources/tester/test_180_b07_cross_group.py +++ b/resources/tester/test_180_b07_cross_group.py @@ -4,7 +4,7 @@ 못박는 것 - 횡단 장 · 측점 한 장의 원지반 층은 안 잠김(종단은 그대로 잠김). - 측점마다 groupId 하나(원지반 · 계획선 · 표 · 글 전부) · 측점끼리 다름. - - 칸 격자선 · 도각엔 groupId 없음. + - 도각엔 groupId 없음. - cross_placements 의 group_id = 그 측점 엔티티 값(화면 덧그림이 같은 묶음). """ @@ -78,7 +78,7 @@ def test_측점마다_groupId_하나() -> None: types = {e["type"] for e in members} ground = any(e["type"] == "PolyLine" and e["lineColor"] == GROUND_COLOR for e in members) assert ground and "Table" in types and "Text" in types - # 격자선 · 도각은 묶지 않음 + # 도각은 묶지 않음 assert all("groupId" not in e for e in drawing["entities"] if e["layerId"] == FRAME_LAYER_ID) assert all(e["layerId"] != FRAME_LAYER_ID for e in grouped) diff --git a/resources/tester/test_212_3_b07_cross_layout_buttons.py b/resources/tester/test_212_3_b07_cross_layout_buttons.py index 49a303508..29654ae21 100644 --- a/resources/tester/test_212_3_b07_cross_layout_buttons.py +++ b/resources/tester/test_212_3_b07_cross_layout_buttons.py @@ -6,16 +6,14 @@ · [안내선 숨기기](부르는 쪽). - 단추 · 숫자 칸 · 이름 = 좌측 패널 공용 양식(269 `.ui-panel-form`) · 한 줄 안 같은 너비. - 옛 한 줄 감기(flex-wrap) · 숫자 칸 48px 고정 없음. - - [안내선 숨기기] 뒤 남는 검은 선 = 인쇄 칸선(도각 층 도면 개체 · 회청 `#8ea0b5` - · 밝은 바탕에서 짙게 그려짐) — 안내선(겹판)과 다른 것이라 지우지 않음 · 단추는 겹판만 끔. + - 인쇄 칸선 없음(526-1 · 옛 「남는 검은 선」) — 칸은 안내선 겹판(단면마다 점선)이라 + [안내선 숨기기] 뒤 남는 선 없음 · 단추는 겹판만 끔. """ import re from pathlib import Path -from B07_DesignDetail.B07_DesignDetail_Engine_Cad import FRAME_LAYER_ID, TABLE_LINE_COLOR -from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Sheet import _grid_lines -from B07_DesignDetail.B07_DesignDetail_Engine_Template import use_project_templates +from B07_DesignDetail import B07_DesignDetail_Engine_Cad_Sheet as sheet_engine ROOT = Path(__file__).resolve().parents[2] CSS = (ROOT / "B07_DesignDetail/B07_DesignDetail_UI_Style.css").read_text(encoding="utf-8") @@ -63,14 +61,11 @@ def test_한_줄에_한_일_같은_너비(): assert "controls.append(deps.cross.guideToggle);" in SETTINGS -def test_남는_검은_선은_인쇄_칸선_지우지_않음(): - use_project_templates(None) - lines = _grid_lines("cross_s00000m", 4, 4) - assert len(lines) == 10 # 세로 5 · 가로 5 그대로 - assert all(e["layerId"] == FRAME_LAYER_ID for e in lines) - assert all(e["lineColor"] == TABLE_LINE_COLOR == "#8ea0b5" for e in lines) - # 안내선 단추는 겹판(점선 영역 + 붉은 틀)만 — 도면 개체를 안 건드림 +def test_인쇄_칸선_없음_칸은_안내선_겹판(): + assert not hasattr(sheet_engine, "_grid_lines") # 526-1 — 칸선 도면 개체 없음 + # 칸 · 점선 영역 · 붉은 틀 모두 겹판 — 숨김이면 안 그림 · 도면 개체를 안 건드림 draw = GUIDE[GUIDE.index("export function drawGuide") :] draw = draw[: draw.index("\n}\n")] assert "if (!guide || !guideVisible) return;" in draw + assert "guide.cells" in draw assert "getEntities" not in draw and "setEntities" not in draw diff --git a/resources/tester/test_212_b07_cross_station_layers.py b/resources/tester/test_212_b07_cross_station_layers.py index b875abb81..e7a4463b6 100644 --- a/resources/tester/test_212_b07_cross_station_layers.py +++ b/resources/tester/test_212_b07_cross_station_layers.py @@ -4,7 +4,7 @@ 못박는 것 - 212-1 옛 확정 장(원지반 층 잠금째 저장)도 열 때 원지반 잠금 풂 · 측점 층 없으면 stale_layers. - 212-2 장의 측점마다 층 하나(`b08-station-{측점}` · 이름 「횡단 {제목}」) = 원지반 · 계획선 · - 암 그림(자식 선까지) · 표 · 글은 수량표 층 · 격자선 · 도각은 도각 층 · 종류 층 없음. + 암 그림(자식 선까지) · 표 · 글은 수량표 층 · 도각은 도각 층 · 종류 층 없음. - 개체 id 는 그대로(층만 바뀜) · placement 에 layer_id · design_ids(화면 덧그림 갈아 끼우기). - DXF 층 이름 = 측점 층 id 그대로(금지 문자 없음). - 화면 덧그림도 측점 층 · design_ids 로 서버 설계선 걷음(소스 확인 · typecheck 는 따로). diff --git a/ui_template/cad_host/cad_host.ts b/ui_template/cad_host/cad_host.ts index 87f000e92..bbf0362f8 100644 --- a/ui_template/cad_host/cad_host.ts +++ b/ui_template/cad_host/cad_host.ts @@ -45,6 +45,8 @@ export interface CadGuide { area: CadGuideArea; /** 넘친 칸 — 붉은 사각형(169-2) */ marks?: CadGuideArea[]; + /** 횡단 장 칸 — 단면마다 점선 사각형(526-1 · 인쇄 칸선 대신) */ + cells?: CadGuideArea[]; /** 모서리 · 변 끌기 — 도각 편집 · M02 양식만(169-3) · 끌면 `onGuideChange` */ editable?: boolean; }