Files
editor/packages/nodes/src/ceiling/floorplan-move.ts
T
Wassim SAMADandClaude Opus 4.7 d747d2f0ea Phase 5 Stage E: full kind migration into packages/nodes
Wholesale move of every remaining kind into its own subdirectory under
`packages/nodes/src/`, finishing the registry-driven migration. Each
kind now ships its definition, schema (re-exported from core), and any
of `geometry` / `renderer` / `system` / `floorplan` / `tool` /
`move-tool` / `panel` / `floorplan-move` / `floorplan-affordances` /
`parametrics` / `preview` it needs — no per-kind code remains under
`packages/editor/src/components/tools/` or
`packages/viewer/src/components/renderers/`.

Deleted (replaced by registry-driven equivalents):
- `tools/{ceiling,column,door,fence,item,slab,spawn,wall,window}/...`
  (boundary editors, hole editors, placement tools, move tools,
  endpoint movers, curve tools, helpers, math libs)
- `ui/helpers/{ceiling,slab,wall}-helper.tsx`
- `ui/panels/{column,door,elevator,item,roof,roof-segment,spawn,
  stair,stair-segment,wall,window}-panel.tsx`
- `viewer/src/components/renderers/{building,ceiling,column,door,
  elevator,fence,guide,item,level,roof,roof-segment,scan,site,slab,
  spawn,stair,stair-segment,wall,window,zone}-renderer.tsx`
- `viewer/src/components/viewer/legacy-system.tsx`

Added under `packages/nodes/src/`:
- `building/`, `column/`, `elevator/`, `guide/`, `level/`, `roof/`,
  `roof-segment/`, `scan/`, `shared/`, `site/`, `stair/`,
  `stair-segment/` packages with definition + schema + renderer / system
  / floorplan / panel as appropriate.
- New `floorplan-move.ts` for every kind that supports 2D moves
  (ceiling, door, item, shelf, slab, window) — single registry-driven
  dispatch path via `def.floorplanMoveTarget`.
- New `floorplan-affordances.ts` for kinds with polygon / endpoint
  drags (ceiling, fence, slab, wall) — using the shared
  `polygon-vertex-affordance` factories.
- New per-kind `panel.tsx` for kinds with custom inspector content
  (door, item, shelf, spawn, wall, window).
- New per-kind `tool.tsx` for placement (door, item, shelf, window).
- New per-kind `move-tool.tsx` for kinds with custom 3D move flows
  (door, item, slab, window).

Coordinator + manager updates in `packages/editor/`:
- `tool-manager.tsx` resolves tools from the registry only — no
  hardcoded type→component map.
- `panel-manager.tsx` resolves inspector panels the same way.
- `placement-{coordinator,strategies,types}.ts` extended with
  shelf-surface placement.
- `selection-manager.tsx` adds the registry-selectable fallback.
- `floorplan-panel.tsx`, `floorplan-background-placement.ts`,
  `floorplan-render-context.tsx` updated for the registry layer's new
  contract (props, affordance dispatch, render context).

Viewer updates:
- `viewer/index.tsx` drops legacy renderer mounts.
- `node-renderer.tsx` resolves by registry only.
- `scene-bvh.tsx`, `use-node-events.ts`, `level-system.tsx`,
  `wall-cutout.tsx`, `zone-system.tsx`, `materials.ts` adjusted for
  the registry-only world.

Sidebar tree nodes for ceiling / fence / slab / shelf / tree-node
updated to read from the registered nodes instead of the deleted
legacy renderer trees.

Wiki: new `plugin-authoring.md` page, README index updated.

Tests in `packages/nodes/src/index.test.ts` validate every registered
kind has the required shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 15:14:12 -04:00

89 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
type AnyNodeId,
type CeilingNode,
type FloorplanMoveTarget,
type FloorplanMoveTargetSession,
sceneRegistry,
useLiveTransforms,
useScene,
} from '@pascal-app/core'
import { snapPointToGrid, type WallPlanPoint } from '@pascal-app/editor'
import type * as THREE from 'three'
/**
* 2D floor-plan move handler for ceiling — mirrors the 3D `MoveCeilingTool`
* live-drag pattern. See the equivalent module in `slab/floorplan-move.ts`
* for the full rationale; the only ceiling-specific detail is the
* preserved Y offset (`CeilingSystem` positions the mesh at `height 0.01`
* on rebuild, so the direct `mesh.position.y` mirrors that to avoid a
* vertical teleport when the React group position is reconciled).
*/
const GRID_STEP = 0.5
function translatePolygon(
polygon: ReadonlyArray<readonly [number, number]>,
dx: number,
dz: number,
): Array<[number, number]> {
return polygon.map(([x, z]) => [x + dx, z + dz] as [number, number])
}
export const ceilingFloorplanMoveTarget: FloorplanMoveTarget<CeilingNode> = ({ node }) => {
const ceilingId = node.id as AnyNodeId
const originalPolygon = node.polygon.map(([x, z]) => [x, z] as [number, number])
const originalHoles = (node.holes ?? []).map((hole) =>
hole.map(([x, z]) => [x, z] as [number, number]),
)
const height = node.height ?? 2.5
let anchor: [number, number] | null = null
let lastDelta: [number, number] = [0, 0]
const session: FloorplanMoveTargetSession = {
affectedIds: [ceilingId],
apply({ planPoint, modifiers }) {
const snapped: WallPlanPoint = modifiers.shiftKey
? ([planPoint[0], planPoint[1]] as WallPlanPoint)
: snapPointToGrid([planPoint[0], planPoint[1]] as WallPlanPoint, GRID_STEP)
if (!anchor) {
anchor = [snapped[0], snapped[1]]
return
}
const dx = snapped[0] - anchor[0]
const dz = snapped[1] - anchor[1]
lastDelta = [dx, dz]
useLiveTransforms.getState().set(ceilingId, {
position: [dx, 0, dz],
rotation: 0,
})
const mesh = sceneRegistry.nodes.get(ceilingId) as THREE.Object3D | undefined
// Preserve ceiling height — `CeilingSystem` sets `mesh.position.y =
// height 0.01` on each rebuild; mirror that during the drag so
// the mesh stays at ceiling height (not collapsed to y=0).
if (mesh) mesh.position.set(dx, height - 0.01, dz)
},
canCommit() {
const live = useScene.getState().nodes[ceilingId] as CeilingNode | undefined
if (!live || live.type !== 'ceiling') return false
const [dx, dz] = lastDelta
if (dx === 0 && dz === 0) return false
// Sync commit sequence — see `slab/floorplan-move.ts` for the
// full ordering rationale (scene write → direct markDirty →
// useLiveTransforms.clear, all sync in this handler so React
// render + CeilingSystem rebuild land in the same paint).
useScene.getState().updateNodes([
{
id: ceilingId,
data: {
polygon: translatePolygon(originalPolygon, dx, dz),
holes: originalHoles.map((h) => translatePolygon(h, dx, dz)),
},
},
])
useScene.getState().markDirty(ceilingId)
useLiveTransforms.getState().clear(ceilingId)
return true
},
}
return session
}