Refine window corner shape support

This commit is contained in:
sudhir
2026-05-07 13:34:16 +05:30
parent eef19804d1
commit 133d81e563
13 changed files with 3034 additions and 6 deletions
+2
View File
@@ -73,6 +73,8 @@ export {
type DoorInteractiveState,
type ItemInteractiveState,
useInteractive,
type WindowAnimationState,
type WindowInteractiveState,
} from './store/use-interactive'
export { default as useLiveTransforms, type LiveTransform } from './store/use-live-transforms'
export { clearSceneHistory, default as useScene } from './store/use-scene'
+1 -1
View File
@@ -82,7 +82,7 @@ export {
getWallSurfaceMaterialSignature,
WallNode,
} from './nodes/wall'
export { WindowNode } from './nodes/window'
export { WindowNode, WindowType } from './nodes/window'
export { ZoneNode } from './nodes/zone'
export type { AnyNodeId, AnyNodeType } from './types'
// Union types
+22
View File
@@ -3,6 +3,20 @@ import { z } from 'zod'
import { BaseNode, nodeType, objectId } from '../base'
import { MaterialSchema } from '../material'
export const WindowType = z.enum([
'fixed',
'sliding',
'casement',
'awning',
'hopper',
'single-hung',
'double-hung',
'bay',
'bow',
'louvered',
])
export type WindowType = z.infer<typeof WindowType>
export const WindowNode = BaseNode.extend({
id: objectId('window'),
type: nodeType('window'),
@@ -21,6 +35,13 @@ export const WindowNode = BaseNode.extend({
// Opening mode - when set to "opening", the window is only a shaped cutout
openingKind: z.enum(['window', 'opening']).default('window'),
// Window family
windowType: WindowType.default('fixed'),
operationState: z.number().min(0).max(1).default(0),
awningDirection: z.enum(['up', 'down']).default('up'),
casementStyle: z.enum(['single', 'french']).default('single'),
hingesSide: z.enum(['left', 'right']).default('left'),
openingShape: z.enum(['rectangle', 'rounded', 'arch']).default('rectangle'),
openingRadiusMode: z.enum(['all', 'individual']).default('all'),
openingCornerRadii: z
@@ -50,6 +71,7 @@ export const WindowNode = BaseNode.extend({
}).describe(dedent`Window node - a parametric window placed on a wall
- position: center of the window in wall-local coordinate system
- width/height: overall outer dimensions
- windowType: explicit window family, defaulting old windows to fixed
- frameThickness: width of the frame members
- frameDepth: how deep the frame sits within the wall
- columnRatios/rowRatios: pane division ratios
@@ -26,10 +26,25 @@ export type DoorAnimationState = {
persist: boolean
}
export type WindowInteractiveState = {
operationState?: number
}
export type WindowAnimationState = {
field: keyof WindowInteractiveState
from: number
to: number
startedAt: number | null
durationMs: number
persist: boolean
}
type InteractiveStore = {
items: Record<AnyNodeId, ItemInteractiveState>
doors: Record<AnyNodeId, DoorInteractiveState>
doorAnimations: Record<AnyNodeId, DoorAnimationState>
windows: Record<AnyNodeId, WindowInteractiveState>
windowAnimations: Record<AnyNodeId, WindowAnimationState>
/** Initialize a node's interactive state from its asset definition (idempotent) */
initItem: (itemId: AnyNodeId, interactive: Interactive) => void
@@ -51,6 +66,18 @@ type InteractiveStore = {
/** Cancel a queued door animation */
cancelDoorAnimation: (doorId: AnyNodeId) => void
/** Set transient window open state without committing it to the scene node */
setWindowOpenState: (windowId: AnyNodeId, value: WindowInteractiveState) => void
/** Clear transient window open state */
removeWindowOpenState: (windowId: AnyNodeId) => void
/** Queue a window animation for the viewer frame loop */
startWindowAnimation: (windowId: AnyNodeId, value: WindowAnimationState) => void
/** Cancel a queued window animation */
cancelWindowAnimation: (windowId: AnyNodeId) => void
}
const defaultControlValue = (interactive: Interactive, index: number): ControlValue => {
@@ -70,6 +97,8 @@ export const useInteractive = create<InteractiveStore>((set, get) => ({
items: {},
doors: {},
doorAnimations: {},
windows: {},
windowAnimations: {},
initItem: (itemId, interactive) => {
const { controls } = interactive
@@ -139,4 +168,39 @@ export const useInteractive = create<InteractiveStore>((set, get) => ({
return { doorAnimations: rest }
})
},
setWindowOpenState: (windowId, value) => {
set((state) => ({
windows: {
...state.windows,
[windowId]: {
...state.windows[windowId],
...value,
},
},
}))
},
removeWindowOpenState: (windowId) => {
set((state) => {
const { [windowId]: _, ...rest } = state.windows
return { windows: rest }
})
},
startWindowAnimation: (windowId, value) => {
set((state) => ({
windowAnimations: {
...state.windowAnimations,
[windowId]: value,
},
}))
},
cancelWindowAnimation: (windowId) => {
set((state) => {
const { [windowId]: _, ...rest } = state.windowAnimations
return { windowAnimations: rest }
})
},
}))