feat(view): solplate in the 3D model (2단계): wedges from the drawing's A, B, S; read the solplate table from DXF

Each bearing gets a solplate wedge: tapered along the 교축 with T1 on the 시점 side, underside at the bearing top (block
top + h3). A, B come from the drawing's ELEVATION table, S and T1/T2 from the SOLPLATE table (NO, S(%), L, W, T, T1, T2),
read by position. The 공통 규칙 (T 30, margin 100, h3 181) feed the plan; changing them regenerates the piers. A new
"솔플레이트" part can be shown or hidden. A solplate table is read only when its S values are found; a missing S is reported.
Also commits the option-1 test change (drawing EL.X minus Excel z).

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-10-23 14:46:53 +09:00
1 parent 1d8746d533
commit a50f4f5ee5
10 files changed
+579 -13

No files matched your search

+32 -1
View File
@@ -1,7 +1,8 @@
import * as THREE from 'three';
import type { PierModel } from '../core/geometry';
import { solplateGeometry } from '../core/solplate';
export type PartName = 'coping' | 'columns' | 'footings' | 'piles' | 'blinding' | 'blocks' | 'pads' | 'ground';
export type PartName = 'coping' | 'columns' | 'footings' | 'piles' | 'blinding' | 'blocks' | 'pads' | 'solplates' | 'ground';
export const PART_LABELS: Record<PartName, string> = {
coping: '코핑',
@@ -11,6 +12,7 @@ export const PART_LABELS: Record<PartName, string> = {
blinding: '버림콘크리트',
blocks: '받침블럭',
pads: '모르타르 패드',
solplates: '솔플레이트',
ground: '지반',
};
@@ -22,6 +24,7 @@ const COLORS: Record<PartName, number> = {
blinding: 0xe6e2d8,
blocks: 0xd6dde5,
pads: 0x8898aa,
solplates: 0x6f7c8a,
ground: 0x6b8f4e,
};
@@ -136,6 +139,11 @@ export function buildPierScene(m: PierModel): PierScene {
pad.rotateY(turn);
pad.translate(b.s, b.padTop - 0.025, b.n);
add('pads', pad, 1, b.k);
if (b.plan) {
// Solplate: a wedge that tapers along the 교축 (T1 on the 시점 side), on the bearing (승인 2026-10-23).
const sp = solplateGeometry(b, b.plan.h3, { A: b.plan.A, B: b.plan.B, margin: b.plan.margin, T: b.plan.T, slopePct: b.plan.S });
add('solplates', wedgeGeometry(sp.corners), 1, b.k);
}
}
// Ground: a translucent plane at the ground EL, used only to judge soil cover (not a computed input).
@@ -186,3 +194,26 @@ export function viewPosition(scene: PierScene, kind: ViewKind, m: PierModel): {
};
return { position: target.clone().add(dirs[kind].multiplyScalar(dist)), target };
}
/**
* A wedge from the eight corners of solplateGeometry: corner index (ai, ci, t) = (ai·2 + ci)·2 + t, with ai the 교축 side
* (0 = 시점), ci the 교각축 직각 side and t 0 = underside, 1 = top. Each face is a quad split into two triangles.
*/
function wedgeGeometry(corners: [number, number, number][]): THREE.BufferGeometry {
const idx = (ai: number, ci: number, t: number) => (ai * 2 + ci) * 2 + t;
const quads = [
[idx(0, 0, 0), idx(0, 1, 0), idx(1, 1, 0), idx(1, 0, 0)],
[idx(0, 0, 1), idx(1, 0, 1), idx(1, 1, 1), idx(0, 1, 1)],
[idx(0, 0, 0), idx(0, 1, 0), idx(0, 1, 1), idx(0, 0, 1)],
[idx(1, 0, 0), idx(1, 0, 1), idx(1, 1, 1), idx(1, 1, 0)],
[idx(0, 0, 0), idx(0, 0, 1), idx(1, 0, 1), idx(1, 0, 0)],
[idx(0, 1, 0), idx(1, 1, 0), idx(1, 1, 1), idx(0, 1, 1)],
];
const index: number[] = [];
for (const [q0, q1, q2, q3] of quads) index.push(q0, q1, q2, q0, q2, q3);
const geo = new THREE.BufferGeometry();
geo.setAttribute('position', new THREE.Float32BufferAttribute(corners.flat(), 3));
geo.setIndex(index);
geo.computeVertexNormals();
return geo;
}