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,
 | 
			
		||||
  isQuaternionVertical,
 | 
			
		||||
  RAYCASTABLE_PLANE,
 | 
			
		||||
  setupSingleton,
 | 
			
		||||
  sceneInfra,
 | 
			
		||||
  SKETCH_GROUP_SEGMENTS,
 | 
			
		||||
  SKETCH_LAYER,
 | 
			
		||||
  X_AXIS,
 | 
			
		||||
  XZ_PLANE,
 | 
			
		||||
  Y_AXIS,
 | 
			
		||||
  YZ_PLANE,
 | 
			
		||||
} from './setup'
 | 
			
		||||
} from './sceneInfra'
 | 
			
		||||
import {
 | 
			
		||||
  CallExpression,
 | 
			
		||||
  getTangentialArcToInfo,
 | 
			
		||||
@ -85,7 +85,10 @@ export const TANGENTIAL_ARC_TO_SEGMENT_BODY = 'tangential-arc-to-segment-body'
 | 
			
		||||
export const TANGENTIAL_ARC_TO__SEGMENT_DASH =
 | 
			
		||||
  '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
 | 
			
		||||
  sceneProgramMemory: ProgramMemory = { root: {}, return: null }
 | 
			
		||||
  activeSegments: { [key: string]: Group } = {}
 | 
			
		||||
@ -93,18 +96,18 @@ class ClientSideScene {
 | 
			
		||||
  axisGroup: Group | null = null
 | 
			
		||||
  currentSketchQuaternion: Quaternion | null = null
 | 
			
		||||
  constructor() {
 | 
			
		||||
    this.scene = setupSingleton?.scene
 | 
			
		||||
    setupSingleton?.setOnCamChange(this.onCamChange)
 | 
			
		||||
    this.scene = sceneInfra?.scene
 | 
			
		||||
    sceneInfra?.setOnCamChange(this.onCamChange)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onCamChange = () => {
 | 
			
		||||
    const orthoFactor = orthoScale(setupSingleton.camera)
 | 
			
		||||
    const orthoFactor = orthoScale(sceneInfra.camera)
 | 
			
		||||
 | 
			
		||||
    Object.values(this.activeSegments).forEach((segment) => {
 | 
			
		||||
      const factor =
 | 
			
		||||
        setupSingleton.camera instanceof OrthographicCamera
 | 
			
		||||
        sceneInfra.camera instanceof OrthographicCamera
 | 
			
		||||
          ? orthoFactor
 | 
			
		||||
          : perspScale(setupSingleton.camera, segment)
 | 
			
		||||
          : perspScale(sceneInfra.camera, segment)
 | 
			
		||||
      if (
 | 
			
		||||
        segment.userData.from &&
 | 
			
		||||
        segment.userData.to &&
 | 
			
		||||
@ -135,9 +138,9 @@ class ClientSideScene {
 | 
			
		||||
    })
 | 
			
		||||
    if (this.axisGroup) {
 | 
			
		||||
      const factor =
 | 
			
		||||
        setupSingleton.camera instanceof OrthographicCamera
 | 
			
		||||
        sceneInfra.camera instanceof OrthographicCamera
 | 
			
		||||
          ? orthoFactor
 | 
			
		||||
          : perspScale(setupSingleton.camera, this.axisGroup)
 | 
			
		||||
          : perspScale(sceneInfra.camera, this.axisGroup)
 | 
			
		||||
      const x = this.axisGroup.getObjectByName(X_AXIS)
 | 
			
		||||
      x?.scale.set(1, factor, 1)
 | 
			
		||||
      const y = this.axisGroup.getObjectByName(Y_AXIS)
 | 
			
		||||
@ -259,11 +262,11 @@ class ClientSideScene {
 | 
			
		||||
      sketchGroup.position[1],
 | 
			
		||||
      sketchGroup.position[2]
 | 
			
		||||
    )
 | 
			
		||||
    const orthoFactor = orthoScale(setupSingleton.camera)
 | 
			
		||||
    const orthoFactor = orthoScale(sceneInfra.camera)
 | 
			
		||||
    const factor =
 | 
			
		||||
      setupSingleton.camera instanceof OrthographicCamera
 | 
			
		||||
      sceneInfra.camera instanceof OrthographicCamera
 | 
			
		||||
        ? orthoFactor
 | 
			
		||||
        : perspScale(setupSingleton.camera, dummy)
 | 
			
		||||
        : perspScale(sceneInfra.camera, dummy)
 | 
			
		||||
    sketchGroup.value.forEach((segment, index) => {
 | 
			
		||||
      let segPathToNode = getNodePathFromSourceRange(
 | 
			
		||||
        draftSegment ? truncatedAst : kclManager.ast,
 | 
			
		||||
@ -310,7 +313,7 @@ class ClientSideScene {
 | 
			
		||||
 | 
			
		||||
    this.scene.add(group)
 | 
			
		||||
    if (!draftSegment) {
 | 
			
		||||
      setupSingleton.setCallbacks({
 | 
			
		||||
      sceneInfra.setCallbacks({
 | 
			
		||||
        onDrag: (args) => {
 | 
			
		||||
          this.onDragSegment({
 | 
			
		||||
            ...args,
 | 
			
		||||
@ -320,7 +323,7 @@ class ClientSideScene {
 | 
			
		||||
        onMove: () => {},
 | 
			
		||||
        onClick: (args) => {
 | 
			
		||||
          if (!args || !args.object) {
 | 
			
		||||
            setupSingleton.modelingSend({
 | 
			
		||||
            sceneInfra.modelingSend({
 | 
			
		||||
              type: 'Set selection',
 | 
			
		||||
              data: {
 | 
			
		||||
                selectionType: 'singleCodeCursor',
 | 
			
		||||
@ -331,7 +334,7 @@ class ClientSideScene {
 | 
			
		||||
          const { object } = args
 | 
			
		||||
          const event = getEventForSegmentSelection(object)
 | 
			
		||||
          if (!event) return
 | 
			
		||||
          setupSingleton.modelingSend(event)
 | 
			
		||||
          sceneInfra.modelingSend(event)
 | 
			
		||||
        },
 | 
			
		||||
        onMouseEnter: ({ object }) => {
 | 
			
		||||
          // TODO change the color of the segment to yellow?
 | 
			
		||||
@ -351,15 +354,15 @@ class ClientSideScene {
 | 
			
		||||
              parent.userData.pathToNode,
 | 
			
		||||
              'CallExpression'
 | 
			
		||||
            ).node
 | 
			
		||||
            setupSingleton.highlightCallback([node.start, node.end])
 | 
			
		||||
            sceneInfra.highlightCallback([node.start, node.end])
 | 
			
		||||
            const yellow = 0xffff00
 | 
			
		||||
            colorSegment(object, yellow)
 | 
			
		||||
            return
 | 
			
		||||
          }
 | 
			
		||||
          setupSingleton.highlightCallback([0, 0])
 | 
			
		||||
          sceneInfra.highlightCallback([0, 0])
 | 
			
		||||
        },
 | 
			
		||||
        onMouseLeave: ({ object }) => {
 | 
			
		||||
          setupSingleton.highlightCallback([0, 0])
 | 
			
		||||
          sceneInfra.highlightCallback([0, 0])
 | 
			
		||||
          const parent = getParentGroup(object)
 | 
			
		||||
          const isSelected = parent?.userData?.isSelected
 | 
			
		||||
          colorSegment(object, isSelected ? 0x0000ff : 0xffffff)
 | 
			
		||||
@ -372,7 +375,7 @@ class ClientSideScene {
 | 
			
		||||
        },
 | 
			
		||||
      })
 | 
			
		||||
    } else {
 | 
			
		||||
      setupSingleton.setCallbacks({
 | 
			
		||||
      sceneInfra.setCallbacks({
 | 
			
		||||
        onDrag: () => {},
 | 
			
		||||
        onClick: async (args) => {
 | 
			
		||||
          if (!args) return
 | 
			
		||||
@ -427,7 +430,7 @@ class ClientSideScene {
 | 
			
		||||
        },
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
    setupSingleton.controls.enableRotate = false
 | 
			
		||||
    sceneInfra.controls.enableRotate = false
 | 
			
		||||
  }
 | 
			
		||||
  updateAstAndRejigSketch = async (
 | 
			
		||||
    sketchPathToNode: PathToNode,
 | 
			
		||||
@ -530,7 +533,7 @@ class ClientSideScene {
 | 
			
		||||
      this.sceneProgramMemory = programMemory
 | 
			
		||||
      const sketchGroup = programMemory.root[variableDeclarationName]
 | 
			
		||||
        .value as Path[]
 | 
			
		||||
      const orthoFactor = orthoScale(setupSingleton.camera)
 | 
			
		||||
      const orthoFactor = orthoScale(sceneInfra.camera)
 | 
			
		||||
      sketchGroup.forEach((segment, index) => {
 | 
			
		||||
        const segPathToNode = getNodePathFromSourceRange(
 | 
			
		||||
          modifiedAst,
 | 
			
		||||
@ -546,9 +549,9 @@ class ClientSideScene {
 | 
			
		||||
        // const prevSegment = sketchGroup.slice(index - 1)[0]
 | 
			
		||||
        const type = group?.userData?.type
 | 
			
		||||
        const factor =
 | 
			
		||||
          setupSingleton.camera instanceof OrthographicCamera
 | 
			
		||||
          sceneInfra.camera instanceof OrthographicCamera
 | 
			
		||||
            ? orthoFactor
 | 
			
		||||
            : perspScale(setupSingleton.camera, group)
 | 
			
		||||
            : perspScale(sceneInfra.camera, group)
 | 
			
		||||
        if (type === TANGENTIAL_ARC_TO_SEGMENT) {
 | 
			
		||||
          this.updateTangentialArcToSegment({
 | 
			
		||||
            prevSegment: sketchGroup[index - 1],
 | 
			
		||||
@ -705,9 +708,9 @@ class ClientSideScene {
 | 
			
		||||
  }
 | 
			
		||||
  async animateAfterSketch() {
 | 
			
		||||
    if (isReducedMotion()) {
 | 
			
		||||
      setupSingleton.usePerspectiveCamera()
 | 
			
		||||
      sceneInfra.usePerspectiveCamera()
 | 
			
		||||
    } else {
 | 
			
		||||
      await setupSingleton.animateToPerspective()
 | 
			
		||||
      await sceneInfra.animateToPerspective()
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  removeSketchGrid() {
 | 
			
		||||
@ -740,7 +743,7 @@ class ClientSideScene {
 | 
			
		||||
        reject()
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    setupSingleton.controls.enableRotate = true
 | 
			
		||||
    sceneInfra.controls.enableRotate = true
 | 
			
		||||
    this.activeSegments = {}
 | 
			
		||||
    // maybe should reset onMove etc handlers
 | 
			
		||||
    if (shouldResolve) resolve(true)
 | 
			
		||||
@ -759,7 +762,7 @@ class ClientSideScene {
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
  setupDefaultPlaneHover() {
 | 
			
		||||
    setupSingleton.setCallbacks({
 | 
			
		||||
    sceneInfra.setCallbacks({
 | 
			
		||||
      onMouseEnter: ({ object }) => {
 | 
			
		||||
        if (object.parent.userData.type !== DEFAULT_PLANES) return
 | 
			
		||||
        const type: DefaultPlane = object.userData.type
 | 
			
		||||
@ -784,7 +787,7 @@ class ClientSideScene {
 | 
			
		||||
          planeString = posNorm ? 'XZ' : '-XZ'
 | 
			
		||||
          normal = posNorm ? [0, 1, 0] : [0, -1, 0]
 | 
			
		||||
        }
 | 
			
		||||
        setupSingleton.modelingSend({
 | 
			
		||||
        sceneInfra.modelingSend({
 | 
			
		||||
          type: 'Select default plane',
 | 
			
		||||
          data: {
 | 
			
		||||
            plane: planeString,
 | 
			
		||||
@ -798,7 +801,7 @@ class ClientSideScene {
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { Quaternion } from 'three'
 | 
			
		||||
import { isQuaternionVertical } from './setup'
 | 
			
		||||
import { isQuaternionVertical } from './sceneInfra'
 | 
			
		||||
 | 
			
		||||
describe('isQuaternionVertical', () => {
 | 
			
		||||
  it('should identify vertical quaternions', () => {
 | 
			
		||||
@ -24,7 +24,6 @@ import {
 | 
			
		||||
  Object3DEventMap,
 | 
			
		||||
} from 'three'
 | 
			
		||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
 | 
			
		||||
import { useRef, useEffect, useState } from 'react'
 | 
			
		||||
import { engineCommandManager } from 'lang/std/engineConnection'
 | 
			
		||||
import { v4 as uuidv4 } from 'uuid'
 | 
			
		||||
import { isReducedMotion, roundOff, throttle } from 'lib/utils'
 | 
			
		||||
@ -33,9 +32,7 @@ import { useModelingContext } from 'hooks/useModelingContext'
 | 
			
		||||
import { deg2Rad } from 'lib/utils2d'
 | 
			
		||||
import * as TWEEN from '@tweenjs/tween.js'
 | 
			
		||||
import { MouseGuard, cameraMouseDragGuards } from 'lib/cameraControls'
 | 
			
		||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
 | 
			
		||||
import { SourceRange } from 'lang/wasm'
 | 
			
		||||
import { useStore } from 'useStore'
 | 
			
		||||
import { Axis } from 'lib/selections'
 | 
			
		||||
import { createGridHelper } from './helpers'
 | 
			
		||||
 | 
			
		||||
@ -181,7 +178,7 @@ interface onMoveCallbackArgs {
 | 
			
		||||
  intersection: Intersection<Object3D<Object3DEventMap>>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ReactCameraProperties =
 | 
			
		||||
export type ReactCameraProperties =
 | 
			
		||||
  | {
 | 
			
		||||
      type: 'perspective'
 | 
			
		||||
      fov?: number
 | 
			
		||||
@ -195,8 +192,11 @@ type ReactCameraProperties =
 | 
			
		||||
      quaternion: [number, number, number, number]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
class SetupSingleton {
 | 
			
		||||
  static instance: SetupSingleton
 | 
			
		||||
// This singleton class is responsible for all of the under the hood setup for the client side scene.
 | 
			
		||||
// 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
 | 
			
		||||
  camera: PerspectiveCamera | OrthographicCamera
 | 
			
		||||
  renderer: WebGLRenderer
 | 
			
		||||
@ -333,7 +333,7 @@ class SetupSingleton {
 | 
			
		||||
    const light = new AmbientLight(0x505050) // soft white light
 | 
			
		||||
    this.scene.add(light)
 | 
			
		||||
 | 
			
		||||
    SetupSingleton.instance = this
 | 
			
		||||
    SceneInfra.instance = this
 | 
			
		||||
  }
 | 
			
		||||
  private _isCamMovingCallback: (isMoving: boolean, isTween: boolean) => void =
 | 
			
		||||
    () => {}
 | 
			
		||||
@ -713,7 +713,7 @@ class SetupSingleton {
 | 
			
		||||
  } | null => {
 | 
			
		||||
    this.planeRaycaster.setFromCamera(
 | 
			
		||||
      this.currentMouseVector,
 | 
			
		||||
      setupSingleton.camera
 | 
			
		||||
      sceneInfra.camera
 | 
			
		||||
    )
 | 
			
		||||
    const planeIntersects = this.planeRaycaster.intersectObjects(
 | 
			
		||||
      this.scene.children,
 | 
			
		||||
@ -976,7 +976,7 @@ class SetupSingleton {
 | 
			
		||||
    if (planesGroup) this.scene.remove(planesGroup)
 | 
			
		||||
  }
 | 
			
		||||
  updateOtherSelectionColors = (otherSelections: Axis[]) => {
 | 
			
		||||
    const axisGroup = setupSingleton.scene.children.find(
 | 
			
		||||
    const axisGroup = sceneInfra.scene.children.find(
 | 
			
		||||
      ({ userData }) => userData?.type === AXIS_GROUP
 | 
			
		||||
    )
 | 
			
		||||
    const axisMap: { [key: string]: Axis } = {
 | 
			
		||||
@ -998,247 +998,7 @@ class SetupSingleton {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const setupSingleton = new SetupSingleton()
 | 
			
		||||
 | 
			
		||||
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>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
export const sceneInfra = new SceneInfra()
 | 
			
		||||
 | 
			
		||||
function convertThreeCamValuesToEngineCam({
 | 
			
		||||
  position,
 | 
			
		||||
@ -25,9 +25,9 @@ import {
 | 
			
		||||
  TANGENTIAL_ARC_TO_SEGMENT,
 | 
			
		||||
  TANGENTIAL_ARC_TO_SEGMENT_BODY,
 | 
			
		||||
  TANGENTIAL_ARC_TO__SEGMENT_DASH,
 | 
			
		||||
} from './clientSideScene'
 | 
			
		||||
} from './sceneEntities'
 | 
			
		||||
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
 | 
			
		||||
import { ARROWHEAD } from './setup'
 | 
			
		||||
import { ARROWHEAD } from './sceneInfra'
 | 
			
		||||
 | 
			
		||||
export function straightSegment({
 | 
			
		||||
  from,
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,10 @@
 | 
			
		||||
import { useState, useEffect } from 'react'
 | 
			
		||||
import { setupSingleton } from '../clientSideScene/setup'
 | 
			
		||||
import { sceneInfra } from '../clientSideScene/sceneInfra'
 | 
			
		||||
import { engineCommandManager } from 'lang/std/engineConnection'
 | 
			
		||||
import { throttle, isReducedMotion } from 'lib/utils'
 | 
			
		||||
 | 
			
		||||
const updateDollyZoom = throttle(
 | 
			
		||||
  (newFov: number) => setupSingleton.dollyZoom(newFov),
 | 
			
		||||
  (newFov: number) => sceneInfra.dollyZoom(newFov),
 | 
			
		||||
  1000 / 15
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -15,19 +15,19 @@ export const CamToggle = () => {
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    engineCommandManager.waitForReady.then(async () => {
 | 
			
		||||
      setupSingleton.dollyZoom(fov)
 | 
			
		||||
      sceneInfra.dollyZoom(fov)
 | 
			
		||||
    })
 | 
			
		||||
  }, [])
 | 
			
		||||
 | 
			
		||||
  const toggleCamera = () => {
 | 
			
		||||
    if (isPerspective) {
 | 
			
		||||
      isReducedMotion()
 | 
			
		||||
        ? setupSingleton.useOrthographicCamera()
 | 
			
		||||
        : setupSingleton.animateToOrthographic()
 | 
			
		||||
        ? sceneInfra.useOrthographicCamera()
 | 
			
		||||
        : sceneInfra.animateToOrthographic()
 | 
			
		||||
    } else {
 | 
			
		||||
      isReducedMotion()
 | 
			
		||||
        ? setupSingleton.usePerspectiveCamera()
 | 
			
		||||
        : setupSingleton.animateToPerspective()
 | 
			
		||||
        ? sceneInfra.usePerspectiveCamera()
 | 
			
		||||
        : sceneInfra.animateToPerspective()
 | 
			
		||||
    }
 | 
			
		||||
    setIsPerspective(!isPerspective)
 | 
			
		||||
  }
 | 
			
		||||
@ -60,9 +60,9 @@ export const CamToggle = () => {
 | 
			
		||||
      <button
 | 
			
		||||
        onClick={() => {
 | 
			
		||||
          if (enableRotate) {
 | 
			
		||||
            setupSingleton.controls.enableRotate = false
 | 
			
		||||
            sceneInfra.controls.enableRotate = false
 | 
			
		||||
          } else {
 | 
			
		||||
            setupSingleton.controls.enableRotate = true
 | 
			
		||||
            sceneInfra.controls.enableRotate = true
 | 
			
		||||
          }
 | 
			
		||||
          setEnableRotate(!enableRotate)
 | 
			
		||||
        }}
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
 | 
			
		||||
import { AstExplorer } from './AstExplorer'
 | 
			
		||||
import { EngineCommands } from './EngineCommands'
 | 
			
		||||
import { CamDebugSettings } from 'clientSideScene/setup'
 | 
			
		||||
import { CamDebugSettings } from 'clientSideScene/ClientSideSceneComp'
 | 
			
		||||
 | 
			
		||||
export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => {
 | 
			
		||||
  return (
 | 
			
		||||
 | 
			
		||||
@ -33,8 +33,8 @@ import { applyConstraintIntersect } from './Toolbar/Intersect'
 | 
			
		||||
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
 | 
			
		||||
import useStateMachineCommands from 'hooks/useStateMachineCommands'
 | 
			
		||||
import { modelingMachineConfig } from 'lib/commandBarConfigs/modelingCommandConfig'
 | 
			
		||||
import { setupSingleton } from 'clientSideScene/setup'
 | 
			
		||||
import { getSketchQuaternion } from 'clientSideScene/clientSideScene'
 | 
			
		||||
import { sceneInfra } from 'clientSideScene/sceneInfra'
 | 
			
		||||
import { getSketchQuaternion } from 'clientSideScene/sceneEntities'
 | 
			
		||||
import { startSketchOnDefault } from 'lang/modifyAst'
 | 
			
		||||
import { Program } from 'lang/wasm'
 | 
			
		||||
 | 
			
		||||
@ -197,7 +197,7 @@ export const ModelingMachineProvider = ({
 | 
			
		||||
          // remove body item at varDecIndex
 | 
			
		||||
          newAst.body = newAst.body.filter((_, i) => i !== varDecIndex)
 | 
			
		||||
          await kclManager.executeAstMock(newAst, { updates: 'code' })
 | 
			
		||||
          setupSingleton.setCallbacks({
 | 
			
		||||
          sceneInfra.setCallbacks({
 | 
			
		||||
            onClick: () => {},
 | 
			
		||||
          })
 | 
			
		||||
        },
 | 
			
		||||
@ -208,7 +208,7 @@ export const ModelingMachineProvider = ({
 | 
			
		||||
          )
 | 
			
		||||
          await kclManager.updateAst(modifiedAst, false)
 | 
			
		||||
          const quaternion = getSketchQuaternion(pathToNode, normal)
 | 
			
		||||
          await setupSingleton.tweenCameraToQuaternion(quaternion)
 | 
			
		||||
          await sceneInfra.tweenCameraToQuaternion(quaternion)
 | 
			
		||||
          return {
 | 
			
		||||
            sketchPathToNode: pathToNode,
 | 
			
		||||
            sketchNormalBackUp: normal,
 | 
			
		||||
@ -222,7 +222,7 @@ export const ModelingMachineProvider = ({
 | 
			
		||||
            sketchPathToNode || [],
 | 
			
		||||
            sketchNormalBackUp
 | 
			
		||||
          )
 | 
			
		||||
          await setupSingleton.tweenCameraToQuaternion(quaternion)
 | 
			
		||||
          await sceneInfra.tweenCameraToQuaternion(quaternion)
 | 
			
		||||
        },
 | 
			
		||||
        'Get horizontal info': async ({
 | 
			
		||||
          selectionRanges,
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@ import { Models } from '@kittycad/lib'
 | 
			
		||||
import { engineCommandManager } from '../lang/std/engineConnection'
 | 
			
		||||
import { useModelingContext } from 'hooks/useModelingContext'
 | 
			
		||||
import { useKclContext } from 'lang/KclSingleton'
 | 
			
		||||
import { ClientSideScene } from 'clientSideScene/setup'
 | 
			
		||||
import { ClientSideScene } from 'clientSideScene/ClientSideSceneComp'
 | 
			
		||||
 | 
			
		||||
export const Stream = ({ className = '' }: { className?: string }) => {
 | 
			
		||||
  const [isLoading, setIsLoading] = useState(true)
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,7 @@ import interact from '@replit/codemirror-interact'
 | 
			
		||||
import { engineCommandManager } from '../lang/std/engineConnection'
 | 
			
		||||
import { kclManager, useKclContext } from 'lang/KclSingleton'
 | 
			
		||||
import { ModelingMachineEvent } from 'machines/modelingMachine'
 | 
			
		||||
import { setupSingleton } from 'clientSideScene/setup'
 | 
			
		||||
import { sceneInfra } from 'clientSideScene/sceneInfra'
 | 
			
		||||
 | 
			
		||||
export const editorShortcutMeta = {
 | 
			
		||||
  formatCode: {
 | 
			
		||||
@ -119,7 +119,7 @@ export const TextEditor = ({
 | 
			
		||||
    if (!editorView) {
 | 
			
		||||
      setEditorView(viewUpdate.view)
 | 
			
		||||
    }
 | 
			
		||||
    if (setupSingleton.selected) return // mid drag
 | 
			
		||||
    if (sceneInfra.selected) return // mid drag
 | 
			
		||||
    const ignoreEvents: ModelingMachineEvent['type'][] = [
 | 
			
		||||
      'Equip Line tool',
 | 
			
		||||
      'Equip tangential arc to',
 | 
			
		||||
 | 
			
		||||
@ -99,7 +99,7 @@ class KclManager {
 | 
			
		||||
          })
 | 
			
		||||
      })
 | 
			
		||||
    } else {
 | 
			
		||||
      localStorage?.setItem(PERSIST_CODE_TOKEN, code)
 | 
			
		||||
      safteLSSetItem(PERSIST_CODE_TOKEN, code)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -154,16 +154,16 @@ class KclManager {
 | 
			
		||||
      this.code = ''
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    const storedCode = localStorage.getItem(PERSIST_CODE_TOKEN)
 | 
			
		||||
    const storedCode = safeLSGetItem(PERSIST_CODE_TOKEN) || ''
 | 
			
		||||
    // TODO #819 remove zustand persistence logic in a few months
 | 
			
		||||
    // short term migration, shouldn't make a difference for tauri app users
 | 
			
		||||
    // 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) {
 | 
			
		||||
      this.code = zustandStore.state.code
 | 
			
		||||
      localStorage.setItem(PERSIST_CODE_TOKEN, this._code)
 | 
			
		||||
      safteLSSetItem(PERSIST_CODE_TOKEN, this._code)
 | 
			
		||||
      zustandStore.state.code = ''
 | 
			
		||||
      localStorage.setItem('store', JSON.stringify(zustandStore))
 | 
			
		||||
      safteLSSetItem('store', JSON.stringify(zustandStore))
 | 
			
		||||
    } else if (storedCode === null) {
 | 
			
		||||
      this.code = bracket
 | 
			
		||||
    } else {
 | 
			
		||||
@ -457,3 +457,13 @@ export function KclContextProvider({
 | 
			
		||||
    </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,
 | 
			
		||||
} from './std/sketch'
 | 
			
		||||
import { isLiteralArrayOrStatic } from './std/sketchcombos'
 | 
			
		||||
import { DefaultPlaneStr } from 'clientSideScene/clientSideScene'
 | 
			
		||||
import { DefaultPlaneStr } from 'clientSideScene/sceneEntities'
 | 
			
		||||
import { roundOff } from 'lib/utils'
 | 
			
		||||
 | 
			
		||||
export function startSketchOnDefault(
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ import { exportSave } from 'lib/exportSave'
 | 
			
		||||
import { v4 as uuidv4 } from 'uuid'
 | 
			
		||||
import * as Sentry from '@sentry/react'
 | 
			
		||||
import { getNodePathFromSourceRange } from 'lang/queryAst'
 | 
			
		||||
import { setupSingleton } from 'clientSideScene/setup'
 | 
			
		||||
import { sceneInfra } from 'clientSideScene/sceneInfra'
 | 
			
		||||
 | 
			
		||||
let lastMessage = ''
 | 
			
		||||
 | 
			
		||||
@ -1012,7 +1012,7 @@ export class EngineCommandManager {
 | 
			
		||||
            gizmo_mode: true,
 | 
			
		||||
          },
 | 
			
		||||
        })
 | 
			
		||||
        setupSingleton.onStreamStart()
 | 
			
		||||
        sceneInfra.onStreamStart()
 | 
			
		||||
 | 
			
		||||
        executeCode(undefined, true)
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
@ -14,11 +14,11 @@ import { CommandArgument } from './commandTypes'
 | 
			
		||||
import {
 | 
			
		||||
  STRAIGHT_SEGMENT,
 | 
			
		||||
  TANGENTIAL_ARC_TO_SEGMENT,
 | 
			
		||||
  clientSideScene,
 | 
			
		||||
  sceneEntitiesManager,
 | 
			
		||||
  getParentGroup,
 | 
			
		||||
} from 'clientSideScene/clientSideScene'
 | 
			
		||||
} from 'clientSideScene/sceneEntities'
 | 
			
		||||
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 Y_AXIS_UUID = '680fd157-266f-4b8a-984f-cdf46b8bdf01'
 | 
			
		||||
@ -401,7 +401,7 @@ function updateSceneObjectColors(codeBasedSelections: Selection[]) {
 | 
			
		||||
    console.error('error parsing code in processCodeMirrorRanges', e)
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
  Object.values(clientSideScene.activeSegments).forEach((segmentGroup) => {
 | 
			
		||||
  Object.values(sceneEntitiesManager.activeSegments).forEach((segmentGroup) => {
 | 
			
		||||
    if (
 | 
			
		||||
      ![STRAIGHT_SEGMENT, TANGENTIAL_ARC_TO_SEGMENT].includes(
 | 
			
		||||
        segmentGroup?.userData?.type
 | 
			
		||||
 | 
			
		||||
@ -44,11 +44,11 @@ import { Models } from '@kittycad/lib/dist/types/src'
 | 
			
		||||
import { ModelingCommandSchema } from 'lib/commandBarConfigs/modelingCommandConfig'
 | 
			
		||||
import {
 | 
			
		||||
  DefaultPlaneStr,
 | 
			
		||||
  clientSideScene,
 | 
			
		||||
  sceneEntitiesManager,
 | 
			
		||||
  quaternionFromSketchGroup,
 | 
			
		||||
  sketchGroupFromPathToNode,
 | 
			
		||||
} from 'clientSideScene/clientSideScene'
 | 
			
		||||
import { setupSingleton } from 'clientSideScene/setup'
 | 
			
		||||
} from 'clientSideScene/sceneEntities'
 | 
			
		||||
import { sceneInfra } from 'clientSideScene/sceneInfra'
 | 
			
		||||
 | 
			
		||||
export const MODELING_PERSIST_KEY = 'MODELING_PERSIST_KEY'
 | 
			
		||||
 | 
			
		||||
@ -605,7 +605,7 @@ export const modelingMachine = createMachine(
 | 
			
		||||
        if (!sketchPathToNode) return {}
 | 
			
		||||
        return getSketchMetadataFromPathToNode(sketchPathToNode)
 | 
			
		||||
      }),
 | 
			
		||||
      'hide default planes': () => setupSingleton.removeDefaultPlanes(),
 | 
			
		||||
      'hide default planes': () => sceneInfra.removeDefaultPlanes(),
 | 
			
		||||
      'reset sketch metadata': assign({
 | 
			
		||||
        sketchPathToNode: null,
 | 
			
		||||
        sketchEnginePathId: '',
 | 
			
		||||
@ -632,7 +632,7 @@ export const modelingMachine = createMachine(
 | 
			
		||||
          kclManager.ast,
 | 
			
		||||
          kclManager.programMemory
 | 
			
		||||
        )
 | 
			
		||||
        clientSideScene.updateAstAndRejigSketch(
 | 
			
		||||
        sceneEntitiesManager.updateAstAndRejigSketch(
 | 
			
		||||
          sketchPathToNode || [],
 | 
			
		||||
          modifiedAst
 | 
			
		||||
        )
 | 
			
		||||
@ -644,7 +644,7 @@ export const modelingMachine = createMachine(
 | 
			
		||||
          kclManager.ast,
 | 
			
		||||
          kclManager.programMemory
 | 
			
		||||
        )
 | 
			
		||||
        clientSideScene.updateAstAndRejigSketch(
 | 
			
		||||
        sceneEntitiesManager.updateAstAndRejigSketch(
 | 
			
		||||
          sketchPathToNode || [],
 | 
			
		||||
          modifiedAst
 | 
			
		||||
        )
 | 
			
		||||
@ -657,7 +657,7 @@ export const modelingMachine = createMachine(
 | 
			
		||||
          selectionRanges,
 | 
			
		||||
          constraint: 'setVertDistance',
 | 
			
		||||
        })
 | 
			
		||||
        clientSideScene.updateAstAndRejigSketch(
 | 
			
		||||
        sceneEntitiesManager.updateAstAndRejigSketch(
 | 
			
		||||
          sketchPathToNode || [],
 | 
			
		||||
          modifiedAst
 | 
			
		||||
        )
 | 
			
		||||
@ -667,7 +667,7 @@ export const modelingMachine = createMachine(
 | 
			
		||||
          selectionRanges,
 | 
			
		||||
          constraint: 'setHorzDistance',
 | 
			
		||||
        })
 | 
			
		||||
        clientSideScene.updateAstAndRejigSketch(
 | 
			
		||||
        sceneEntitiesManager.updateAstAndRejigSketch(
 | 
			
		||||
          sketchPathToNode || [],
 | 
			
		||||
          modifiedAst
 | 
			
		||||
        )
 | 
			
		||||
@ -677,7 +677,7 @@ export const modelingMachine = createMachine(
 | 
			
		||||
          selectionRanges,
 | 
			
		||||
          constraint: 'snapToXAxis',
 | 
			
		||||
        })
 | 
			
		||||
        clientSideScene.updateAstAndRejigSketch(
 | 
			
		||||
        sceneEntitiesManager.updateAstAndRejigSketch(
 | 
			
		||||
          sketchPathToNode || [],
 | 
			
		||||
          modifiedAst
 | 
			
		||||
        )
 | 
			
		||||
@ -687,7 +687,7 @@ export const modelingMachine = createMachine(
 | 
			
		||||
          selectionRanges,
 | 
			
		||||
          constraint: 'snapToYAxis',
 | 
			
		||||
        })
 | 
			
		||||
        clientSideScene.updateAstAndRejigSketch(
 | 
			
		||||
        sceneEntitiesManager.updateAstAndRejigSketch(
 | 
			
		||||
          sketchPathToNode || [],
 | 
			
		||||
          modifiedAst
 | 
			
		||||
        )
 | 
			
		||||
@ -696,7 +696,7 @@ export const modelingMachine = createMachine(
 | 
			
		||||
        const { modifiedAst } = applyConstraintEqualLength({
 | 
			
		||||
          selectionRanges,
 | 
			
		||||
        })
 | 
			
		||||
        clientSideScene.updateAstAndRejigSketch(
 | 
			
		||||
        sceneEntitiesManager.updateAstAndRejigSketch(
 | 
			
		||||
          sketchPathToNode || [],
 | 
			
		||||
          modifiedAst
 | 
			
		||||
        )
 | 
			
		||||
@ -705,7 +705,7 @@ export const modelingMachine = createMachine(
 | 
			
		||||
        const { modifiedAst } = applyConstraintEqualAngle({
 | 
			
		||||
          selectionRanges,
 | 
			
		||||
        })
 | 
			
		||||
        clientSideScene.updateAstAndRejigSketch(
 | 
			
		||||
        sceneEntitiesManager.updateAstAndRejigSketch(
 | 
			
		||||
          sketchPathToNode || [],
 | 
			
		||||
          modifiedAst
 | 
			
		||||
        )
 | 
			
		||||
@ -717,7 +717,7 @@ export const modelingMachine = createMachine(
 | 
			
		||||
        const { modifiedAst } = applyRemoveConstrainingValues({
 | 
			
		||||
          selectionRanges,
 | 
			
		||||
        })
 | 
			
		||||
        clientSideScene.updateAstAndRejigSketch(
 | 
			
		||||
        sceneEntitiesManager.updateAstAndRejigSketch(
 | 
			
		||||
          sketchPathToNode || [],
 | 
			
		||||
          modifiedAst
 | 
			
		||||
        )
 | 
			
		||||
@ -742,59 +742,61 @@ export const modelingMachine = createMachine(
 | 
			
		||||
      },
 | 
			
		||||
      'conditionally equip line tool': (_, { type }) => {
 | 
			
		||||
        if (type === 'done.invoke.animate-to-face') {
 | 
			
		||||
          setupSingleton.modelingSend('Equip Line tool')
 | 
			
		||||
          sceneInfra.modelingSend('Equip Line tool')
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      'setup client side sketch segments': ({ sketchPathToNode }, { type }) => {
 | 
			
		||||
        if (Object.keys(clientSideScene.activeSegments).length > 0) {
 | 
			
		||||
          clientSideScene.tearDownSketch({ removeAxis: false }).then(() => {
 | 
			
		||||
            clientSideScene.setupSketch({
 | 
			
		||||
              sketchPathToNode: sketchPathToNode || [],
 | 
			
		||||
        if (Object.keys(sceneEntitiesManager.activeSegments).length > 0) {
 | 
			
		||||
          sceneEntitiesManager
 | 
			
		||||
            .tearDownSketch({ removeAxis: false })
 | 
			
		||||
            .then(() => {
 | 
			
		||||
              sceneEntitiesManager.setupSketch({
 | 
			
		||||
                sketchPathToNode: sketchPathToNode || [],
 | 
			
		||||
              })
 | 
			
		||||
            })
 | 
			
		||||
          })
 | 
			
		||||
        } else {
 | 
			
		||||
          clientSideScene.setupSketch({
 | 
			
		||||
          sceneEntitiesManager.setupSketch({
 | 
			
		||||
            sketchPathToNode: sketchPathToNode || [],
 | 
			
		||||
          })
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      'animate after sketch': () => {
 | 
			
		||||
        clientSideScene.animateAfterSketch()
 | 
			
		||||
        sceneEntitiesManager.animateAfterSketch()
 | 
			
		||||
      },
 | 
			
		||||
      'tear down client sketch': () => {
 | 
			
		||||
        if (clientSideScene.activeSegments) {
 | 
			
		||||
          clientSideScene.tearDownSketch({ removeAxis: false })
 | 
			
		||||
        if (sceneEntitiesManager.activeSegments) {
 | 
			
		||||
          sceneEntitiesManager.tearDownSketch({ removeAxis: false })
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      'remove sketch grid': () => clientSideScene.removeSketchGrid(),
 | 
			
		||||
      'remove sketch grid': () => sceneEntitiesManager.removeSketchGrid(),
 | 
			
		||||
      'set up draft line': ({ sketchPathToNode }) => {
 | 
			
		||||
        clientSideScene.setUpDraftLine(sketchPathToNode || [])
 | 
			
		||||
        sceneEntitiesManager.setUpDraftLine(sketchPathToNode || [])
 | 
			
		||||
      },
 | 
			
		||||
      'set up draft arc': ({ sketchPathToNode }) => {
 | 
			
		||||
        clientSideScene.setUpDraftArc(sketchPathToNode || [])
 | 
			
		||||
        sceneEntitiesManager.setUpDraftArc(sketchPathToNode || [])
 | 
			
		||||
      },
 | 
			
		||||
      'set up draft line without teardown': ({ sketchPathToNode }) =>
 | 
			
		||||
        clientSideScene.setupSketch({
 | 
			
		||||
        sceneEntitiesManager.setupSketch({
 | 
			
		||||
          sketchPathToNode: sketchPathToNode || [],
 | 
			
		||||
          draftSegment: 'line',
 | 
			
		||||
        }),
 | 
			
		||||
      'show default planes': () => {
 | 
			
		||||
        setupSingleton.showDefaultPlanes()
 | 
			
		||||
        clientSideScene.setupDefaultPlaneHover()
 | 
			
		||||
        sceneInfra.showDefaultPlanes()
 | 
			
		||||
        sceneEntitiesManager.setupDefaultPlaneHover()
 | 
			
		||||
      },
 | 
			
		||||
      'setup noPoints onClick listener': ({ sketchPathToNode }) => {
 | 
			
		||||
        clientSideScene.createIntersectionPlane()
 | 
			
		||||
        sceneEntitiesManager.createIntersectionPlane()
 | 
			
		||||
        const sketchGroup = sketchGroupFromPathToNode({
 | 
			
		||||
          pathToNode: sketchPathToNode || [],
 | 
			
		||||
          ast: kclManager.ast,
 | 
			
		||||
          programMemory: kclManager.programMemory,
 | 
			
		||||
        })
 | 
			
		||||
        const quaternion = quaternionFromSketchGroup(sketchGroup)
 | 
			
		||||
        clientSideScene.intersectionPlane &&
 | 
			
		||||
          clientSideScene.intersectionPlane.setRotationFromQuaternion(
 | 
			
		||||
        sceneEntitiesManager.intersectionPlane &&
 | 
			
		||||
          sceneEntitiesManager.intersectionPlane.setRotationFromQuaternion(
 | 
			
		||||
            quaternion
 | 
			
		||||
          )
 | 
			
		||||
        setupSingleton.setCallbacks({
 | 
			
		||||
        sceneInfra.setCallbacks({
 | 
			
		||||
          onClick: async (args) => {
 | 
			
		||||
            if (!args) return
 | 
			
		||||
            const { intersection2d } = args
 | 
			
		||||
@ -805,13 +807,13 @@ export const modelingMachine = createMachine(
 | 
			
		||||
              [intersection2d.x, intersection2d.y]
 | 
			
		||||
            )
 | 
			
		||||
            await kclManager.updateAst(modifiedAst, false)
 | 
			
		||||
            clientSideScene.removeIntersectionPlane()
 | 
			
		||||
            setupSingleton.modelingSend('Add start point')
 | 
			
		||||
            sceneEntitiesManager.removeIntersectionPlane()
 | 
			
		||||
            sceneInfra.modelingSend('Add start point')
 | 
			
		||||
          },
 | 
			
		||||
        })
 | 
			
		||||
      },
 | 
			
		||||
      'add axis n grid': ({ sketchPathToNode }) =>
 | 
			
		||||
        clientSideScene.createSketchAxis(sketchPathToNode || []),
 | 
			
		||||
        sceneEntitiesManager.createSketchAxis(sketchPathToNode || []),
 | 
			
		||||
    },
 | 
			
		||||
    // end actions
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user