rename scene classes for clarity (#1409)
* rename for clarity * typo * make coverage happ+ somewhat pointless since we don't use coverage because its not complete with both vitest and playwright * local storage issue * fmt * fix
This commit is contained in:
252
src/clientSideScene/ClientSideSceneComp.tsx
Normal file
252
src/clientSideScene/ClientSideSceneComp.tsx
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
import { useRef, useEffect, useState } from 'react'
|
||||||
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
|
|
||||||
|
import { cameraMouseDragGuards } from 'lib/cameraControls'
|
||||||
|
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||||
|
import { useStore } from 'useStore'
|
||||||
|
import {
|
||||||
|
DEBUG_SHOW_BOTH_SCENES,
|
||||||
|
ReactCameraProperties,
|
||||||
|
sceneInfra,
|
||||||
|
} from './sceneInfra'
|
||||||
|
import { throttle } from 'lib/utils'
|
||||||
|
|
||||||
|
function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } {
|
||||||
|
const [isCamMoving, setIsCamMoving] = useState(false)
|
||||||
|
const [isTween, setIsTween] = useState(false)
|
||||||
|
|
||||||
|
const { state } = useModelingContext()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
sceneInfra.setIsCamMovingCallback((isMoving, isTween) => {
|
||||||
|
setIsCamMoving(isMoving)
|
||||||
|
setIsTween(isTween)
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (DEBUG_SHOW_BOTH_SCENES || !isCamMoving)
|
||||||
|
return { hideClient: false, hideServer: false }
|
||||||
|
let hideServer = state.matches('Sketch') || state.matches('Sketch no face')
|
||||||
|
if (isTween) {
|
||||||
|
hideServer = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return { hideClient: !hideServer, hideServer }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ClientSideScene = ({
|
||||||
|
cameraControls,
|
||||||
|
}: {
|
||||||
|
cameraControls: ReturnType<
|
||||||
|
typeof useGlobalStateContext
|
||||||
|
>['settings']['context']['cameraControls']
|
||||||
|
}) => {
|
||||||
|
const canvasRef = useRef<HTMLDivElement>(null)
|
||||||
|
const { state, send } = useModelingContext()
|
||||||
|
const { hideClient, hideServer } = useShouldHideScene()
|
||||||
|
const { setHighlightRange } = useStore((s) => ({
|
||||||
|
setHighlightRange: s.setHighlightRange,
|
||||||
|
highlightRange: s.highlightRange,
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Listen for changes to the camera controls setting
|
||||||
|
// and update the client-side scene's controls accordingly.
|
||||||
|
useEffect(() => {
|
||||||
|
sceneInfra.setInteractionGuards(cameraMouseDragGuards[cameraControls])
|
||||||
|
}, [cameraControls])
|
||||||
|
useEffect(() => {
|
||||||
|
sceneInfra.updateOtherSelectionColors(
|
||||||
|
state?.context?.selectionRanges?.otherSelections || []
|
||||||
|
)
|
||||||
|
}, [state?.context?.selectionRanges?.otherSelections])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!canvasRef.current) return
|
||||||
|
const canvas = canvasRef.current
|
||||||
|
canvas.appendChild(sceneInfra.renderer.domElement)
|
||||||
|
sceneInfra.animate()
|
||||||
|
sceneInfra.setHighlightCallback(setHighlightRange)
|
||||||
|
canvas.addEventListener('mousemove', sceneInfra.onMouseMove, false)
|
||||||
|
canvas.addEventListener('mousedown', sceneInfra.onMouseDown, false)
|
||||||
|
canvas.addEventListener('mouseup', sceneInfra.onMouseUp, false)
|
||||||
|
sceneInfra.setSend(send)
|
||||||
|
return () => {
|
||||||
|
canvas?.removeEventListener('mousemove', sceneInfra.onMouseMove)
|
||||||
|
canvas?.removeEventListener('mousedown', sceneInfra.onMouseDown)
|
||||||
|
canvas?.removeEventListener('mouseup', sceneInfra.onMouseUp)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={canvasRef}
|
||||||
|
className={`absolute inset-0 h-full w-full transition-all duration-300 ${
|
||||||
|
hideClient ? 'opacity-0' : 'opacity-100'
|
||||||
|
} ${hideServer ? 'bg-black' : ''} ${
|
||||||
|
!hideClient && !hideServer && state.matches('Sketch')
|
||||||
|
? 'bg-black/80'
|
||||||
|
: ''
|
||||||
|
}`}
|
||||||
|
></div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const throttled = throttle((a: ReactCameraProperties) => {
|
||||||
|
if (a.type === 'perspective' && a.fov) {
|
||||||
|
sceneInfra.dollyZoom(a.fov)
|
||||||
|
}
|
||||||
|
}, 1000 / 15)
|
||||||
|
|
||||||
|
export const CamDebugSettings = () => {
|
||||||
|
const [camSettings, setCamSettings] = useState<ReactCameraProperties>({
|
||||||
|
type: 'perspective',
|
||||||
|
fov: 12,
|
||||||
|
position: [0, 0, 0],
|
||||||
|
quaternion: [0, 0, 0, 1],
|
||||||
|
})
|
||||||
|
const [fov, setFov] = useState(12)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
sceneInfra.setReactCameraPropertiesCallback(setCamSettings)
|
||||||
|
}, [sceneInfra])
|
||||||
|
useEffect(() => {
|
||||||
|
if (camSettings.type === 'perspective' && camSettings.fov) {
|
||||||
|
setFov(camSettings.fov)
|
||||||
|
}
|
||||||
|
}, [(camSettings as any)?.fov])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h3>cam settings</h3>
|
||||||
|
perspective cam
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={camSettings.type === 'perspective'}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (camSettings.type === 'perspective') {
|
||||||
|
sceneInfra.useOrthographicCamera()
|
||||||
|
} else {
|
||||||
|
sceneInfra.usePerspectiveCamera()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{camSettings.type === 'perspective' && (
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min="4"
|
||||||
|
max="90"
|
||||||
|
step={0.5}
|
||||||
|
value={fov}
|
||||||
|
onChange={(e) => {
|
||||||
|
setFov(parseFloat(e.target.value))
|
||||||
|
|
||||||
|
throttled({
|
||||||
|
...camSettings,
|
||||||
|
fov: parseFloat(e.target.value),
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
className="w-full cursor-pointer pointer-events-auto"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{camSettings.type === 'perspective' && (
|
||||||
|
<div>
|
||||||
|
<span>fov</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={camSettings.fov}
|
||||||
|
className="text-black w-16"
|
||||||
|
onChange={(e) => {
|
||||||
|
sceneInfra.setCam({
|
||||||
|
...camSettings,
|
||||||
|
fov: parseFloat(e.target.value),
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{camSettings.type === 'orthographic' && (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
<span>fov</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={camSettings.zoom}
|
||||||
|
className="text-black w-16"
|
||||||
|
onChange={(e) => {
|
||||||
|
sceneInfra.setCam({
|
||||||
|
...camSettings,
|
||||||
|
zoom: parseFloat(e.target.value),
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<div>
|
||||||
|
Position
|
||||||
|
<ul className="flex">
|
||||||
|
<li>
|
||||||
|
<span className="pl-2 pr-1">x:</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step={5}
|
||||||
|
data-testid="cam-x-position"
|
||||||
|
value={camSettings.position[0]}
|
||||||
|
className="text-black w-16"
|
||||||
|
onChange={(e) => {
|
||||||
|
sceneInfra.setCam({
|
||||||
|
...camSettings,
|
||||||
|
position: [
|
||||||
|
parseFloat(e.target.value),
|
||||||
|
camSettings.position[1],
|
||||||
|
camSettings.position[2],
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="pl-2 pr-1">y:</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step={5}
|
||||||
|
data-testid="cam-y-position"
|
||||||
|
value={camSettings.position[1]}
|
||||||
|
className="text-black w-16"
|
||||||
|
onChange={(e) => {
|
||||||
|
sceneInfra.setCam({
|
||||||
|
...camSettings,
|
||||||
|
position: [
|
||||||
|
camSettings.position[0],
|
||||||
|
parseFloat(e.target.value),
|
||||||
|
camSettings.position[2],
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="pl-2 pr-1">z:</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step={5}
|
||||||
|
data-testid="cam-z-position"
|
||||||
|
value={camSettings.position[2]}
|
||||||
|
className="text-black w-16"
|
||||||
|
onChange={(e) => {
|
||||||
|
sceneInfra.setCam({
|
||||||
|
...camSettings,
|
||||||
|
position: [
|
||||||
|
camSettings.position[0],
|
||||||
|
camSettings.position[1],
|
||||||
|
parseFloat(e.target.value),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -25,14 +25,14 @@ import {
|
|||||||
INTERSECTION_PLANE_LAYER,
|
INTERSECTION_PLANE_LAYER,
|
||||||
isQuaternionVertical,
|
isQuaternionVertical,
|
||||||
RAYCASTABLE_PLANE,
|
RAYCASTABLE_PLANE,
|
||||||
setupSingleton,
|
sceneInfra,
|
||||||
SKETCH_GROUP_SEGMENTS,
|
SKETCH_GROUP_SEGMENTS,
|
||||||
SKETCH_LAYER,
|
SKETCH_LAYER,
|
||||||
X_AXIS,
|
X_AXIS,
|
||||||
XZ_PLANE,
|
XZ_PLANE,
|
||||||
Y_AXIS,
|
Y_AXIS,
|
||||||
YZ_PLANE,
|
YZ_PLANE,
|
||||||
} from './setup'
|
} from './sceneInfra'
|
||||||
import {
|
import {
|
||||||
CallExpression,
|
CallExpression,
|
||||||
getTangentialArcToInfo,
|
getTangentialArcToInfo,
|
||||||
@ -85,7 +85,10 @@ export const TANGENTIAL_ARC_TO_SEGMENT_BODY = 'tangential-arc-to-segment-body'
|
|||||||
export const TANGENTIAL_ARC_TO__SEGMENT_DASH =
|
export const TANGENTIAL_ARC_TO__SEGMENT_DASH =
|
||||||
'tangential-arc-to-segment-body-dashed'
|
'tangential-arc-to-segment-body-dashed'
|
||||||
|
|
||||||
class ClientSideScene {
|
// This singleton Class is responsible for all of the things the user sees and interacts with.
|
||||||
|
// That mostly mean sketch elements.
|
||||||
|
// Cameras, controls, raycasters, etc are handled by sceneInfra
|
||||||
|
class SceneEntities {
|
||||||
scene: Scene
|
scene: Scene
|
||||||
sceneProgramMemory: ProgramMemory = { root: {}, return: null }
|
sceneProgramMemory: ProgramMemory = { root: {}, return: null }
|
||||||
activeSegments: { [key: string]: Group } = {}
|
activeSegments: { [key: string]: Group } = {}
|
||||||
@ -93,18 +96,18 @@ class ClientSideScene {
|
|||||||
axisGroup: Group | null = null
|
axisGroup: Group | null = null
|
||||||
currentSketchQuaternion: Quaternion | null = null
|
currentSketchQuaternion: Quaternion | null = null
|
||||||
constructor() {
|
constructor() {
|
||||||
this.scene = setupSingleton?.scene
|
this.scene = sceneInfra?.scene
|
||||||
setupSingleton?.setOnCamChange(this.onCamChange)
|
sceneInfra?.setOnCamChange(this.onCamChange)
|
||||||
}
|
}
|
||||||
|
|
||||||
onCamChange = () => {
|
onCamChange = () => {
|
||||||
const orthoFactor = orthoScale(setupSingleton.camera)
|
const orthoFactor = orthoScale(sceneInfra.camera)
|
||||||
|
|
||||||
Object.values(this.activeSegments).forEach((segment) => {
|
Object.values(this.activeSegments).forEach((segment) => {
|
||||||
const factor =
|
const factor =
|
||||||
setupSingleton.camera instanceof OrthographicCamera
|
sceneInfra.camera instanceof OrthographicCamera
|
||||||
? orthoFactor
|
? orthoFactor
|
||||||
: perspScale(setupSingleton.camera, segment)
|
: perspScale(sceneInfra.camera, segment)
|
||||||
if (
|
if (
|
||||||
segment.userData.from &&
|
segment.userData.from &&
|
||||||
segment.userData.to &&
|
segment.userData.to &&
|
||||||
@ -135,9 +138,9 @@ class ClientSideScene {
|
|||||||
})
|
})
|
||||||
if (this.axisGroup) {
|
if (this.axisGroup) {
|
||||||
const factor =
|
const factor =
|
||||||
setupSingleton.camera instanceof OrthographicCamera
|
sceneInfra.camera instanceof OrthographicCamera
|
||||||
? orthoFactor
|
? orthoFactor
|
||||||
: perspScale(setupSingleton.camera, this.axisGroup)
|
: perspScale(sceneInfra.camera, this.axisGroup)
|
||||||
const x = this.axisGroup.getObjectByName(X_AXIS)
|
const x = this.axisGroup.getObjectByName(X_AXIS)
|
||||||
x?.scale.set(1, factor, 1)
|
x?.scale.set(1, factor, 1)
|
||||||
const y = this.axisGroup.getObjectByName(Y_AXIS)
|
const y = this.axisGroup.getObjectByName(Y_AXIS)
|
||||||
@ -259,11 +262,11 @@ class ClientSideScene {
|
|||||||
sketchGroup.position[1],
|
sketchGroup.position[1],
|
||||||
sketchGroup.position[2]
|
sketchGroup.position[2]
|
||||||
)
|
)
|
||||||
const orthoFactor = orthoScale(setupSingleton.camera)
|
const orthoFactor = orthoScale(sceneInfra.camera)
|
||||||
const factor =
|
const factor =
|
||||||
setupSingleton.camera instanceof OrthographicCamera
|
sceneInfra.camera instanceof OrthographicCamera
|
||||||
? orthoFactor
|
? orthoFactor
|
||||||
: perspScale(setupSingleton.camera, dummy)
|
: perspScale(sceneInfra.camera, dummy)
|
||||||
sketchGroup.value.forEach((segment, index) => {
|
sketchGroup.value.forEach((segment, index) => {
|
||||||
let segPathToNode = getNodePathFromSourceRange(
|
let segPathToNode = getNodePathFromSourceRange(
|
||||||
draftSegment ? truncatedAst : kclManager.ast,
|
draftSegment ? truncatedAst : kclManager.ast,
|
||||||
@ -310,7 +313,7 @@ class ClientSideScene {
|
|||||||
|
|
||||||
this.scene.add(group)
|
this.scene.add(group)
|
||||||
if (!draftSegment) {
|
if (!draftSegment) {
|
||||||
setupSingleton.setCallbacks({
|
sceneInfra.setCallbacks({
|
||||||
onDrag: (args) => {
|
onDrag: (args) => {
|
||||||
this.onDragSegment({
|
this.onDragSegment({
|
||||||
...args,
|
...args,
|
||||||
@ -320,7 +323,7 @@ class ClientSideScene {
|
|||||||
onMove: () => {},
|
onMove: () => {},
|
||||||
onClick: (args) => {
|
onClick: (args) => {
|
||||||
if (!args || !args.object) {
|
if (!args || !args.object) {
|
||||||
setupSingleton.modelingSend({
|
sceneInfra.modelingSend({
|
||||||
type: 'Set selection',
|
type: 'Set selection',
|
||||||
data: {
|
data: {
|
||||||
selectionType: 'singleCodeCursor',
|
selectionType: 'singleCodeCursor',
|
||||||
@ -331,7 +334,7 @@ class ClientSideScene {
|
|||||||
const { object } = args
|
const { object } = args
|
||||||
const event = getEventForSegmentSelection(object)
|
const event = getEventForSegmentSelection(object)
|
||||||
if (!event) return
|
if (!event) return
|
||||||
setupSingleton.modelingSend(event)
|
sceneInfra.modelingSend(event)
|
||||||
},
|
},
|
||||||
onMouseEnter: ({ object }) => {
|
onMouseEnter: ({ object }) => {
|
||||||
// TODO change the color of the segment to yellow?
|
// TODO change the color of the segment to yellow?
|
||||||
@ -351,15 +354,15 @@ class ClientSideScene {
|
|||||||
parent.userData.pathToNode,
|
parent.userData.pathToNode,
|
||||||
'CallExpression'
|
'CallExpression'
|
||||||
).node
|
).node
|
||||||
setupSingleton.highlightCallback([node.start, node.end])
|
sceneInfra.highlightCallback([node.start, node.end])
|
||||||
const yellow = 0xffff00
|
const yellow = 0xffff00
|
||||||
colorSegment(object, yellow)
|
colorSegment(object, yellow)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setupSingleton.highlightCallback([0, 0])
|
sceneInfra.highlightCallback([0, 0])
|
||||||
},
|
},
|
||||||
onMouseLeave: ({ object }) => {
|
onMouseLeave: ({ object }) => {
|
||||||
setupSingleton.highlightCallback([0, 0])
|
sceneInfra.highlightCallback([0, 0])
|
||||||
const parent = getParentGroup(object)
|
const parent = getParentGroup(object)
|
||||||
const isSelected = parent?.userData?.isSelected
|
const isSelected = parent?.userData?.isSelected
|
||||||
colorSegment(object, isSelected ? 0x0000ff : 0xffffff)
|
colorSegment(object, isSelected ? 0x0000ff : 0xffffff)
|
||||||
@ -372,7 +375,7 @@ class ClientSideScene {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
setupSingleton.setCallbacks({
|
sceneInfra.setCallbacks({
|
||||||
onDrag: () => {},
|
onDrag: () => {},
|
||||||
onClick: async (args) => {
|
onClick: async (args) => {
|
||||||
if (!args) return
|
if (!args) return
|
||||||
@ -427,7 +430,7 @@ class ClientSideScene {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
setupSingleton.controls.enableRotate = false
|
sceneInfra.controls.enableRotate = false
|
||||||
}
|
}
|
||||||
updateAstAndRejigSketch = async (
|
updateAstAndRejigSketch = async (
|
||||||
sketchPathToNode: PathToNode,
|
sketchPathToNode: PathToNode,
|
||||||
@ -530,7 +533,7 @@ class ClientSideScene {
|
|||||||
this.sceneProgramMemory = programMemory
|
this.sceneProgramMemory = programMemory
|
||||||
const sketchGroup = programMemory.root[variableDeclarationName]
|
const sketchGroup = programMemory.root[variableDeclarationName]
|
||||||
.value as Path[]
|
.value as Path[]
|
||||||
const orthoFactor = orthoScale(setupSingleton.camera)
|
const orthoFactor = orthoScale(sceneInfra.camera)
|
||||||
sketchGroup.forEach((segment, index) => {
|
sketchGroup.forEach((segment, index) => {
|
||||||
const segPathToNode = getNodePathFromSourceRange(
|
const segPathToNode = getNodePathFromSourceRange(
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
@ -546,9 +549,9 @@ class ClientSideScene {
|
|||||||
// const prevSegment = sketchGroup.slice(index - 1)[0]
|
// const prevSegment = sketchGroup.slice(index - 1)[0]
|
||||||
const type = group?.userData?.type
|
const type = group?.userData?.type
|
||||||
const factor =
|
const factor =
|
||||||
setupSingleton.camera instanceof OrthographicCamera
|
sceneInfra.camera instanceof OrthographicCamera
|
||||||
? orthoFactor
|
? orthoFactor
|
||||||
: perspScale(setupSingleton.camera, group)
|
: perspScale(sceneInfra.camera, group)
|
||||||
if (type === TANGENTIAL_ARC_TO_SEGMENT) {
|
if (type === TANGENTIAL_ARC_TO_SEGMENT) {
|
||||||
this.updateTangentialArcToSegment({
|
this.updateTangentialArcToSegment({
|
||||||
prevSegment: sketchGroup[index - 1],
|
prevSegment: sketchGroup[index - 1],
|
||||||
@ -705,9 +708,9 @@ class ClientSideScene {
|
|||||||
}
|
}
|
||||||
async animateAfterSketch() {
|
async animateAfterSketch() {
|
||||||
if (isReducedMotion()) {
|
if (isReducedMotion()) {
|
||||||
setupSingleton.usePerspectiveCamera()
|
sceneInfra.usePerspectiveCamera()
|
||||||
} else {
|
} else {
|
||||||
await setupSingleton.animateToPerspective()
|
await sceneInfra.animateToPerspective()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
removeSketchGrid() {
|
removeSketchGrid() {
|
||||||
@ -740,7 +743,7 @@ class ClientSideScene {
|
|||||||
reject()
|
reject()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setupSingleton.controls.enableRotate = true
|
sceneInfra.controls.enableRotate = true
|
||||||
this.activeSegments = {}
|
this.activeSegments = {}
|
||||||
// maybe should reset onMove etc handlers
|
// maybe should reset onMove etc handlers
|
||||||
if (shouldResolve) resolve(true)
|
if (shouldResolve) resolve(true)
|
||||||
@ -759,7 +762,7 @@ class ClientSideScene {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
setupDefaultPlaneHover() {
|
setupDefaultPlaneHover() {
|
||||||
setupSingleton.setCallbacks({
|
sceneInfra.setCallbacks({
|
||||||
onMouseEnter: ({ object }) => {
|
onMouseEnter: ({ object }) => {
|
||||||
if (object.parent.userData.type !== DEFAULT_PLANES) return
|
if (object.parent.userData.type !== DEFAULT_PLANES) return
|
||||||
const type: DefaultPlane = object.userData.type
|
const type: DefaultPlane = object.userData.type
|
||||||
@ -784,7 +787,7 @@ class ClientSideScene {
|
|||||||
planeString = posNorm ? 'XZ' : '-XZ'
|
planeString = posNorm ? 'XZ' : '-XZ'
|
||||||
normal = posNorm ? [0, 1, 0] : [0, -1, 0]
|
normal = posNorm ? [0, 1, 0] : [0, -1, 0]
|
||||||
}
|
}
|
||||||
setupSingleton.modelingSend({
|
sceneInfra.modelingSend({
|
||||||
type: 'Select default plane',
|
type: 'Select default plane',
|
||||||
data: {
|
data: {
|
||||||
plane: planeString,
|
plane: planeString,
|
||||||
@ -798,7 +801,7 @@ class ClientSideScene {
|
|||||||
|
|
||||||
export type DefaultPlaneStr = 'XY' | 'XZ' | 'YZ' | '-XY' | '-XZ' | '-YZ'
|
export type DefaultPlaneStr = 'XY' | 'XZ' | 'YZ' | '-XY' | '-XZ' | '-YZ'
|
||||||
|
|
||||||
export const clientSideScene = new ClientSideScene()
|
export const sceneEntitiesManager = new SceneEntities()
|
||||||
|
|
||||||
// calculations/pure-functions/easy to test so no excuse not to
|
// calculations/pure-functions/easy to test so no excuse not to
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
import { Quaternion } from 'three'
|
import { Quaternion } from 'three'
|
||||||
import { isQuaternionVertical } from './setup'
|
import { isQuaternionVertical } from './sceneInfra'
|
||||||
|
|
||||||
describe('isQuaternionVertical', () => {
|
describe('isQuaternionVertical', () => {
|
||||||
it('should identify vertical quaternions', () => {
|
it('should identify vertical quaternions', () => {
|
@ -24,7 +24,6 @@ import {
|
|||||||
Object3DEventMap,
|
Object3DEventMap,
|
||||||
} from 'three'
|
} from 'three'
|
||||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
|
||||||
import { useRef, useEffect, useState } from 'react'
|
|
||||||
import { engineCommandManager } from 'lang/std/engineConnection'
|
import { engineCommandManager } from 'lang/std/engineConnection'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { isReducedMotion, roundOff, throttle } from 'lib/utils'
|
import { isReducedMotion, roundOff, throttle } from 'lib/utils'
|
||||||
@ -33,9 +32,7 @@ import { useModelingContext } from 'hooks/useModelingContext'
|
|||||||
import { deg2Rad } from 'lib/utils2d'
|
import { deg2Rad } from 'lib/utils2d'
|
||||||
import * as TWEEN from '@tweenjs/tween.js'
|
import * as TWEEN from '@tweenjs/tween.js'
|
||||||
import { MouseGuard, cameraMouseDragGuards } from 'lib/cameraControls'
|
import { MouseGuard, cameraMouseDragGuards } from 'lib/cameraControls'
|
||||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
|
||||||
import { SourceRange } from 'lang/wasm'
|
import { SourceRange } from 'lang/wasm'
|
||||||
import { useStore } from 'useStore'
|
|
||||||
import { Axis } from 'lib/selections'
|
import { Axis } from 'lib/selections'
|
||||||
import { createGridHelper } from './helpers'
|
import { createGridHelper } from './helpers'
|
||||||
|
|
||||||
@ -181,7 +178,7 @@ interface onMoveCallbackArgs {
|
|||||||
intersection: Intersection<Object3D<Object3DEventMap>>
|
intersection: Intersection<Object3D<Object3DEventMap>>
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReactCameraProperties =
|
export type ReactCameraProperties =
|
||||||
| {
|
| {
|
||||||
type: 'perspective'
|
type: 'perspective'
|
||||||
fov?: number
|
fov?: number
|
||||||
@ -195,8 +192,11 @@ type ReactCameraProperties =
|
|||||||
quaternion: [number, number, number, number]
|
quaternion: [number, number, number, number]
|
||||||
}
|
}
|
||||||
|
|
||||||
class SetupSingleton {
|
// This singleton class is responsible for all of the under the hood setup for the client side scene.
|
||||||
static instance: SetupSingleton
|
// That is the cameras and switching between them, raycasters for click mouse events and their abstractions (onClick etc), setting up controls.
|
||||||
|
// Anything that added the the scene for the user to interact with is probably in SceneEntities.ts
|
||||||
|
class SceneInfra {
|
||||||
|
static instance: SceneInfra
|
||||||
scene: Scene
|
scene: Scene
|
||||||
camera: PerspectiveCamera | OrthographicCamera
|
camera: PerspectiveCamera | OrthographicCamera
|
||||||
renderer: WebGLRenderer
|
renderer: WebGLRenderer
|
||||||
@ -333,7 +333,7 @@ class SetupSingleton {
|
|||||||
const light = new AmbientLight(0x505050) // soft white light
|
const light = new AmbientLight(0x505050) // soft white light
|
||||||
this.scene.add(light)
|
this.scene.add(light)
|
||||||
|
|
||||||
SetupSingleton.instance = this
|
SceneInfra.instance = this
|
||||||
}
|
}
|
||||||
private _isCamMovingCallback: (isMoving: boolean, isTween: boolean) => void =
|
private _isCamMovingCallback: (isMoving: boolean, isTween: boolean) => void =
|
||||||
() => {}
|
() => {}
|
||||||
@ -713,7 +713,7 @@ class SetupSingleton {
|
|||||||
} | null => {
|
} | null => {
|
||||||
this.planeRaycaster.setFromCamera(
|
this.planeRaycaster.setFromCamera(
|
||||||
this.currentMouseVector,
|
this.currentMouseVector,
|
||||||
setupSingleton.camera
|
sceneInfra.camera
|
||||||
)
|
)
|
||||||
const planeIntersects = this.planeRaycaster.intersectObjects(
|
const planeIntersects = this.planeRaycaster.intersectObjects(
|
||||||
this.scene.children,
|
this.scene.children,
|
||||||
@ -976,7 +976,7 @@ class SetupSingleton {
|
|||||||
if (planesGroup) this.scene.remove(planesGroup)
|
if (planesGroup) this.scene.remove(planesGroup)
|
||||||
}
|
}
|
||||||
updateOtherSelectionColors = (otherSelections: Axis[]) => {
|
updateOtherSelectionColors = (otherSelections: Axis[]) => {
|
||||||
const axisGroup = setupSingleton.scene.children.find(
|
const axisGroup = sceneInfra.scene.children.find(
|
||||||
({ userData }) => userData?.type === AXIS_GROUP
|
({ userData }) => userData?.type === AXIS_GROUP
|
||||||
)
|
)
|
||||||
const axisMap: { [key: string]: Axis } = {
|
const axisMap: { [key: string]: Axis } = {
|
||||||
@ -998,247 +998,7 @@ class SetupSingleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setupSingleton = new SetupSingleton()
|
export const sceneInfra = new SceneInfra()
|
||||||
|
|
||||||
function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } {
|
|
||||||
const [isCamMoving, setIsCamMoving] = useState(false)
|
|
||||||
const [isTween, setIsTween] = useState(false)
|
|
||||||
|
|
||||||
const { state } = useModelingContext()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setupSingleton.setIsCamMovingCallback((isMoving, isTween) => {
|
|
||||||
setIsCamMoving(isMoving)
|
|
||||||
setIsTween(isTween)
|
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
if (DEBUG_SHOW_BOTH_SCENES || !isCamMoving)
|
|
||||||
return { hideClient: false, hideServer: false }
|
|
||||||
let hideServer = state.matches('Sketch') || state.matches('Sketch no face')
|
|
||||||
if (isTween) {
|
|
||||||
hideServer = false
|
|
||||||
}
|
|
||||||
|
|
||||||
return { hideClient: !hideServer, hideServer }
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ClientSideScene = ({
|
|
||||||
cameraControls,
|
|
||||||
}: {
|
|
||||||
cameraControls: ReturnType<
|
|
||||||
typeof useGlobalStateContext
|
|
||||||
>['settings']['context']['cameraControls']
|
|
||||||
}) => {
|
|
||||||
const canvasRef = useRef<HTMLDivElement>(null)
|
|
||||||
const { state, send } = useModelingContext()
|
|
||||||
const { hideClient, hideServer } = useShouldHideScene()
|
|
||||||
const { setHighlightRange } = useStore((s) => ({
|
|
||||||
setHighlightRange: s.setHighlightRange,
|
|
||||||
highlightRange: s.highlightRange,
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Listen for changes to the camera controls setting
|
|
||||||
// and update the client-side scene's controls accordingly.
|
|
||||||
useEffect(() => {
|
|
||||||
setupSingleton.setInteractionGuards(cameraMouseDragGuards[cameraControls])
|
|
||||||
}, [cameraControls])
|
|
||||||
useEffect(() => {
|
|
||||||
setupSingleton.updateOtherSelectionColors(
|
|
||||||
state?.context?.selectionRanges?.otherSelections || []
|
|
||||||
)
|
|
||||||
}, [state?.context?.selectionRanges?.otherSelections])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!canvasRef.current) return
|
|
||||||
const canvas = canvasRef.current
|
|
||||||
canvas.appendChild(setupSingleton.renderer.domElement)
|
|
||||||
setupSingleton.animate()
|
|
||||||
setupSingleton.setHighlightCallback(setHighlightRange)
|
|
||||||
canvas.addEventListener('mousemove', setupSingleton.onMouseMove, false)
|
|
||||||
canvas.addEventListener('mousedown', setupSingleton.onMouseDown, false)
|
|
||||||
canvas.addEventListener('mouseup', setupSingleton.onMouseUp, false)
|
|
||||||
setupSingleton.setSend(send)
|
|
||||||
return () => {
|
|
||||||
canvas?.removeEventListener('mousemove', setupSingleton.onMouseMove)
|
|
||||||
canvas?.removeEventListener('mousedown', setupSingleton.onMouseDown)
|
|
||||||
canvas?.removeEventListener('mouseup', setupSingleton.onMouseUp)
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
ref={canvasRef}
|
|
||||||
className={`absolute inset-0 h-full w-full transition-all duration-300 ${
|
|
||||||
hideClient ? 'opacity-0' : 'opacity-100'
|
|
||||||
} ${hideServer ? 'bg-black' : ''} ${
|
|
||||||
!hideClient && !hideServer && state.matches('Sketch')
|
|
||||||
? 'bg-black/80'
|
|
||||||
: ''
|
|
||||||
}`}
|
|
||||||
></div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const throttled = throttle((a: ReactCameraProperties) => {
|
|
||||||
if (a.type === 'perspective' && a.fov) {
|
|
||||||
setupSingleton.dollyZoom(a.fov)
|
|
||||||
}
|
|
||||||
}, 1000 / 15)
|
|
||||||
|
|
||||||
export const CamDebugSettings = () => {
|
|
||||||
const [camSettings, setCamSettings] = useState<ReactCameraProperties>({
|
|
||||||
type: 'perspective',
|
|
||||||
fov: 12,
|
|
||||||
position: [0, 0, 0],
|
|
||||||
quaternion: [0, 0, 0, 1],
|
|
||||||
})
|
|
||||||
const [fov, setFov] = useState(12)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setupSingleton.setReactCameraPropertiesCallback(setCamSettings)
|
|
||||||
}, [setupSingleton])
|
|
||||||
useEffect(() => {
|
|
||||||
if (camSettings.type === 'perspective' && camSettings.fov) {
|
|
||||||
setFov(camSettings.fov)
|
|
||||||
}
|
|
||||||
}, [(camSettings as any)?.fov])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h3>cam settings</h3>
|
|
||||||
perspective cam
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={camSettings.type === 'perspective'}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (camSettings.type === 'perspective') {
|
|
||||||
setupSingleton.useOrthographicCamera()
|
|
||||||
} else {
|
|
||||||
setupSingleton.usePerspectiveCamera()
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{camSettings.type === 'perspective' && (
|
|
||||||
<input
|
|
||||||
type="range"
|
|
||||||
min="4"
|
|
||||||
max="90"
|
|
||||||
step={0.5}
|
|
||||||
value={fov}
|
|
||||||
onChange={(e) => {
|
|
||||||
setFov(parseFloat(e.target.value))
|
|
||||||
|
|
||||||
throttled({
|
|
||||||
...camSettings,
|
|
||||||
fov: parseFloat(e.target.value),
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
className="w-full cursor-pointer pointer-events-auto"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{camSettings.type === 'perspective' && (
|
|
||||||
<div>
|
|
||||||
<span>fov</span>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
value={camSettings.fov}
|
|
||||||
className="text-black w-16"
|
|
||||||
onChange={(e) => {
|
|
||||||
setupSingleton.setCam({
|
|
||||||
...camSettings,
|
|
||||||
fov: parseFloat(e.target.value),
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{camSettings.type === 'orthographic' && (
|
|
||||||
<>
|
|
||||||
<div>
|
|
||||||
<span>fov</span>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
value={camSettings.zoom}
|
|
||||||
className="text-black w-16"
|
|
||||||
onChange={(e) => {
|
|
||||||
setupSingleton.setCam({
|
|
||||||
...camSettings,
|
|
||||||
zoom: parseFloat(e.target.value),
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<div>
|
|
||||||
Position
|
|
||||||
<ul className="flex">
|
|
||||||
<li>
|
|
||||||
<span className="pl-2 pr-1">x:</span>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
step={5}
|
|
||||||
data-testid="cam-x-position"
|
|
||||||
value={camSettings.position[0]}
|
|
||||||
className="text-black w-16"
|
|
||||||
onChange={(e) => {
|
|
||||||
setupSingleton.setCam({
|
|
||||||
...camSettings,
|
|
||||||
position: [
|
|
||||||
parseFloat(e.target.value),
|
|
||||||
camSettings.position[1],
|
|
||||||
camSettings.position[2],
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<span className="pl-2 pr-1">y:</span>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
step={5}
|
|
||||||
data-testid="cam-y-position"
|
|
||||||
value={camSettings.position[1]}
|
|
||||||
className="text-black w-16"
|
|
||||||
onChange={(e) => {
|
|
||||||
setupSingleton.setCam({
|
|
||||||
...camSettings,
|
|
||||||
position: [
|
|
||||||
camSettings.position[0],
|
|
||||||
parseFloat(e.target.value),
|
|
||||||
camSettings.position[2],
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<span className="pl-2 pr-1">z:</span>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
step={5}
|
|
||||||
data-testid="cam-z-position"
|
|
||||||
value={camSettings.position[2]}
|
|
||||||
className="text-black w-16"
|
|
||||||
onChange={(e) => {
|
|
||||||
setupSingleton.setCam({
|
|
||||||
...camSettings,
|
|
||||||
position: [
|
|
||||||
camSettings.position[0],
|
|
||||||
camSettings.position[1],
|
|
||||||
parseFloat(e.target.value),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertThreeCamValuesToEngineCam({
|
function convertThreeCamValuesToEngineCam({
|
||||||
position,
|
position,
|
@ -25,9 +25,9 @@ import {
|
|||||||
TANGENTIAL_ARC_TO_SEGMENT,
|
TANGENTIAL_ARC_TO_SEGMENT,
|
||||||
TANGENTIAL_ARC_TO_SEGMENT_BODY,
|
TANGENTIAL_ARC_TO_SEGMENT_BODY,
|
||||||
TANGENTIAL_ARC_TO__SEGMENT_DASH,
|
TANGENTIAL_ARC_TO__SEGMENT_DASH,
|
||||||
} from './clientSideScene'
|
} from './sceneEntities'
|
||||||
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
|
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
|
||||||
import { ARROWHEAD } from './setup'
|
import { ARROWHEAD } from './sceneInfra'
|
||||||
|
|
||||||
export function straightSegment({
|
export function straightSegment({
|
||||||
from,
|
from,
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { setupSingleton } from '../clientSideScene/setup'
|
import { sceneInfra } from '../clientSideScene/sceneInfra'
|
||||||
import { engineCommandManager } from 'lang/std/engineConnection'
|
import { engineCommandManager } from 'lang/std/engineConnection'
|
||||||
import { throttle, isReducedMotion } from 'lib/utils'
|
import { throttle, isReducedMotion } from 'lib/utils'
|
||||||
|
|
||||||
const updateDollyZoom = throttle(
|
const updateDollyZoom = throttle(
|
||||||
(newFov: number) => setupSingleton.dollyZoom(newFov),
|
(newFov: number) => sceneInfra.dollyZoom(newFov),
|
||||||
1000 / 15
|
1000 / 15
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -15,19 +15,19 @@ export const CamToggle = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
engineCommandManager.waitForReady.then(async () => {
|
engineCommandManager.waitForReady.then(async () => {
|
||||||
setupSingleton.dollyZoom(fov)
|
sceneInfra.dollyZoom(fov)
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const toggleCamera = () => {
|
const toggleCamera = () => {
|
||||||
if (isPerspective) {
|
if (isPerspective) {
|
||||||
isReducedMotion()
|
isReducedMotion()
|
||||||
? setupSingleton.useOrthographicCamera()
|
? sceneInfra.useOrthographicCamera()
|
||||||
: setupSingleton.animateToOrthographic()
|
: sceneInfra.animateToOrthographic()
|
||||||
} else {
|
} else {
|
||||||
isReducedMotion()
|
isReducedMotion()
|
||||||
? setupSingleton.usePerspectiveCamera()
|
? sceneInfra.usePerspectiveCamera()
|
||||||
: setupSingleton.animateToPerspective()
|
: sceneInfra.animateToPerspective()
|
||||||
}
|
}
|
||||||
setIsPerspective(!isPerspective)
|
setIsPerspective(!isPerspective)
|
||||||
}
|
}
|
||||||
@ -60,9 +60,9 @@ export const CamToggle = () => {
|
|||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (enableRotate) {
|
if (enableRotate) {
|
||||||
setupSingleton.controls.enableRotate = false
|
sceneInfra.controls.enableRotate = false
|
||||||
} else {
|
} else {
|
||||||
setupSingleton.controls.enableRotate = true
|
sceneInfra.controls.enableRotate = true
|
||||||
}
|
}
|
||||||
setEnableRotate(!enableRotate)
|
setEnableRotate(!enableRotate)
|
||||||
}}
|
}}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
|
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
|
||||||
import { AstExplorer } from './AstExplorer'
|
import { AstExplorer } from './AstExplorer'
|
||||||
import { EngineCommands } from './EngineCommands'
|
import { EngineCommands } from './EngineCommands'
|
||||||
import { CamDebugSettings } from 'clientSideScene/setup'
|
import { CamDebugSettings } from 'clientSideScene/ClientSideSceneComp'
|
||||||
|
|
||||||
export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => {
|
export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => {
|
||||||
return (
|
return (
|
||||||
|
@ -33,8 +33,8 @@ import { applyConstraintIntersect } from './Toolbar/Intersect'
|
|||||||
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
|
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
|
||||||
import useStateMachineCommands from 'hooks/useStateMachineCommands'
|
import useStateMachineCommands from 'hooks/useStateMachineCommands'
|
||||||
import { modelingMachineConfig } from 'lib/commandBarConfigs/modelingCommandConfig'
|
import { modelingMachineConfig } from 'lib/commandBarConfigs/modelingCommandConfig'
|
||||||
import { setupSingleton } from 'clientSideScene/setup'
|
import { sceneInfra } from 'clientSideScene/sceneInfra'
|
||||||
import { getSketchQuaternion } from 'clientSideScene/clientSideScene'
|
import { getSketchQuaternion } from 'clientSideScene/sceneEntities'
|
||||||
import { startSketchOnDefault } from 'lang/modifyAst'
|
import { startSketchOnDefault } from 'lang/modifyAst'
|
||||||
import { Program } from 'lang/wasm'
|
import { Program } from 'lang/wasm'
|
||||||
|
|
||||||
@ -197,7 +197,7 @@ export const ModelingMachineProvider = ({
|
|||||||
// remove body item at varDecIndex
|
// remove body item at varDecIndex
|
||||||
newAst.body = newAst.body.filter((_, i) => i !== varDecIndex)
|
newAst.body = newAst.body.filter((_, i) => i !== varDecIndex)
|
||||||
await kclManager.executeAstMock(newAst, { updates: 'code' })
|
await kclManager.executeAstMock(newAst, { updates: 'code' })
|
||||||
setupSingleton.setCallbacks({
|
sceneInfra.setCallbacks({
|
||||||
onClick: () => {},
|
onClick: () => {},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -208,7 +208,7 @@ export const ModelingMachineProvider = ({
|
|||||||
)
|
)
|
||||||
await kclManager.updateAst(modifiedAst, false)
|
await kclManager.updateAst(modifiedAst, false)
|
||||||
const quaternion = getSketchQuaternion(pathToNode, normal)
|
const quaternion = getSketchQuaternion(pathToNode, normal)
|
||||||
await setupSingleton.tweenCameraToQuaternion(quaternion)
|
await sceneInfra.tweenCameraToQuaternion(quaternion)
|
||||||
return {
|
return {
|
||||||
sketchPathToNode: pathToNode,
|
sketchPathToNode: pathToNode,
|
||||||
sketchNormalBackUp: normal,
|
sketchNormalBackUp: normal,
|
||||||
@ -222,7 +222,7 @@ export const ModelingMachineProvider = ({
|
|||||||
sketchPathToNode || [],
|
sketchPathToNode || [],
|
||||||
sketchNormalBackUp
|
sketchNormalBackUp
|
||||||
)
|
)
|
||||||
await setupSingleton.tweenCameraToQuaternion(quaternion)
|
await sceneInfra.tweenCameraToQuaternion(quaternion)
|
||||||
},
|
},
|
||||||
'Get horizontal info': async ({
|
'Get horizontal info': async ({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
|
@ -15,7 +15,7 @@ import { Models } from '@kittycad/lib'
|
|||||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
import { useKclContext } from 'lang/KclSingleton'
|
import { useKclContext } from 'lang/KclSingleton'
|
||||||
import { ClientSideScene } from 'clientSideScene/setup'
|
import { ClientSideScene } from 'clientSideScene/ClientSideSceneComp'
|
||||||
|
|
||||||
export const Stream = ({ className = '' }: { className?: string }) => {
|
export const Stream = ({ className = '' }: { className?: string }) => {
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
|
@ -26,7 +26,7 @@ import interact from '@replit/codemirror-interact'
|
|||||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||||
import { kclManager, useKclContext } from 'lang/KclSingleton'
|
import { kclManager, useKclContext } from 'lang/KclSingleton'
|
||||||
import { ModelingMachineEvent } from 'machines/modelingMachine'
|
import { ModelingMachineEvent } from 'machines/modelingMachine'
|
||||||
import { setupSingleton } from 'clientSideScene/setup'
|
import { sceneInfra } from 'clientSideScene/sceneInfra'
|
||||||
|
|
||||||
export const editorShortcutMeta = {
|
export const editorShortcutMeta = {
|
||||||
formatCode: {
|
formatCode: {
|
||||||
@ -119,7 +119,7 @@ export const TextEditor = ({
|
|||||||
if (!editorView) {
|
if (!editorView) {
|
||||||
setEditorView(viewUpdate.view)
|
setEditorView(viewUpdate.view)
|
||||||
}
|
}
|
||||||
if (setupSingleton.selected) return // mid drag
|
if (sceneInfra.selected) return // mid drag
|
||||||
const ignoreEvents: ModelingMachineEvent['type'][] = [
|
const ignoreEvents: ModelingMachineEvent['type'][] = [
|
||||||
'Equip Line tool',
|
'Equip Line tool',
|
||||||
'Equip tangential arc to',
|
'Equip tangential arc to',
|
||||||
|
@ -99,7 +99,7 @@ class KclManager {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
localStorage?.setItem(PERSIST_CODE_TOKEN, code)
|
safteLSSetItem(PERSIST_CODE_TOKEN, code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,16 +154,16 @@ class KclManager {
|
|||||||
this.code = ''
|
this.code = ''
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const storedCode = localStorage.getItem(PERSIST_CODE_TOKEN)
|
const storedCode = safeLSGetItem(PERSIST_CODE_TOKEN) || ''
|
||||||
// TODO #819 remove zustand persistence logic in a few months
|
// TODO #819 remove zustand persistence logic in a few months
|
||||||
// short term migration, shouldn't make a difference for tauri app users
|
// short term migration, shouldn't make a difference for tauri app users
|
||||||
// anyway since that's filesystem based.
|
// anyway since that's filesystem based.
|
||||||
const zustandStore = JSON.parse(localStorage.getItem('store') || '{}')
|
const zustandStore = JSON.parse(safeLSGetItem('store') || '{}')
|
||||||
if (storedCode === null && zustandStore?.state?.code) {
|
if (storedCode === null && zustandStore?.state?.code) {
|
||||||
this.code = zustandStore.state.code
|
this.code = zustandStore.state.code
|
||||||
localStorage.setItem(PERSIST_CODE_TOKEN, this._code)
|
safteLSSetItem(PERSIST_CODE_TOKEN, this._code)
|
||||||
zustandStore.state.code = ''
|
zustandStore.state.code = ''
|
||||||
localStorage.setItem('store', JSON.stringify(zustandStore))
|
safteLSSetItem('store', JSON.stringify(zustandStore))
|
||||||
} else if (storedCode === null) {
|
} else if (storedCode === null) {
|
||||||
this.code = bracket
|
this.code = bracket
|
||||||
} else {
|
} else {
|
||||||
@ -457,3 +457,13 @@ export function KclContextProvider({
|
|||||||
</KclContext.Provider>
|
</KclContext.Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function safeLSGetItem(key: string) {
|
||||||
|
if (typeof window === 'undefined') return null
|
||||||
|
return localStorage?.getItem(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
function safteLSSetItem(key: string, value: string) {
|
||||||
|
if (typeof window === 'undefined') return
|
||||||
|
localStorage?.setItem(key, value)
|
||||||
|
}
|
||||||
|
@ -30,7 +30,7 @@ import {
|
|||||||
createFirstArg,
|
createFirstArg,
|
||||||
} from './std/sketch'
|
} from './std/sketch'
|
||||||
import { isLiteralArrayOrStatic } from './std/sketchcombos'
|
import { isLiteralArrayOrStatic } from './std/sketchcombos'
|
||||||
import { DefaultPlaneStr } from 'clientSideScene/clientSideScene'
|
import { DefaultPlaneStr } from 'clientSideScene/sceneEntities'
|
||||||
import { roundOff } from 'lib/utils'
|
import { roundOff } from 'lib/utils'
|
||||||
|
|
||||||
export function startSketchOnDefault(
|
export function startSketchOnDefault(
|
||||||
|
@ -5,7 +5,7 @@ import { exportSave } from 'lib/exportSave'
|
|||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import * as Sentry from '@sentry/react'
|
import * as Sentry from '@sentry/react'
|
||||||
import { getNodePathFromSourceRange } from 'lang/queryAst'
|
import { getNodePathFromSourceRange } from 'lang/queryAst'
|
||||||
import { setupSingleton } from 'clientSideScene/setup'
|
import { sceneInfra } from 'clientSideScene/sceneInfra'
|
||||||
|
|
||||||
let lastMessage = ''
|
let lastMessage = ''
|
||||||
|
|
||||||
@ -1012,7 +1012,7 @@ export class EngineCommandManager {
|
|||||||
gizmo_mode: true,
|
gizmo_mode: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
setupSingleton.onStreamStart()
|
sceneInfra.onStreamStart()
|
||||||
|
|
||||||
executeCode(undefined, true)
|
executeCode(undefined, true)
|
||||||
},
|
},
|
||||||
|
@ -14,11 +14,11 @@ import { CommandArgument } from './commandTypes'
|
|||||||
import {
|
import {
|
||||||
STRAIGHT_SEGMENT,
|
STRAIGHT_SEGMENT,
|
||||||
TANGENTIAL_ARC_TO_SEGMENT,
|
TANGENTIAL_ARC_TO_SEGMENT,
|
||||||
clientSideScene,
|
sceneEntitiesManager,
|
||||||
getParentGroup,
|
getParentGroup,
|
||||||
} from 'clientSideScene/clientSideScene'
|
} from 'clientSideScene/sceneEntities'
|
||||||
import { Mesh } from 'three'
|
import { Mesh } from 'three'
|
||||||
import { AXIS_GROUP, X_AXIS } from 'clientSideScene/setup'
|
import { AXIS_GROUP, X_AXIS } from 'clientSideScene/sceneInfra'
|
||||||
|
|
||||||
export const X_AXIS_UUID = 'ad792545-7fd3-482a-a602-a93924e3055b'
|
export const X_AXIS_UUID = 'ad792545-7fd3-482a-a602-a93924e3055b'
|
||||||
export const Y_AXIS_UUID = '680fd157-266f-4b8a-984f-cdf46b8bdf01'
|
export const Y_AXIS_UUID = '680fd157-266f-4b8a-984f-cdf46b8bdf01'
|
||||||
@ -401,7 +401,7 @@ function updateSceneObjectColors(codeBasedSelections: Selection[]) {
|
|||||||
console.error('error parsing code in processCodeMirrorRanges', e)
|
console.error('error parsing code in processCodeMirrorRanges', e)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Object.values(clientSideScene.activeSegments).forEach((segmentGroup) => {
|
Object.values(sceneEntitiesManager.activeSegments).forEach((segmentGroup) => {
|
||||||
if (
|
if (
|
||||||
![STRAIGHT_SEGMENT, TANGENTIAL_ARC_TO_SEGMENT].includes(
|
![STRAIGHT_SEGMENT, TANGENTIAL_ARC_TO_SEGMENT].includes(
|
||||||
segmentGroup?.userData?.type
|
segmentGroup?.userData?.type
|
||||||
|
@ -44,11 +44,11 @@ import { Models } from '@kittycad/lib/dist/types/src'
|
|||||||
import { ModelingCommandSchema } from 'lib/commandBarConfigs/modelingCommandConfig'
|
import { ModelingCommandSchema } from 'lib/commandBarConfigs/modelingCommandConfig'
|
||||||
import {
|
import {
|
||||||
DefaultPlaneStr,
|
DefaultPlaneStr,
|
||||||
clientSideScene,
|
sceneEntitiesManager,
|
||||||
quaternionFromSketchGroup,
|
quaternionFromSketchGroup,
|
||||||
sketchGroupFromPathToNode,
|
sketchGroupFromPathToNode,
|
||||||
} from 'clientSideScene/clientSideScene'
|
} from 'clientSideScene/sceneEntities'
|
||||||
import { setupSingleton } from 'clientSideScene/setup'
|
import { sceneInfra } from 'clientSideScene/sceneInfra'
|
||||||
|
|
||||||
export const MODELING_PERSIST_KEY = 'MODELING_PERSIST_KEY'
|
export const MODELING_PERSIST_KEY = 'MODELING_PERSIST_KEY'
|
||||||
|
|
||||||
@ -605,7 +605,7 @@ export const modelingMachine = createMachine(
|
|||||||
if (!sketchPathToNode) return {}
|
if (!sketchPathToNode) return {}
|
||||||
return getSketchMetadataFromPathToNode(sketchPathToNode)
|
return getSketchMetadataFromPathToNode(sketchPathToNode)
|
||||||
}),
|
}),
|
||||||
'hide default planes': () => setupSingleton.removeDefaultPlanes(),
|
'hide default planes': () => sceneInfra.removeDefaultPlanes(),
|
||||||
'reset sketch metadata': assign({
|
'reset sketch metadata': assign({
|
||||||
sketchPathToNode: null,
|
sketchPathToNode: null,
|
||||||
sketchEnginePathId: '',
|
sketchEnginePathId: '',
|
||||||
@ -632,7 +632,7 @@ export const modelingMachine = createMachine(
|
|||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
kclManager.programMemory
|
kclManager.programMemory
|
||||||
)
|
)
|
||||||
clientSideScene.updateAstAndRejigSketch(
|
sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
sketchPathToNode || [],
|
sketchPathToNode || [],
|
||||||
modifiedAst
|
modifiedAst
|
||||||
)
|
)
|
||||||
@ -644,7 +644,7 @@ export const modelingMachine = createMachine(
|
|||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
kclManager.programMemory
|
kclManager.programMemory
|
||||||
)
|
)
|
||||||
clientSideScene.updateAstAndRejigSketch(
|
sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
sketchPathToNode || [],
|
sketchPathToNode || [],
|
||||||
modifiedAst
|
modifiedAst
|
||||||
)
|
)
|
||||||
@ -657,7 +657,7 @@ export const modelingMachine = createMachine(
|
|||||||
selectionRanges,
|
selectionRanges,
|
||||||
constraint: 'setVertDistance',
|
constraint: 'setVertDistance',
|
||||||
})
|
})
|
||||||
clientSideScene.updateAstAndRejigSketch(
|
sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
sketchPathToNode || [],
|
sketchPathToNode || [],
|
||||||
modifiedAst
|
modifiedAst
|
||||||
)
|
)
|
||||||
@ -667,7 +667,7 @@ export const modelingMachine = createMachine(
|
|||||||
selectionRanges,
|
selectionRanges,
|
||||||
constraint: 'setHorzDistance',
|
constraint: 'setHorzDistance',
|
||||||
})
|
})
|
||||||
clientSideScene.updateAstAndRejigSketch(
|
sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
sketchPathToNode || [],
|
sketchPathToNode || [],
|
||||||
modifiedAst
|
modifiedAst
|
||||||
)
|
)
|
||||||
@ -677,7 +677,7 @@ export const modelingMachine = createMachine(
|
|||||||
selectionRanges,
|
selectionRanges,
|
||||||
constraint: 'snapToXAxis',
|
constraint: 'snapToXAxis',
|
||||||
})
|
})
|
||||||
clientSideScene.updateAstAndRejigSketch(
|
sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
sketchPathToNode || [],
|
sketchPathToNode || [],
|
||||||
modifiedAst
|
modifiedAst
|
||||||
)
|
)
|
||||||
@ -687,7 +687,7 @@ export const modelingMachine = createMachine(
|
|||||||
selectionRanges,
|
selectionRanges,
|
||||||
constraint: 'snapToYAxis',
|
constraint: 'snapToYAxis',
|
||||||
})
|
})
|
||||||
clientSideScene.updateAstAndRejigSketch(
|
sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
sketchPathToNode || [],
|
sketchPathToNode || [],
|
||||||
modifiedAst
|
modifiedAst
|
||||||
)
|
)
|
||||||
@ -696,7 +696,7 @@ export const modelingMachine = createMachine(
|
|||||||
const { modifiedAst } = applyConstraintEqualLength({
|
const { modifiedAst } = applyConstraintEqualLength({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
clientSideScene.updateAstAndRejigSketch(
|
sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
sketchPathToNode || [],
|
sketchPathToNode || [],
|
||||||
modifiedAst
|
modifiedAst
|
||||||
)
|
)
|
||||||
@ -705,7 +705,7 @@ export const modelingMachine = createMachine(
|
|||||||
const { modifiedAst } = applyConstraintEqualAngle({
|
const { modifiedAst } = applyConstraintEqualAngle({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
clientSideScene.updateAstAndRejigSketch(
|
sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
sketchPathToNode || [],
|
sketchPathToNode || [],
|
||||||
modifiedAst
|
modifiedAst
|
||||||
)
|
)
|
||||||
@ -717,7 +717,7 @@ export const modelingMachine = createMachine(
|
|||||||
const { modifiedAst } = applyRemoveConstrainingValues({
|
const { modifiedAst } = applyRemoveConstrainingValues({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
clientSideScene.updateAstAndRejigSketch(
|
sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
sketchPathToNode || [],
|
sketchPathToNode || [],
|
||||||
modifiedAst
|
modifiedAst
|
||||||
)
|
)
|
||||||
@ -742,59 +742,61 @@ export const modelingMachine = createMachine(
|
|||||||
},
|
},
|
||||||
'conditionally equip line tool': (_, { type }) => {
|
'conditionally equip line tool': (_, { type }) => {
|
||||||
if (type === 'done.invoke.animate-to-face') {
|
if (type === 'done.invoke.animate-to-face') {
|
||||||
setupSingleton.modelingSend('Equip Line tool')
|
sceneInfra.modelingSend('Equip Line tool')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'setup client side sketch segments': ({ sketchPathToNode }, { type }) => {
|
'setup client side sketch segments': ({ sketchPathToNode }, { type }) => {
|
||||||
if (Object.keys(clientSideScene.activeSegments).length > 0) {
|
if (Object.keys(sceneEntitiesManager.activeSegments).length > 0) {
|
||||||
clientSideScene.tearDownSketch({ removeAxis: false }).then(() => {
|
sceneEntitiesManager
|
||||||
clientSideScene.setupSketch({
|
.tearDownSketch({ removeAxis: false })
|
||||||
sketchPathToNode: sketchPathToNode || [],
|
.then(() => {
|
||||||
|
sceneEntitiesManager.setupSketch({
|
||||||
|
sketchPathToNode: sketchPathToNode || [],
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
clientSideScene.setupSketch({
|
sceneEntitiesManager.setupSketch({
|
||||||
sketchPathToNode: sketchPathToNode || [],
|
sketchPathToNode: sketchPathToNode || [],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'animate after sketch': () => {
|
'animate after sketch': () => {
|
||||||
clientSideScene.animateAfterSketch()
|
sceneEntitiesManager.animateAfterSketch()
|
||||||
},
|
},
|
||||||
'tear down client sketch': () => {
|
'tear down client sketch': () => {
|
||||||
if (clientSideScene.activeSegments) {
|
if (sceneEntitiesManager.activeSegments) {
|
||||||
clientSideScene.tearDownSketch({ removeAxis: false })
|
sceneEntitiesManager.tearDownSketch({ removeAxis: false })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'remove sketch grid': () => clientSideScene.removeSketchGrid(),
|
'remove sketch grid': () => sceneEntitiesManager.removeSketchGrid(),
|
||||||
'set up draft line': ({ sketchPathToNode }) => {
|
'set up draft line': ({ sketchPathToNode }) => {
|
||||||
clientSideScene.setUpDraftLine(sketchPathToNode || [])
|
sceneEntitiesManager.setUpDraftLine(sketchPathToNode || [])
|
||||||
},
|
},
|
||||||
'set up draft arc': ({ sketchPathToNode }) => {
|
'set up draft arc': ({ sketchPathToNode }) => {
|
||||||
clientSideScene.setUpDraftArc(sketchPathToNode || [])
|
sceneEntitiesManager.setUpDraftArc(sketchPathToNode || [])
|
||||||
},
|
},
|
||||||
'set up draft line without teardown': ({ sketchPathToNode }) =>
|
'set up draft line without teardown': ({ sketchPathToNode }) =>
|
||||||
clientSideScene.setupSketch({
|
sceneEntitiesManager.setupSketch({
|
||||||
sketchPathToNode: sketchPathToNode || [],
|
sketchPathToNode: sketchPathToNode || [],
|
||||||
draftSegment: 'line',
|
draftSegment: 'line',
|
||||||
}),
|
}),
|
||||||
'show default planes': () => {
|
'show default planes': () => {
|
||||||
setupSingleton.showDefaultPlanes()
|
sceneInfra.showDefaultPlanes()
|
||||||
clientSideScene.setupDefaultPlaneHover()
|
sceneEntitiesManager.setupDefaultPlaneHover()
|
||||||
},
|
},
|
||||||
'setup noPoints onClick listener': ({ sketchPathToNode }) => {
|
'setup noPoints onClick listener': ({ sketchPathToNode }) => {
|
||||||
clientSideScene.createIntersectionPlane()
|
sceneEntitiesManager.createIntersectionPlane()
|
||||||
const sketchGroup = sketchGroupFromPathToNode({
|
const sketchGroup = sketchGroupFromPathToNode({
|
||||||
pathToNode: sketchPathToNode || [],
|
pathToNode: sketchPathToNode || [],
|
||||||
ast: kclManager.ast,
|
ast: kclManager.ast,
|
||||||
programMemory: kclManager.programMemory,
|
programMemory: kclManager.programMemory,
|
||||||
})
|
})
|
||||||
const quaternion = quaternionFromSketchGroup(sketchGroup)
|
const quaternion = quaternionFromSketchGroup(sketchGroup)
|
||||||
clientSideScene.intersectionPlane &&
|
sceneEntitiesManager.intersectionPlane &&
|
||||||
clientSideScene.intersectionPlane.setRotationFromQuaternion(
|
sceneEntitiesManager.intersectionPlane.setRotationFromQuaternion(
|
||||||
quaternion
|
quaternion
|
||||||
)
|
)
|
||||||
setupSingleton.setCallbacks({
|
sceneInfra.setCallbacks({
|
||||||
onClick: async (args) => {
|
onClick: async (args) => {
|
||||||
if (!args) return
|
if (!args) return
|
||||||
const { intersection2d } = args
|
const { intersection2d } = args
|
||||||
@ -805,13 +807,13 @@ export const modelingMachine = createMachine(
|
|||||||
[intersection2d.x, intersection2d.y]
|
[intersection2d.x, intersection2d.y]
|
||||||
)
|
)
|
||||||
await kclManager.updateAst(modifiedAst, false)
|
await kclManager.updateAst(modifiedAst, false)
|
||||||
clientSideScene.removeIntersectionPlane()
|
sceneEntitiesManager.removeIntersectionPlane()
|
||||||
setupSingleton.modelingSend('Add start point')
|
sceneInfra.modelingSend('Add start point')
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
'add axis n grid': ({ sketchPathToNode }) =>
|
'add axis n grid': ({ sketchPathToNode }) =>
|
||||||
clientSideScene.createSketchAxis(sketchPathToNode || []),
|
sceneEntitiesManager.createSketchAxis(sketchPathToNode || []),
|
||||||
},
|
},
|
||||||
// end actions
|
// end actions
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user