fix(B07 CAD): 어두운 테마에서 검정 선이 안 보이던 것 — 자동색(흰/검정)
검정 · 흰색(DXF 색 7) · 기본색(#f5f7fa = 새 개체 · DXF ByLayer)은 오토캐드처럼 자동색으로 보아 어두운 바탕에서는 흰색 · 밝은 바탕에서는 검정으로 그림(그릴 때만 · 저장 값은 그대로). 다른 색은 그대로 · 어두운 바탕에서 어두운 색을 밝히지 않음 · 밝은 바탕의 밝은 색 대비 보정은 기존대로. 좌표 입력 칸 바탕도 자동색(검정)으로 바꿔 두 테마에서 글이 읽힘. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K7JS47SacjPxZ718QsZKyr
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 자동색 시험 통과');
|
||||
Reference in New Issue
Block a user