Centralize door animation state and operation mapping
This commit is contained in:
@@ -1,4 +1,11 @@
|
||||
import { type AnyNodeId, type DoorNode, sceneRegistry, useScene } from '@pascal-app/core'
|
||||
import {
|
||||
getGarageVisibleOpeningRatio,
|
||||
type AnyNodeId,
|
||||
type DoorNode,
|
||||
isOperationDoorType,
|
||||
sceneRegistry,
|
||||
useScene,
|
||||
} from '@pascal-app/core'
|
||||
import * as THREE from 'three'
|
||||
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
||||
import { acceleratedRaycast, computeBoundsTree, disposeBoundsTree } from 'three-mesh-bvh'
|
||||
@@ -100,14 +107,6 @@ function createDoorLeafColliderGeometry(root: THREE.Object3D, node: DoorNode) {
|
||||
const hasLeafContent = node.segments.some((segment) => segment.type !== 'empty')
|
||||
if (!hasLeafContent) return null
|
||||
|
||||
const isOperationDoor =
|
||||
node.doorType === 'folding' ||
|
||||
node.doorType === 'pocket' ||
|
||||
node.doorType === 'barn' ||
|
||||
node.doorType === 'sliding' ||
|
||||
node.doorType === 'garage-sectional' ||
|
||||
node.doorType === 'garage-rollup' ||
|
||||
node.doorType === 'garage-tiltup'
|
||||
const leafW = node.width - 2 * node.frameThickness
|
||||
const leafH = node.height - node.frameThickness
|
||||
if (leafW <= 0 || leafH <= 0) return null
|
||||
@@ -117,10 +116,7 @@ function createDoorLeafColliderGeometry(root: THREE.Object3D, node: DoorNode) {
|
||||
root.updateWorldMatrix(true, false)
|
||||
|
||||
if (node.doorType === 'garage-sectional' || node.doorType === 'garage-rollup') {
|
||||
const openAmount =
|
||||
node.doorType === 'garage-sectional'
|
||||
? Math.min(1, (node.operationState ?? 0) / 0.88)
|
||||
: Math.max(0, Math.min(1, node.operationState ?? 0))
|
||||
const openAmount = getGarageVisibleOpeningRatio(node.doorType, node.operationState)
|
||||
const visibleHeight = leafH * (1 - openAmount)
|
||||
if (visibleHeight <= 0.12) return null
|
||||
|
||||
@@ -140,7 +136,10 @@ function createDoorLeafColliderGeometry(root: THREE.Object3D, node: DoorNode) {
|
||||
return geometry
|
||||
}
|
||||
|
||||
if (isOperationDoor && (node.operationState ?? 0) >= OPERATION_DOOR_COLLIDER_OPEN_THRESHOLD) {
|
||||
if (
|
||||
isOperationDoorType(node.doorType) &&
|
||||
(node.operationState ?? 0) >= OPERATION_DOOR_COLLIDER_OPEN_THRESHOLD
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,11 @@
|
||||
import { type AnyNodeId, useScene } from '@pascal-app/core'
|
||||
import { type AnyNodeId, isOperationDoorType, useInteractive, useScene } from '@pascal-app/core'
|
||||
|
||||
export const DOOR_SWING_OPEN_ANGLE = Math.PI / 2
|
||||
|
||||
const DOOR_TOGGLE_ANIMATION_MS = 520
|
||||
const activeDoorAnimations = new Map<AnyNodeId, number>()
|
||||
|
||||
export function isOperationDoorType(doorType: string | undefined) {
|
||||
return (
|
||||
doorType === 'folding' ||
|
||||
doorType === 'pocket' ||
|
||||
doorType === 'barn' ||
|
||||
doorType === 'sliding' ||
|
||||
doorType === 'garage-sectional' ||
|
||||
doorType === 'garage-rollup' ||
|
||||
doorType === 'garage-tiltup'
|
||||
)
|
||||
}
|
||||
export { isOperationDoorType }
|
||||
|
||||
export function updateDoorOpenState(
|
||||
doorId: AnyNodeId,
|
||||
@@ -28,6 +18,25 @@ export function updateDoorOpenState(
|
||||
if (node?.parentId) scene.dirtyNodes.add(node.parentId as AnyNodeId)
|
||||
}
|
||||
|
||||
function setRuntimeDoorOpenState(
|
||||
doorId: AnyNodeId,
|
||||
data: { operationState?: number; swingAngle?: number },
|
||||
) {
|
||||
const scene = useScene.getState()
|
||||
const node = scene.nodes[doorId]
|
||||
useInteractive.getState().setDoorOpenState(doorId, data)
|
||||
scene.dirtyNodes.add(doorId)
|
||||
if (node?.parentId) scene.dirtyNodes.add(node.parentId as AnyNodeId)
|
||||
}
|
||||
|
||||
function clearRuntimeDoorOpenState(doorId: AnyNodeId) {
|
||||
const scene = useScene.getState()
|
||||
const node = scene.nodes[doorId]
|
||||
useInteractive.getState().removeDoorOpenState(doorId)
|
||||
scene.dirtyNodes.add(doorId)
|
||||
if (node?.parentId) scene.dirtyNodes.add(node.parentId as AnyNodeId)
|
||||
}
|
||||
|
||||
export function animateDoorOpenState(
|
||||
doorId: AnyNodeId,
|
||||
field: 'operationState' | 'swingAngle',
|
||||
@@ -46,13 +55,14 @@ export function animateDoorOpenState(
|
||||
const tick = (now: number) => {
|
||||
const progress = Math.min(1, (now - startedAt) / DOOR_TOGGLE_ANIMATION_MS)
|
||||
const value = from + (to - from) * ease(progress)
|
||||
updateDoorOpenState(doorId, { [field]: value })
|
||||
setRuntimeDoorOpenState(doorId, { [field]: value })
|
||||
|
||||
if (progress < 1) {
|
||||
activeDoorAnimations.set(doorId, window.requestAnimationFrame(tick))
|
||||
} else {
|
||||
activeDoorAnimations.delete(doorId)
|
||||
updateDoorOpenState(doorId, { [field]: to })
|
||||
clearRuntimeDoorOpenState(doorId)
|
||||
onComplete?.()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user