feat(view): colour the ③ 3D rebar by diameter or by member, with a legend

Role colours stay the default (spec §6.7). A control on ③ switches to diameter (nominal size, H and S of one size
share a colour, Okabe–Ito palette) or member (받침블럭 · 코핑 · 기둥 · 확대기초). The legend lists only the items
the model has. Highlighting and the state colours are unchanged. Approved 2026-10-23.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-10-23 09:10:20 +09:00
1 parent 5d5ad7e997
commit d35e27e7de
6 files changed
+176 -3

No files matched your search

+33 -1
View File
@@ -12,7 +12,7 @@ import { applyCopy, DEFAULT_COPY_ITEMS, planCopy, type CopyItem } from './core/c
import { pileGeometry } from './core/geometry';
import { rebarSummary, type RebarRow } from './core/rebar/summary';
import { shearSegments } from './core/rebar/coping';
import { buildRebar3D, type RebarScene } from './view/rebar3d';
import { buildRebar3D, type RebarColorMode, type RebarScene } from './view/rebar3d';
import { DEFAULT_MATERIAL, FCK_MEMBERS, isAssumedMaterial, type FckByMember, type FckMember, type Material } from './core/rebar/common';
import { allVisibleChecked, setVisibleChecked, type TypeFilter } from './core/selection';
import { choose, defaultSummary, isSelectable, SHAPE_GROUPS, UNDEFINED_REASON, type MemberId } from './core/shape-options';
@@ -80,6 +80,8 @@ const state = {
fckByMember: {} as FckByMember,
/** Bar highlighted on ③ after [보기] (시안 ③ 알림). */
rebarFocus: null as string | null,
/** Colour basis of the 3D rebar on ③: role (default), diameter, or member. */
rebarColor: 'role' as RebarColorMode,
/** Member tab on ③ (철근표). */
rebarMember: 'coping' as RebarRow['member'],
/** Member card selected on ② (시안 ② 부재 목록). */
@@ -229,6 +231,7 @@ function setupScene(): void {
if (pier?.model) {
rebar3d = buildRebar3D(pier.model, state.material, state.fckByMember);
rebar3d.group.visible = state.stage === 'rebar';
rebar3d.setColorMode(state.rebarColor);
world.add(rebar3d.group);
const scene = buildPierScene(pier.model);
for (const key of Object.keys(state.visible) as PartName[]) {
@@ -415,6 +418,7 @@ function renderAll(): void {
renderEditCard();
renderPlan();
renderRebar();
renderRebarColor();
scheduleBackup();
}
@@ -765,6 +769,34 @@ const REBAR_MEMBERS: { id: RebarRow['member']; label: string }[] = [
{ id: 'footing', label: '확대기초' },
];
/** 3D colour basis buttons on ③ and their legend (spec §6.7). */
function renderRebarColor(): void {
for (const b of document.querySelectorAll<HTMLButtonElement>('#rebar-color button')) {
b.setAttribute('aria-pressed', String(b.dataset.color === state.rebarColor));
}
const legend = $<HTMLUListElement>('rebar-legend');
legend.replaceChildren();
const items = rebar3d?.legend(state.rebarColor) ?? [];
if (state.rebarColor === 'role') {
legend.appendChild(el('li', '역할별 색 (명세 §6.7)', 'muted small'));
}
for (const item of items) {
const li = el('li', undefined, 'rebar-legend-item');
const swatch = el('span', undefined, 'swatch');
swatch.style.background = item.color;
li.append(swatch, el('span', item.label));
legend.appendChild(li);
}
}
for (const b of document.querySelectorAll<HTMLButtonElement>('#rebar-color button')) {
b.addEventListener('click', () => {
state.rebarColor = b.dataset.color as RebarColorMode;
rebar3d?.setColorMode(state.rebarColor);
renderRebarColor();
});
}
function renderRebar(): void {
const pier = activePier();
// 교각 목록 with the 배근 status (시안 ③). The checks are the same as in ① and ②.