65 lines
2.2 KiB
TypeScript
65 lines
2.2 KiB
TypeScript
import { Group, Mesh, MeshStandardMaterial, SphereGeometry, Vector3 } from 'three'
|
|
import { buildSection, INCHES_TO_METERS } from '../duct-segment/geometry'
|
|
import type { PipeSegmentNode } from './schema'
|
|
|
|
const PVC_COLOR = '#f5f5f5'
|
|
const ABS_COLOR = '#3a3a3a'
|
|
const CAST_IRON_COLOR = '#54575c'
|
|
/** Vents read slightly translucent-matte so they don't visually compete
|
|
* with the water-carrying waste runs. */
|
|
const VENT_OPACITY = 0.85
|
|
|
|
const RADIAL_SEGMENTS = 20
|
|
|
|
type PipeAppearance = {
|
|
pipeMaterial: 'pvc' | 'abs' | 'cast-iron'
|
|
system: 'waste' | 'vent'
|
|
}
|
|
|
|
function getPipeColor(node: PipeAppearance): string {
|
|
if (node.pipeMaterial === 'abs') return ABS_COLOR
|
|
if (node.pipeMaterial === 'cast-iron') return CAST_IRON_COLOR
|
|
return PVC_COLOR
|
|
}
|
|
|
|
export function createPipeMaterial(node: PipeAppearance): MeshStandardMaterial {
|
|
return new MeshStandardMaterial({
|
|
color: getPipeColor(node),
|
|
metalness: node.pipeMaterial === 'cast-iron' ? 0.5 : 0.05,
|
|
roughness: node.pipeMaterial === 'cast-iron' ? 0.6 : 0.45,
|
|
transparent: node.system === 'vent',
|
|
opacity: node.system === 'vent' ? VENT_OPACITY : 1,
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Pure geometry builder for a DWV pipe run: capped cylinder sections
|
|
* between consecutive path points with sphere hubs at interior joints
|
|
* (proper wyes / sanitary tees come in the next slice). Slope lives in
|
|
* the path's Y coordinates — nothing here is slope-aware.
|
|
*/
|
|
export function buildPipeSegmentGeometry(node: PipeSegmentNode): Group {
|
|
const group = new Group()
|
|
if (node.path.length < 2) return group
|
|
|
|
const radius = (node.diameter * INCHES_TO_METERS) / 2
|
|
const material = createPipeMaterial(node)
|
|
const points = node.path.map(([x, y, z]) => new Vector3(x, y, z))
|
|
|
|
for (let i = 0; i < points.length - 1; i++) {
|
|
const a = points[i] as Vector3
|
|
const b = points[i + 1] as Vector3
|
|
const mesh = buildSection(a, b, radius, material, `pipe-section-${i}`)
|
|
if (mesh) group.add(mesh)
|
|
}
|
|
// Slightly proud hubs at interior joints — reads as a coupling.
|
|
for (let i = 1; i < points.length - 1; i++) {
|
|
const hub = new Mesh(new SphereGeometry(radius * 1.12, RADIAL_SEGMENTS, 12), material)
|
|
hub.name = `pipe-hub-${i}`
|
|
hub.position.copy(points[i] as Vector3)
|
|
group.add(hub)
|
|
}
|
|
|
|
return group
|
|
}
|