diff --git a/B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.test.ts b/B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.test.ts index bad08fe98..e1e6e1fbe 100644 --- a/B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.test.ts +++ b/B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.test.ts @@ -1,5 +1,8 @@ import { Point } from '@flatten-js/core'; -import { describe, expect, it } from 'vitest'; +import { beforeAll, describe, expect, it } from 'vitest'; +import { LineEntity } from '../entities/LineEntity'; +import type { Layer } from '../App.types'; +import { setEntities, setLayers } from '../state'; import { ScreenCanvasDrawController } from './screenCanvas.drawController'; /** translate·drawImage 인자만 기록하는 최소 2d 컨텍스트. */ @@ -37,3 +40,34 @@ describe('ScreenCanvasDrawController.drawImage', () => { expect(calls.rotate).toEqual([-0.5, 0.5]); }); }); + +describe('ScreenCanvasDrawController.zoomToFitScreen', () => { + beforeAll(() => { + (globalThis as { window?: unknown }).window = { dispatchEvent: () => true }; + }); + const layer = (id: string, isLocked: boolean) => + ({ id, name: id, isVisible: true, isLocked }) as unknown as Layer; + + it('얹은 도각(m02-structure-frame)이 있으면 도각 전체에 맞춘다 — 도각 밖 먼 그림에 안 끌린다', () => { + setLayers([layer('m02-structure-frame', true), layer('기본', false)]); + setEntities( + [ + new LineEntity('m02-structure-frame', new Point(0, 0), new Point(800, 600)), + new LineEntity('기본', new Point(-8000, -8000), new Point(0, 0)), + ], + false + ); + const controller = new ScreenCanvasDrawController(recordingContext().context); + controller.setCanvasSize(new Point(800, 600)); + controller.zoomToFitScreen(); + const scale = controller.getScreenScale(); + expect(scale).toBeCloseTo(0.9, 5); // 800×600 도각 → 캔버스 90 % + for (const corner of [new Point(0, 0), new Point(800, 600)]) { + const t = controller.worldToTarget(corner); + expect(t.x).toBeGreaterThanOrEqual(0); + expect(t.x).toBeLessThanOrEqual(800); + expect(t.y).toBeGreaterThanOrEqual(0); + expect(t.y).toBeLessThanOrEqual(600); + } + }); +}); diff --git a/B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.ts b/B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.ts index b958d7b47..0388acdb3 100644 --- a/B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.ts +++ b/B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.ts @@ -31,6 +31,8 @@ const CANVAS_BACKGROUND_FALLBACK = '#111'; * * To convert between the 2 coordinate systems, you need the screenOffset and screenScale */ +/** M02 가 구조물 도면에 얹는 도각 층 — 서버 `STRUCTURE_FRAME_LAYER` 와 같음 */ +const STRUCTURE_FRAME_LAYER_ID = 'm02-structure-frame'; // Batch-mode stroke decimation: skip chain segments shorter than this (screen px) const BATCH_LOD_PX = 0.5; // Batch-mode text smaller than this (screen px) is unreadable — skip drawing it @@ -150,8 +152,15 @@ export class ScreenCanvasDrawController implements DrawController { public zoomToFitScreen() { // 도각·원지반 같은 잠금 도면층은 빼고 설계 콘텐츠에만 맞춘다 (2026-08-31 사용자 // 확정). 도각까지 넣으면 A1 한 장 전체가 잡혀 화면의 40%가 여백으로 간다. + // 구조물 도면(M02·B08)은 얹은 도각(작도 틀)이 있으면 도각 전체에 맞춘다 — 옛 DXF 가 자기 도곽· + // 표제 띠를 품어 범위가 크면 도각이 화면 밖으로 밀린다. + const structureFrame = getEntities().filter((e) => e.layerId === STRUCTURE_FRAME_LAYER_ID); const pickable = getPickableEntities(); - const entities = pickable.length ? pickable : getEntities(); + const entities = structureFrame.length + ? structureFrame + : pickable.length + ? pickable + : getEntities(); if (!entities.length) { // 빈 도면 — 원점(0,0)을 화면 가운데로. 안 옮기면 기준점 십자가 왼쪽 아래 귀퉁이에 잘린다. const canvasSize = this.getCanvasSize(); diff --git a/B08_Quantity/B08_Quantity_UI_Hydro.css b/B08_Quantity/B08_Quantity_UI_Hydro.css index 30cb173d2..77a3f09ec 100644 --- a/B08_Quantity/B08_Quantity_UI_Hydro.css +++ b/B08_Quantity/B08_Quantity_UI_Hydro.css @@ -97,7 +97,7 @@ } .b08-hydro__hint { - color: var(--color-text-muted); + color: var(--color-text-secondary); font-size: var(--text-caption); } @@ -185,7 +185,7 @@ .b08-hydro__verdict { padding: var(--spacing-8) 0; - color: var(--color-text-muted); + color: var(--color-text-secondary); font-size: var(--text-body); text-align: center; } diff --git a/B08_Quantity/B08_Quantity_UI_Page.css b/B08_Quantity/B08_Quantity_UI_Page.css index afcde50ec..528ebe73e 100644 --- a/B08_Quantity/B08_Quantity_UI_Page.css +++ b/B08_Quantity/B08_Quantity_UI_Page.css @@ -6,6 +6,10 @@ gap: var(--spacing-16); } +.b08-quantity .ui-general-block__empty { + color: var(--color-text-secondary); /* muted 는 밝은 테마에서 대비 3:1 — 4.5:1 로 */ +} + .b08-quantity__tabs { display: flex; flex-wrap: wrap; diff --git a/ui_template/ui_template_overlay.css b/ui_template/ui_template_overlay.css index cff809cab..c9b6c76c5 100644 --- a/ui_template/ui_template_overlay.css +++ b/ui_template/ui_template_overlay.css @@ -134,7 +134,7 @@ max-width: 60%; min-width: 0; overflow: hidden; - color: var(--color-text-muted, var(--color-text)); + color: var(--color-text-secondary, var(--color-text)); font-size: var(--text-body-sm); text-align: right; text-overflow: ellipsis;