Fix relative move drag offsets
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
import {
|
||||
type AnyNodeId,
|
||||
type RoofEvent,
|
||||
type RoofNode,
|
||||
type RoofSegmentNode,
|
||||
sceneRegistry,
|
||||
} from '@pascal-app/core'
|
||||
import { useViewer } from '@pascal-app/viewer'
|
||||
import * as THREE from 'three'
|
||||
import { type RoofSegmentHit, resolveRoofSegmentHit } from './roof-segment-hit'
|
||||
import { getSurfaceY } from './roof-surface'
|
||||
|
||||
export type RelativeRoofDragTarget = {
|
||||
segment: RoofSegmentNode
|
||||
localX: number
|
||||
localY: number
|
||||
localZ: number
|
||||
hit: RoofSegmentHit
|
||||
}
|
||||
|
||||
type RelativeRoofDragState = {
|
||||
segmentId: string
|
||||
anchor: [number, number]
|
||||
start: [number, number, number]
|
||||
current: [number, number, number]
|
||||
surfaceOffsetY: number
|
||||
}
|
||||
|
||||
export function roofSegmentLocalToBuildingLocal(
|
||||
segmentId: string,
|
||||
position: [number, number, number],
|
||||
): [number, number, number] {
|
||||
const segmentObj = sceneRegistry.nodes.get(segmentId as AnyNodeId)
|
||||
if (!segmentObj) return position
|
||||
|
||||
const point = segmentObj.localToWorld(new THREE.Vector3(...position))
|
||||
const buildingId = useViewer.getState().selection.buildingId
|
||||
const buildingObj = buildingId ? sceneRegistry.nodes.get(buildingId as AnyNodeId) : null
|
||||
if (buildingObj) buildingObj.worldToLocal(point)
|
||||
return [point.x, point.y, point.z]
|
||||
}
|
||||
|
||||
export function createRelativeRoofDrag(original: {
|
||||
position: [number, number, number]
|
||||
roofSegmentId?: string
|
||||
}): {
|
||||
resolve: (event: RoofEvent) => RelativeRoofDragTarget | null
|
||||
} {
|
||||
let state: RelativeRoofDragState | null = null
|
||||
|
||||
const getPositionInSegment = (
|
||||
position: [number, number, number],
|
||||
fromSegmentId: string | undefined,
|
||||
segment: RoofSegmentNode,
|
||||
): [number, number, number] => {
|
||||
if (fromSegmentId === segment.id) return position
|
||||
|
||||
const fromSegmentObj = fromSegmentId
|
||||
? sceneRegistry.nodes.get(fromSegmentId as AnyNodeId)
|
||||
: null
|
||||
const targetSegmentObj = sceneRegistry.nodes.get(segment.id as AnyNodeId)
|
||||
if (!(fromSegmentObj && targetSegmentObj)) return position
|
||||
|
||||
const point = fromSegmentObj.localToWorld(new THREE.Vector3(...position))
|
||||
targetSegmentObj.worldToLocal(point)
|
||||
return [point.x, point.y, point.z]
|
||||
}
|
||||
|
||||
const getStartPositionForSegment = (
|
||||
segment: RoofSegmentNode,
|
||||
previousState: RelativeRoofDragState | null,
|
||||
): [number, number, number] => {
|
||||
if (previousState) {
|
||||
return getPositionInSegment(previousState.current, previousState.segmentId, segment)
|
||||
}
|
||||
|
||||
if (original.roofSegmentId === segment.id) return original.position
|
||||
|
||||
return getPositionInSegment(original.position, original.roofSegmentId, segment)
|
||||
}
|
||||
|
||||
return {
|
||||
resolve(event) {
|
||||
const hit = resolveRoofSegmentHit(
|
||||
event.node as RoofNode,
|
||||
event.position[0],
|
||||
event.position[1],
|
||||
event.position[2],
|
||||
)
|
||||
if (!hit) return null
|
||||
|
||||
if (!state || state.segmentId !== hit.segment.id) {
|
||||
const start = getStartPositionForSegment(hit.segment, state)
|
||||
state = {
|
||||
segmentId: hit.segment.id,
|
||||
anchor: [hit.localX, hit.localZ],
|
||||
start,
|
||||
current: start,
|
||||
surfaceOffsetY: start[1] - getSurfaceY(start[0], start[2], hit.segment),
|
||||
}
|
||||
}
|
||||
|
||||
const localX = state.start[0] + (hit.localX - state.anchor[0])
|
||||
const localZ = state.start[2] + (hit.localZ - state.anchor[1])
|
||||
const localY = getSurfaceY(localX, localZ, hit.segment) + state.surfaceOffsetY
|
||||
state.current = [localX, localY, localZ]
|
||||
return {
|
||||
segment: hit.segment,
|
||||
localX,
|
||||
localY,
|
||||
localZ,
|
||||
hit,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user