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:
Sudhir Yadav
2026-06-16 15:30:39 -04:00
committed by GitHub
parent a0d3d9c701
commit 5551500d98
172 changed files with 17361 additions and 150 deletions
@@ -0,0 +1,106 @@
import type { NodeDefinition } from '@pascal-app/core'
import { buildHvacEquipmentFloorplan } from './floorplan'
import { buildHvacEquipmentGeometry } from './geometry'
import { hvacEquipmentParametrics } from './parametrics'
import { getHvacEquipmentPorts } from './ports'
import { HvacEquipmentNode } from './schema'
/**
* Phase 3 of the HVAC node system — equipment cabinets (furnace /
* air handler / condenser). Furnaces and air handlers expose supply +
* return ports, giving duct runs a real origin: the duct and fitting
* tools snap onto these collars like any other port.
*
* Composition: `def.geometry` only. Yaw-only rotation, so the editor's
* default R-rotate works on a selected unit without custom actions.
*/
export const hvacEquipmentDefinition: NodeDefinition<typeof HvacEquipmentNode> = {
kind: 'hvac-equipment',
schemaVersion: 1,
schema: HvacEquipmentNode,
category: 'utility',
distributionRole: 'equipment',
defaults: () => ({
object: 'node',
parentId: null,
visible: true,
metadata: {},
position: [0, 0, 0],
rotation: 0,
equipmentType: 'furnace',
width: 0.56,
depth: 0.71,
height: 1.1,
supplyShape: 'round',
returnShape: 'round',
supplyDiameter: 8,
returnDiameter: 8,
supplyWidth: 12,
supplyHeight: 8,
returnWidth: 14,
returnHeight: 8,
}),
capabilities: {
selectable: { hitVolume: 'bbox' },
movable: { axes: ['x', 'z'], gridSnap: true },
rotatable: { axes: ['y'], snapAngles: [Math.PI / 4] },
duplicable: true,
deletable: true,
floorPlaced: {
footprint: (node) => {
const n = node as HvacEquipmentNode
return {
dimensions: [n.width, n.height, n.depth],
rotation: [0, n.rotation, 0],
}
},
},
},
parametrics: hvacEquipmentParametrics,
geometry: buildHvacEquipmentGeometry,
geometryKey: (n) =>
JSON.stringify([
n.equipmentType,
n.width,
n.depth,
n.height,
n.supplyShape,
n.returnShape,
n.supplyDiameter,
n.returnDiameter,
n.supplyWidth,
n.supplyHeight,
n.returnWidth,
n.returnHeight,
]),
ports: getHvacEquipmentPorts,
floorplan: buildHvacEquipmentFloorplan,
tool: () => import('./tool'),
toolHints: [
{ key: 'Click', label: 'Place unit' },
{ key: 'R / T', label: 'Rotate ±45°' },
{ key: 'Shift', label: 'Smooth (no grid snap)' },
{ key: 'Esc', label: 'Exit' },
],
presentation: {
label: 'HVAC Unit',
description:
'Furnace, air handler, or condenser — duct runs connect to its supply/return collars.',
icon: { kind: 'url', src: '/icons/HVAC.png' },
paletteSection: 'structure',
paletteOrder: 92,
},
mcp: {
description:
'HVAC equipment cabinet (furnace, air handler, or condenser). Furnaces and air handlers have supply/return duct ports; every unit also has a refrigerant service port that a lineset run connects to. Position is level-local meters; rotation is yaw radians.',
},
}