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:
1 parent
5d5ad7e997
commit
d35e27e7de
6 files changed
+176
-3
No files matched your search
+39
-2
@@ -6,6 +6,7 @@ import { C14_INSET, c14Bars, CAP, COPING_REBAR, shearBars, shearBarsMulti, shear
|
||||
import { columnRebar, COLUMN_P1 } from '../core/rebar/column';
|
||||
import { barsAcross, BLOCK_REBAR } from '../core/rebar/bearing-block';
|
||||
import { DEFAULT_MATERIAL, FckByMember, hookLength, Material, materialFor, NOMINAL_DIAMETER } from '../core/rebar/common';
|
||||
import { diameterGroupIndex, DIAMETER_GROUPS, memberOfBar, type RebarMemberKey } from '../core/rebar/color';
|
||||
import { stretchMatrix } from './scene';
|
||||
|
||||
/**
|
||||
@@ -18,8 +19,21 @@ export interface RebarScene {
|
||||
byName: Map<string, THREE.Mesh[]>;
|
||||
/** Highlight one bar name (or none): the others are dimmed. */
|
||||
highlight(name: string | null): void;
|
||||
/** Colour the bars by role (default, spec §6.7), by nominal diameter, or by member. */
|
||||
setColorMode(mode: RebarColorMode): void;
|
||||
/** Legend entries of the mode, only for the items this model has. Role mode has none (the role colours are in §6.7). */
|
||||
legend(mode: RebarColorMode): { label: string; color: string }[];
|
||||
}
|
||||
|
||||
export type RebarColorMode = 'role' | 'diameter' | 'member';
|
||||
|
||||
/** Okabe–Ito colours (colour-blind safe), one per diameter group in order; grey for anything else. */
|
||||
const DIAMETER_COLORS = [0xe69f00, 0x56b4e9, 0x009e73, 0x0072b2, 0xd55e00, 0xcc79a7, 0x000000, 0x8c8c8c];
|
||||
const OTHER_COLOR = 0x8c8c8c;
|
||||
const MEMBER_COLORS: Record<RebarMemberKey, number> = { bearing: 0xe69f00, coping: 0x0072b2, column: 0x009e73, footing: 0xcc79a7, other: OTHER_COLOR };
|
||||
const MEMBER_LABELS: Record<RebarMemberKey, string> = { bearing: '받침블럭', coping: '코핑', column: '기둥', footing: '확대기초', other: '기타' };
|
||||
const hex = (v: number) => `#${v.toString(16).padStart(6, '0')}`;
|
||||
|
||||
const COLOR = {
|
||||
coping: 0xb4531f,
|
||||
shear: 0x6b3fa0,
|
||||
@@ -54,7 +68,7 @@ export function buildRebar3D(m: PierModel, common: Material = DEFAULT_MATERIAL,
|
||||
geo.applyMatrix4(Ms[k]);
|
||||
const mat = new THREE.MeshStandardMaterial({ color, roughness: 0.6, metalness: 0.1 });
|
||||
const mesh = new THREE.Mesh(geo, mat);
|
||||
mesh.userData.name = name;
|
||||
mesh.userData = { name, roleColor: color, dMm, member: memberOfBar(name) };
|
||||
group.add(mesh);
|
||||
const list = byName.get(name) ?? [];
|
||||
list.push(mesh);
|
||||
@@ -436,5 +450,28 @@ export function buildRebar3D(m: PierModel, common: Material = DEFAULT_MATERIAL,
|
||||
}
|
||||
}
|
||||
};
|
||||
return { group, byName, highlight };
|
||||
const colorOf = (mesh: THREE.Mesh, mode: RebarColorMode): number => {
|
||||
const data = mesh.userData as { roleColor: number; dMm: number; member: RebarMemberKey };
|
||||
if (mode === 'role') return data.roleColor;
|
||||
if (mode === 'member') return MEMBER_COLORS[data.member];
|
||||
const idx = diameterGroupIndex(data.dMm);
|
||||
return idx < 0 ? OTHER_COLOR : DIAMETER_COLORS[idx];
|
||||
};
|
||||
const setColorMode = (mode: RebarColorMode) => {
|
||||
for (const meshes of byName.values()) {
|
||||
for (const mesh of meshes) (mesh.material as THREE.MeshStandardMaterial).color.setHex(colorOf(mesh, mode));
|
||||
}
|
||||
};
|
||||
const legend = (mode: RebarColorMode) => {
|
||||
const all = [...byName.values()].flat().map((mesh) => mesh.userData as { dMm: number; member: RebarMemberKey });
|
||||
if (mode === 'role') return [];
|
||||
if (mode === 'member') {
|
||||
const present = new Set(all.map((d) => d.member));
|
||||
return (Object.keys(MEMBER_LABELS) as RebarMemberKey[]).filter((k) => present.has(k)).map((k) => ({ label: MEMBER_LABELS[k], color: hex(MEMBER_COLORS[k]) }));
|
||||
}
|
||||
const present = new Set(all.map((d) => diameterGroupIndex(d.dMm)));
|
||||
const items = DIAMETER_GROUPS.map((g, i) => ({ label: g.label, color: hex(DIAMETER_COLORS[i]) })).filter((_, i) => present.has(i));
|
||||
return present.has(-1) ? [...items, { label: '기타', color: hex(OTHER_COLOR) }] : items;
|
||||
};
|
||||
return { group, byName, highlight, setColorMode, legend };
|
||||
}
|
||||
Reference in new issue
Block a user