Centralize door animation state and operation mapping

This commit is contained in:
sudhir
2026-05-05 17:23:48 +05:30
parent 0ef2254e6b
commit f5f80d0f3f
6 changed files with 139 additions and 37 deletions
+8
View File
@@ -33,6 +33,13 @@ export {
} from './hooks/spatial-grid/spatial-grid-sync'
export { useSpatialQuery } from './hooks/spatial-grid/use-spatial-query'
export { loadAssetUrl, saveAsset } from './lib/asset-storage'
export {
clampDoorOperationState,
getDoorRenderOpenAmount,
getGarageVisibleOpeningRatio,
isOperationDoorType,
SECTIONAL_GARAGE_RENDER_OPEN_SCALE,
} from './lib/door-operation'
export { getRenderableSlabPolygon } from './lib/slab-polygon'
export {
detectSpacesForLevel,
@@ -61,6 +68,7 @@ export {
} from './store/history-control'
export {
type ControlValue,
type DoorInteractiveState,
type ItemInteractiveState,
useInteractive,
} from './store/use-interactive'
+42
View File
@@ -0,0 +1,42 @@
import type { DoorNode, DoorType } from '../schema/nodes/door'
export const SECTIONAL_GARAGE_RENDER_OPEN_SCALE = 0.88
export function clampDoorOperationState(value: number | undefined) {
return Math.max(0, Math.min(1, value ?? 0))
}
export function isOperationDoorType(
doorType: DoorType | DoorNode['doorType'] | string | undefined,
) {
return (
doorType === 'folding' ||
doorType === 'pocket' ||
doorType === 'barn' ||
doorType === 'sliding' ||
doorType === 'garage-sectional' ||
doorType === 'garage-rollup' ||
doorType === 'garage-tiltup'
)
}
export function getDoorRenderOpenAmount(
doorType: DoorType | DoorNode['doorType'],
operationState: number | undefined,
) {
const openAmount = clampDoorOperationState(operationState)
return doorType === 'garage-sectional'
? openAmount * SECTIONAL_GARAGE_RENDER_OPEN_SCALE
: openAmount
}
export function getGarageVisibleOpeningRatio(
doorType: DoorType | DoorNode['doorType'],
operationState: number | undefined,
) {
if (doorType === 'garage-sectional') {
return Math.min(1, clampDoorOperationState(operationState) / SECTIONAL_GARAGE_RENDER_OPEN_SCALE)
}
return clampDoorOperationState(operationState)
}
@@ -12,8 +12,14 @@ export type ItemInteractiveState = {
controlValues: ControlValue[]
}
export type DoorInteractiveState = {
operationState?: number
swingAngle?: number
}
type InteractiveStore = {
items: Record<AnyNodeId, ItemInteractiveState>
doors: Record<AnyNodeId, DoorInteractiveState>
/** Initialize a node's interactive state from its asset definition (idempotent) */
initItem: (itemId: AnyNodeId, interactive: Interactive) => void
@@ -23,6 +29,12 @@ type InteractiveStore = {
/** Remove a node's state (e.g. on unmount) */
removeItem: (itemId: AnyNodeId) => void
/** Set transient door open state without committing it to the scene node */
setDoorOpenState: (doorId: AnyNodeId, value: DoorInteractiveState) => void
/** Clear transient door open state */
removeDoorOpenState: (doorId: AnyNodeId) => void
}
const defaultControlValue = (interactive: Interactive, index: number): ControlValue => {
@@ -40,6 +52,7 @@ const defaultControlValue = (interactive: Interactive, index: number): ControlVa
export const useInteractive = create<InteractiveStore>((set, get) => ({
items: {},
doors: {},
initItem: (itemId, interactive) => {
const { controls } = interactive
@@ -74,4 +87,23 @@ export const useInteractive = create<InteractiveStore>((set, get) => ({
return { items: rest }
})
},
setDoorOpenState: (doorId, value) => {
set((state) => ({
doors: {
...state.doors,
[doorId]: {
...state.doors[doorId],
...value,
},
},
}))
},
removeDoorOpenState: (doorId) => {
set((state) => {
const { [doorId]: _, ...rest } = state.doors
return { doors: rest }
})
},
}))