item scale
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { getScaledDimensions } from '../../schema'
|
||||
import type { AnyNode, CeilingNode, ItemNode, SlabNode, WallNode } from '../../schema'
|
||||
import { SpatialGrid } from './spatial-grid'
|
||||
import { WallSpatialGrid } from './wall-spatial-grid'
|
||||
@@ -291,7 +292,7 @@ export class SpatialGridManager {
|
||||
if (wallId && this.walls.has(wallId)) {
|
||||
const wallLength = this.getWallLength(wallId)
|
||||
if (wallLength > 0) {
|
||||
const [width, height] = item.asset.dimensions
|
||||
const [width, height] = getScaledDimensions(item)
|
||||
const halfW = width / wallLength / 2
|
||||
// Calculate t from local X position (position[0] is distance along wall)
|
||||
const t = item.position[0] / wallLength
|
||||
@@ -312,7 +313,7 @@ export class SpatialGridManager {
|
||||
// Ceiling item - use parentId as the ceiling ID
|
||||
const ceilingId = item.parentId
|
||||
if (ceilingId && this.ceilings.has(ceilingId)) {
|
||||
this.getCeilingGrid(ceilingId).insert(item.id, item.position, item.asset.dimensions, item.rotation)
|
||||
this.getCeilingGrid(ceilingId).insert(item.id, item.position, getScaledDimensions(item), item.rotation)
|
||||
this.itemCeilingMap.set(item.id, ceilingId)
|
||||
}
|
||||
} else if (!item.asset.attachTo) {
|
||||
@@ -320,7 +321,7 @@ export class SpatialGridManager {
|
||||
this.getFloorGrid(levelId).insert(
|
||||
item.id,
|
||||
item.position,
|
||||
item.asset.dimensions,
|
||||
getScaledDimensions(item),
|
||||
item.rotation,
|
||||
)
|
||||
}
|
||||
@@ -344,7 +345,7 @@ export class SpatialGridManager {
|
||||
if (wallId && this.walls.has(wallId)) {
|
||||
const wallLength = this.getWallLength(wallId)
|
||||
if (wallLength > 0) {
|
||||
const [width, height] = item.asset.dimensions
|
||||
const [width, height] = getScaledDimensions(item)
|
||||
const halfW = width / wallLength / 2
|
||||
// Calculate t from local X position (position[0] is distance along wall)
|
||||
const t = item.position[0] / wallLength
|
||||
@@ -371,14 +372,14 @@ export class SpatialGridManager {
|
||||
// Insert into new ceiling grid
|
||||
const ceilingId = item.parentId
|
||||
if (ceilingId && this.ceilings.has(ceilingId)) {
|
||||
this.getCeilingGrid(ceilingId).insert(item.id, item.position, item.asset.dimensions, item.rotation)
|
||||
this.getCeilingGrid(ceilingId).insert(item.id, item.position, getScaledDimensions(item), item.rotation)
|
||||
this.itemCeilingMap.set(item.id, ceilingId)
|
||||
}
|
||||
} else if (!item.asset.attachTo) {
|
||||
this.getFloorGrid(levelId).update(
|
||||
item.id,
|
||||
item.position,
|
||||
item.asset.dimensions,
|
||||
getScaledDimensions(item),
|
||||
item.rotation,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { AnyNode, AnyNodeId, ItemNode, SlabNode, WallNode } from '../../schema'
|
||||
import { getScaledDimensions, type AnyNode, type AnyNodeId, type ItemNode, type SlabNode, type WallNode } from '../../schema'
|
||||
import useScene from '../../store/use-scene'
|
||||
import { itemOverlapsPolygon, spatialGridManager, wallOverlapsPolygon } from './spatial-grid-manager'
|
||||
|
||||
@@ -73,11 +73,16 @@ export function initSpatialGridSync() {
|
||||
if (
|
||||
!arraysEqual(node.position, prev.position) ||
|
||||
!arraysEqual(node.rotation, prev.rotation) ||
|
||||
!arraysEqual(node.scale, prev.scale) ||
|
||||
node.parentId !== prev.parentId ||
|
||||
node.side !== prev.side
|
||||
) {
|
||||
const levelId = resolveLevelId(node, state.nodes)
|
||||
spatialGridManager.handleNodeUpdated(node, levelId)
|
||||
// Scale changes affect footprint size — mark dirty so slab elevation recalculates
|
||||
if (!arraysEqual(node.scale, prev.scale)) {
|
||||
markDirty(node.id)
|
||||
}
|
||||
}
|
||||
} else if (node.type === 'slab' && prev.type === 'slab') {
|
||||
if (node.polygon !== prev.polygon || node.elevation !== prev.elevation) {
|
||||
@@ -114,7 +119,7 @@ function markNodesOverlappingSlab(
|
||||
// Only floor items are affected by slabs
|
||||
if (item.asset.attachTo) continue
|
||||
if (resolveLevelId(node, nodes) !== slabLevelId) continue
|
||||
if (itemOverlapsPolygon(item.position, item.asset.dimensions, item.rotation, slab.polygon, 0.01)) {
|
||||
if (itemOverlapsPolygon(item.position, getScaledDimensions(item), item.rotation, slab.polygon, 0.01)) {
|
||||
markDirty(node.id)
|
||||
}
|
||||
} else if (node.type === 'wall') {
|
||||
|
||||
@@ -3,7 +3,7 @@ export { BaseNode, generateId, Material, nodeType, objectId } from './base'
|
||||
// Camera
|
||||
export { CameraSchema } from './camera'
|
||||
export type { AssetInput } from './nodes/item'
|
||||
export { ItemNode } from './nodes/item'
|
||||
export { getScaledDimensions, ItemNode } from './nodes/item'
|
||||
export { LevelNode } from './nodes/level'
|
||||
// Nodes
|
||||
export { SiteNode } from './nodes/site'
|
||||
|
||||
@@ -30,6 +30,7 @@ export const ItemNode = BaseNode.extend({
|
||||
type: nodeType('item'),
|
||||
position: z.tuple([z.number(), z.number(), z.number()]).default([0, 0, 0]),
|
||||
rotation: z.tuple([z.number(), z.number(), z.number()]).default([0, 0, 0]),
|
||||
scale: z.tuple([z.number(), z.number(), z.number()]).default([1, 1, 1]),
|
||||
side: z.enum(['front', 'back']).optional(),
|
||||
children: z.array(objectId('item')).default([]),
|
||||
|
||||
@@ -53,3 +54,13 @@ export const ItemNode = BaseNode.extend({
|
||||
`)
|
||||
|
||||
export type ItemNode = z.infer<typeof ItemNode>
|
||||
|
||||
/**
|
||||
* Returns the effective world-space dimensions of an item after applying its scale.
|
||||
* Use this everywhere item.asset.dimensions is used for spatial calculations.
|
||||
*/
|
||||
export function getScaledDimensions(item: ItemNode): [number, number, number] {
|
||||
const [w, h, d] = item.asset.dimensions
|
||||
const [sx, sy, sz] = item.scale
|
||||
return [w * sx, h * sy, d * sz]
|
||||
}
|
||||
|
||||
@@ -68,13 +68,21 @@ const useScene: UseSceneStore = create<SceneState>()(
|
||||
},
|
||||
|
||||
setScene: (nodes, rootNodeIds) => {
|
||||
// Backward compat: add default scale to item nodes loaded from external sources
|
||||
// (pascal_local_projects, Supabase) saved before scale was added to ItemNode
|
||||
const patchedNodes = { ...nodes }
|
||||
for (const [id, node] of Object.entries(patchedNodes)) {
|
||||
if (node.type === 'item' && !('scale' in node)) {
|
||||
patchedNodes[id as AnyNodeId] = { ...(node as object), scale: [1, 1, 1] } as AnyNode
|
||||
}
|
||||
}
|
||||
set({
|
||||
nodes,
|
||||
nodes: patchedNodes,
|
||||
rootNodeIds,
|
||||
dirtyNodes: new Set<AnyNodeId>(),
|
||||
})
|
||||
// Mark all nodes as dirty to trigger re-validation
|
||||
Object.values(nodes).forEach((node) => {
|
||||
Object.values(patchedNodes).forEach((node) => {
|
||||
get().markDirty(node.id)
|
||||
})
|
||||
},
|
||||
@@ -146,6 +154,7 @@ const useScene: UseSceneStore = create<SceneState>()(
|
||||
),
|
||||
{
|
||||
name: 'editor-storage',
|
||||
version: 1,
|
||||
partialize: (state) => ({
|
||||
nodes: Object.fromEntries(
|
||||
Object.entries(state.nodes).filter(([_, node]) => {
|
||||
@@ -157,6 +166,19 @@ const useScene: UseSceneStore = create<SceneState>()(
|
||||
),
|
||||
rootNodeIds: state.rootNodeIds,
|
||||
}),
|
||||
merge: (persistedState, currentState) => {
|
||||
console.log('merge calling...', persistedState, currentState)
|
||||
const persisted = persistedState as Partial<SceneState>
|
||||
// Backward compat: add default scale to item nodes saved before scale was added
|
||||
if (persisted.nodes) {
|
||||
for (const [id, node] of Object.entries(persisted.nodes)) {
|
||||
if (node.type === 'item' && !('scale' in node)) {
|
||||
persisted.nodes[id as AnyNodeId] = { ...(node as object), scale: [1, 1, 1] } as AnyNode
|
||||
}
|
||||
}
|
||||
}
|
||||
return { ...currentState, ...persisted }
|
||||
},
|
||||
onRehydrateStorage: (state) => {
|
||||
console.log('hydrating...')
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import type * as THREE from 'three'
|
||||
import { sceneRegistry } from '../../hooks/scene-registry/scene-registry'
|
||||
import { spatialGridManager } from '../../hooks/spatial-grid/spatial-grid-manager'
|
||||
import { resolveLevelId } from '../../hooks/spatial-grid/spatial-grid-sync'
|
||||
import type { AnyNodeId, ItemNode, WallNode } from '../../schema'
|
||||
import { getScaledDimensions, type AnyNodeId, type ItemNode, type WallNode } from '../../schema'
|
||||
import useScene from '../../store/use-scene'
|
||||
|
||||
// ============================================================================
|
||||
@@ -43,7 +43,7 @@ export const ItemSystem = () => {
|
||||
const slabElevation = spatialGridManager.getSlabElevationForItem(
|
||||
levelId,
|
||||
item.position,
|
||||
item.asset.dimensions,
|
||||
getScaledDimensions(item),
|
||||
item.rotation,
|
||||
)
|
||||
mesh.position.y = slabElevation + item.position[1]
|
||||
|
||||
Reference in New Issue
Block a user