feat: HVAC ductwork + DWV plumbing systems (#402)
Adds two new MEP node families (HVAC ductwork, DWV plumbing) built on a shared port-connectivity model. Co-authored by @sudhir9297.
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import type { FloorplanGeometry, GeometryContext } from '@pascal-app/core'
|
||||
import { INCHES_TO_METERS } from '../duct-segment/geometry'
|
||||
import { getDuctFittingPorts } from './ports'
|
||||
import type { DuctFittingNode } from './schema'
|
||||
|
||||
const SUPPLY_COLOR = '#d4825a'
|
||||
const RETURN_COLOR = '#5a8ad4'
|
||||
const BODY_COLOR = '#9ca3af'
|
||||
|
||||
/**
|
||||
* Floor-plan symbol for a duct fitting: one stub line per port from the
|
||||
* junction center out to the collar (drawn at each collar's real
|
||||
* diameter), plus a junction circle. Ports are computed in level-local
|
||||
* 3D and projected to plan, so a rotated or riser-turned fitting shows
|
||||
* its true plan footprint; a vertical port collapses onto the junction
|
||||
* circle, which is exactly how it should read from above.
|
||||
*/
|
||||
export function buildDuctFittingFloorplan(
|
||||
node: DuctFittingNode,
|
||||
ctx: GeometryContext,
|
||||
): FloorplanGeometry | null {
|
||||
const [cx, , cz] = node.position
|
||||
const ports = getDuctFittingPorts(node)
|
||||
const view = ctx.viewState
|
||||
const palette = view?.palette
|
||||
const showSelectedChrome = (view?.selected || view?.highlighted) ?? false
|
||||
const accent = node.system === 'supply' ? SUPPLY_COLOR : RETURN_COLOR
|
||||
const bodyStroke = showSelectedChrome && palette ? palette.selectedStroke : BODY_COLOR
|
||||
|
||||
const children: FloorplanGeometry[] = []
|
||||
for (const port of ports) {
|
||||
const px = port.position[0]
|
||||
const pz = port.position[2]
|
||||
// Vertical port — projects onto the junction itself; skip the stub.
|
||||
if (Math.hypot(px - cx, pz - cz) < 1e-4) continue
|
||||
children.push({
|
||||
kind: 'line',
|
||||
x1: cx,
|
||||
y1: cz,
|
||||
x2: px,
|
||||
y2: pz,
|
||||
stroke: bodyStroke,
|
||||
strokeWidth: port.diameter * INCHES_TO_METERS,
|
||||
strokeLinecap: 'round',
|
||||
opacity: showSelectedChrome ? 0.95 : 0.8,
|
||||
})
|
||||
}
|
||||
|
||||
children.push({
|
||||
kind: 'circle',
|
||||
cx,
|
||||
cy: cz,
|
||||
r: (node.diameter * INCHES_TO_METERS) / 2 + 0.015,
|
||||
fill: bodyStroke,
|
||||
stroke: accent,
|
||||
strokeWidth: 1.5,
|
||||
vectorEffect: 'non-scaling-stroke',
|
||||
opacity: 0.95,
|
||||
})
|
||||
|
||||
if (showSelectedChrome) {
|
||||
children.push({
|
||||
kind: 'move-handle',
|
||||
point: [cx, cz],
|
||||
})
|
||||
}
|
||||
|
||||
return { kind: 'group', children }
|
||||
}
|
||||
Reference in New Issue
Block a user