From 44a6ec51db2be56e7418e317c824c9b975cb3dd9 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 28 Sep 2026 16:14:22 +0900 Subject: [PATCH] =?UTF-8?q?fix(B07=20CAD):=20=EC=96=B4=EB=91=90=EC=9A=B4?= =?UTF-8?q?=20=ED=85=8C=EB=A7=88=EC=97=90=EC=84=9C=20=EA=B2=80=EC=A0=95=20?= =?UTF-8?q?=EC=84=A0=EC=9D=B4=20=EC=95=88=20=EB=B3=B4=EC=9D=B4=EB=8D=98=20?= =?UTF-8?q?=EA=B2=83=20=E2=80=94=20=EC=9E=90=EB=8F=99=EC=83=89(=ED=9D=B0/?= =?UTF-8?q?=EA=B2=80=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 검정 · 흰색(DXF 색 7) · 기본색(#f5f7fa = 새 개체 · DXF ByLayer)은 오토캐드처럼 자동색으로 보아 어두운 바탕에서는 흰색 · 밝은 바탕에서는 검정으로 그림(그릴 때만 · 저장 값은 그대로). 다른 색은 그대로 · 어두운 바탕에서 어두운 색을 밝히지 않음 · 밝은 바탕의 밝은 색 대비 보정은 기존대로. 좌표 입력 칸 바탕도 자동색(검정)으로 바꿔 두 테마에서 글이 읽힘. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01K7JS47SacjPxZ718QsZKyr --- B07_DesignDetail/openwebcad/src/App.consts.ts | 3 +- .../screenCanvas.drawController.ts | 2 +- B07_DesignDetail/openwebcad/src/theme.ts | 36 +++++++++++------ resources/tester/cad/theme-auto-color.test.ts | 40 +++++++++++++++++++ 4 files changed, 67 insertions(+), 14 deletions(-) create mode 100644 resources/tester/cad/theme-auto-color.test.ts diff --git a/B07_DesignDetail/openwebcad/src/App.consts.ts b/B07_DesignDetail/openwebcad/src/App.consts.ts index 22242cec..63613edb 100644 --- a/B07_DesignDetail/openwebcad/src/App.consts.ts +++ b/B07_DesignDetail/openwebcad/src/App.consts.ts @@ -176,8 +176,9 @@ export const CANVAS_INPUT_FIELD_HEIGHT = 20; /** * Canvas input field background color + * 자동색(검정) — 어두운 바탕 검정 칸에 흰 글 · 밝은 바탕 흰 칸에 검정 글(`theme.ts` paintColor) */ -export const CANVAS_INPUT_FIELD_BACKGROUND_COLOR = '#161616'; +export const CANVAS_INPUT_FIELD_BACKGROUND_COLOR = '#000000'; /** * Canvas input field text color diff --git a/B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.ts b/B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.ts index 2afd797f..56343908 100644 --- a/B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.ts +++ b/B07_DesignDetail/openwebcad/src/drawControllers/screenCanvas.drawController.ts @@ -242,7 +242,7 @@ export class ScreenCanvasDrawController implements DrawController { lineWidth: number, dash: number[] = [] ) { - // 저장된 도면 색은 다크 배경 기준 — 라이트 테마에서는 그릴 때만 대비를 맞춘다. + // 자동색(검정·흰색·기본색)은 바탕 따라 흰/검정 · 라이트 테마는 밝은 색 대비도 맞춤(그릴 때만). const color = paintColor(rawColor); if (this.batching) { const effectiveWidth = isHighlighted ? lineWidth + 1 : lineWidth; diff --git a/B07_DesignDetail/openwebcad/src/theme.ts b/B07_DesignDetail/openwebcad/src/theme.ts index 4c577bb0..aa33bee1 100644 --- a/B07_DesignDetail/openwebcad/src/theme.ts +++ b/B07_DesignDetail/openwebcad/src/theme.ts @@ -61,26 +61,38 @@ function relativeLuminance(r: number, g: number, b: number): number { } /** - * 도면 색 보정 — 저장된 엔티티 색은 다크 배경 기준(밝은 회색·흰색)이라 - * 라이트 모드 흰 종이 위에서는 안 보인다. **그릴 때만** 휘도를 낮춰 색상(색조)은 - * 유지한 채 대비를 살린다. 저장 데이터는 건드리지 않는다. + * 자동색 — 오토캐드 색 번호 7 처럼 바탕을 따라 흰/검정으로 그리는 색. + * 검정 · 흰색(DXF 색 7) · 기본색(`#f5f7fa` = 새 개체 · DXF ByLayer 가져온 색). + */ +const AUTO_COLORS = new Set(['#000000', '#ffffff', '#f5f7fa', 'black', 'white']); + +function normalizeHex(color: string): string { + const value = color.trim().toLowerCase(); + const short = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/.exec(value); + return short ? `#${short[1]}${short[1]}${short[2]}${short[2]}${short[3]}${short[3]}` : value; +} + +export function isAutoColor(color: string): boolean { + return AUTO_COLORS.has(normalizeHex(color)); +} + +/** + * 도면 색 보정 — **그릴 때만** 바꾼다(저장 데이터는 그대로). + * - 자동색: 어두운 바탕 흰색 · 밝은 바탕 검정. + * - 밝은 바탕: 흰 종이에 묻히는 밝은 색은 휘도를 낮춰 색조를 살린 채 대비를 맞춘다. + * - 어두운 바탕: 자동색 밖은 그대로(어두운 색을 밝히지 않음). */ export function paintColor(color: string): string { - if (!isLightTheme()) return color; + const light = isLightTheme(); + if (isAutoColor(color)) return light ? '#000000' : '#ffffff'; + if (!light) return color; const cached = paintCache.get(color); if (cached !== undefined) return cached; const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(color); let result = color; if (hex) { - const raw = hex[1]; - const full = - raw.length === 3 - ? raw - .split('') - .map((c) => c + c) - .join('') - : raw; + const full = normalizeHex(color).slice(1); const r = Number.parseInt(full.slice(0, 2), 16); const g = Number.parseInt(full.slice(2, 4), 16); const b = Number.parseInt(full.slice(4, 6), 16); diff --git a/resources/tester/cad/theme-auto-color.test.ts b/resources/tester/cad/theme-auto-color.test.ts new file mode 100644 index 00000000..a10e809f --- /dev/null +++ b/resources/tester/cad/theme-auto-color.test.ts @@ -0,0 +1,40 @@ +/** + * CAD 자동색(`theme.ts` paintColor) 검증 — 검정 · 흰색 · 기본색은 바탕 따라 흰/검정, 다른 색은 그대로. + * 실행: node --experimental-strip-types resources/tester/cad/theme-auto-color.test.ts + */ +import assert from 'node:assert/strict'; + +let theme = 'dark'; +// biome-ignore lint: 시험 — 브라우저 전역 흉내 +const g = globalThis as any; +g.document = { documentElement: { getAttribute: () => theme } }; +g.window = { matchMedia: () => ({ matches: true }) }; + +const { paintColor, isAutoColor } = await import( + '../../../B07_DesignDetail/openwebcad/src/theme.ts' +); + +for (const color of ['#000000', '#000', '#FFFFFF', '#fff', '#f5f7fa', 'black']) { + assert.ok(isAutoColor(color), `${color} 자동색`); +} +for (const color of ['#ff0000', '#111111', '#333333', '#1a237e', '#a55200']) { + assert.ok(!isAutoColor(color), `${color} 자동색 아님`); +} + +theme = 'dark'; +assert.equal(paintColor('#000000'), '#ffffff', '어두운 바탕: 검정은 흰색'); +assert.equal(paintColor('#f5f7fa'), '#ffffff', '어두운 바탕: 기본색은 흰색'); +assert.equal(paintColor('#ffffff'), '#ffffff'); +assert.equal(paintColor('#ff0000'), '#ff0000', '어두운 바탕: 빨강 그대로'); +assert.equal(paintColor('#1a237e'), '#1a237e', '어두운 바탕: 남색 밝히지 않음'); +assert.equal(paintColor('#333333'), '#333333', '어두운 바탕: 어두운 회색 밝히지 않음'); + +theme = 'light'; +assert.equal(paintColor('#ffffff'), '#000000', '밝은 바탕: 흰색은 검정'); +assert.equal(paintColor('#FFF'), '#000000'); +assert.equal(paintColor('#000000'), '#000000'); +assert.equal(paintColor('#f5f7fa'), '#000000', '밝은 바탕: 기본색은 검정'); +assert.equal(paintColor('#ff0000'), '#ff0000', '밝은 바탕: 빨강 그대로'); +assert.notEqual(paintColor('#ffff00'), '#ffff00', '밝은 바탕: 노랑 등 밝은 색은 대비 보정(기존)'); + +console.log('theme 자동색 시험 통과');