* Add roof surface placement support for items Items (e.g. solar panels) can now be placed on sloped roof surfaces. The placement system computes euler rotation from the roof surface normal so items sit flush on the slope instead of going inside. - Add roofStrategy to placement-strategies with enter/move/click/leave - Wire roof:enter/move/click/leave events in the placement coordinator - Add calculateRoofRotation in placement-math using surface normals - Support full 3D cursor rotation for sloped surfaces - Items on roofs are parented to the level with world-space rotation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fixed conflict * fix(editor): anchor floorplan cursor to snapped point * fix(nodes): preview floorplan edits through live overrides * feat(editor): add context-aware floorplan modes * fix(nodes): render crisp wall selection hatching * fix(editor): cap floorplan handles at extreme zoom * fix(editor): keep zone labels upright after rotation * refactor(editor): make referenced annotations registry-driven * refactor(nodes): colocate contextual dimension builders * fix(editor): use mode-driven angle snapping * fix(nodes): use mode-driven move snapping * chore(editor): update react scan tooling * refactor(floorplan): streamline construction documentation * refactor(floorplan): remove wall assembly roadmap * fix(floorplan): migrate retired scene data --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
62 lines
1.9 KiB
TypeScript
62 lines
1.9 KiB
TypeScript
import { BuildingNode as BuildingNodeSchema, type NodeDefinition } from '@pascal-app/core'
|
|
import { buildingParametrics } from './parametrics'
|
|
import { BuildingNode } from './schema'
|
|
|
|
/**
|
|
* Building — Stage A. Container for levels; can be translated /
|
|
* rotated as a whole (movable + rotatable on Y). The legacy
|
|
* `MoveBuildingContent` handles building-wide drag; the registry
|
|
* fallback would translate position, which is close to right —
|
|
* but kept legacy at Stage A to avoid disturbing the building's
|
|
* world-space group transform handling.
|
|
*/
|
|
export const buildingDefinition: NodeDefinition<typeof BuildingNode> = {
|
|
kind: 'building',
|
|
schemaVersion: 3,
|
|
schema: BuildingNode,
|
|
category: 'site',
|
|
|
|
defaults: () => {
|
|
const stub = BuildingNodeSchema.parse({ id: 'building_default' as never, type: 'building' })
|
|
const { id: _id, type: _type, ...rest } = stub
|
|
return rest
|
|
},
|
|
|
|
capabilities: {
|
|
// Building is a container — sidebar / building switcher drive
|
|
// selection, never 3D click. Same reasoning as `level` / `site`.
|
|
duplicable: false,
|
|
deletable: false,
|
|
floorplanLevelContainer: true,
|
|
presettable: false,
|
|
},
|
|
|
|
// Building-wide drag (whole-building translate + R/T rotation). Routed
|
|
// through `MoveTool`'s registry-affordance lookup rather than a
|
|
// hardcoded dispatcher arm.
|
|
affordanceTools: {
|
|
move: () => import('./move-tool'),
|
|
},
|
|
|
|
parametrics: buildingParametrics,
|
|
// No dirty consumer rebuilds this kind — see NodeDefinition.dirtyTracking.
|
|
dirtyTracking: false,
|
|
|
|
renderer: {
|
|
kind: 'parametric',
|
|
module: () => import('./renderer'),
|
|
},
|
|
|
|
presentation: {
|
|
label: 'Building',
|
|
description: 'A building container holding one or more levels.',
|
|
icon: { kind: 'url', src: '/icons/building.webp' },
|
|
paletteSection: 'site',
|
|
paletteOrder: 6,
|
|
},
|
|
|
|
mcp: {
|
|
description: 'A building container that groups levels.',
|
|
},
|
|
}
|