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
}