show default planes on empty scene (#3237)
* show default planes on empty sceen
* fmt
* remove log
* fix silly click listener bug
* delete old stuff
* test tweak
* Revert "test tweak"
This reverts commit e9cb4ac4b5.
---------
Co-authored-by: Paul Tagliamonte <paul@zoo.dev>
			
			
This commit is contained in:
		
							
								
								
									
										20
									
								
								src/App.tsx
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								src/App.tsx
									
									
									
									
									
								
							@ -95,16 +95,16 @@ export function App() {
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    const newCmdId = uuidv4()
 | 
			
		||||
    if (context.store?.buttonDownInStream === undefined) {
 | 
			
		||||
      debounceSocketSend({
 | 
			
		||||
        type: 'modeling_cmd_req',
 | 
			
		||||
        cmd: {
 | 
			
		||||
          type: 'highlight_set_entity',
 | 
			
		||||
          selected_at_window: { x, y },
 | 
			
		||||
        },
 | 
			
		||||
        cmd_id: newCmdId,
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
    if (state.matches('idle.showPlanes')) return
 | 
			
		||||
    if (context.store?.buttonDownInStream !== undefined) return
 | 
			
		||||
    debounceSocketSend({
 | 
			
		||||
      type: 'modeling_cmd_req',
 | 
			
		||||
      cmd: {
 | 
			
		||||
        type: 'highlight_set_entity',
 | 
			
		||||
        selected_at_window: { x, y },
 | 
			
		||||
      },
 | 
			
		||||
      cmd_id: newCmdId,
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
 | 
			
		||||
@ -102,6 +102,7 @@ export const ClientSideScene = ({
 | 
			
		||||
    canvas.addEventListener('mousedown', sceneInfra.onMouseDown, false)
 | 
			
		||||
    canvas.addEventListener('mouseup', sceneInfra.onMouseUp, false)
 | 
			
		||||
    sceneInfra.setSend(send)
 | 
			
		||||
    engineCommandManager.modelingSend = send
 | 
			
		||||
    return () => {
 | 
			
		||||
      canvas?.removeEventListener('mousemove', sceneInfra.onMouseMove)
 | 
			
		||||
      canvas?.removeEventListener('mousedown', sceneInfra.onMouseDown)
 | 
			
		||||
 | 
			
		||||
@ -22,9 +22,6 @@ import {
 | 
			
		||||
import {
 | 
			
		||||
  ARROWHEAD,
 | 
			
		||||
  AXIS_GROUP,
 | 
			
		||||
  DEFAULT_PLANES,
 | 
			
		||||
  DefaultPlane,
 | 
			
		||||
  defaultPlaneColor,
 | 
			
		||||
  getSceneScale,
 | 
			
		||||
  INTERSECTION_PLANE_LAYER,
 | 
			
		||||
  OnClickCallbackArgs,
 | 
			
		||||
@ -202,6 +199,7 @@ export class SceneEntities {
 | 
			
		||||
 | 
			
		||||
  createIntersectionPlane() {
 | 
			
		||||
    if (sceneInfra.scene.getObjectByName(RAYCASTABLE_PLANE)) {
 | 
			
		||||
      // this.removeIntersectionPlane()
 | 
			
		||||
      console.warn('createIntersectionPlane called when it already exists')
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
@ -1502,146 +1500,6 @@ export class SceneEntities {
 | 
			
		||||
      this._tearDownSketch(0, resolve, reject, { removeAxis })
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
  setupDefaultPlaneHover() {
 | 
			
		||||
    sceneInfra.setCallbacks({
 | 
			
		||||
      onMouseEnter: ({ selected }) => {
 | 
			
		||||
        if (!(selected instanceof Mesh && selected.parent)) return
 | 
			
		||||
        if (selected.parent.userData.type !== DEFAULT_PLANES) return
 | 
			
		||||
        const type: DefaultPlane = selected.userData.type
 | 
			
		||||
        selected.material.color = defaultPlaneColor(type, 0.5, 1)
 | 
			
		||||
      },
 | 
			
		||||
      onMouseLeave: ({ selected }) => {
 | 
			
		||||
        if (!(selected instanceof Mesh && selected.parent)) return
 | 
			
		||||
        if (selected.parent.userData.type !== DEFAULT_PLANES) return
 | 
			
		||||
        const type: DefaultPlane = selected.userData.type
 | 
			
		||||
        selected.material.color = defaultPlaneColor(type)
 | 
			
		||||
      },
 | 
			
		||||
      onClick: async (args) => {
 | 
			
		||||
        const { entity_id } = await sendSelectEventToEngine(
 | 
			
		||||
          args?.mouseEvent,
 | 
			
		||||
          document.getElementById('video-stream') as HTMLVideoElement,
 | 
			
		||||
          sceneInfra._streamDimensions
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        let _entity_id = entity_id
 | 
			
		||||
        if (!_entity_id) return
 | 
			
		||||
        if (
 | 
			
		||||
          engineCommandManager.defaultPlanes?.xy === _entity_id ||
 | 
			
		||||
          engineCommandManager.defaultPlanes?.xz === _entity_id ||
 | 
			
		||||
          engineCommandManager.defaultPlanes?.yz === _entity_id ||
 | 
			
		||||
          engineCommandManager.defaultPlanes?.negXy === _entity_id ||
 | 
			
		||||
          engineCommandManager.defaultPlanes?.negXz === _entity_id ||
 | 
			
		||||
          engineCommandManager.defaultPlanes?.negYz === _entity_id
 | 
			
		||||
        ) {
 | 
			
		||||
          const defaultPlaneStrMap: Record<string, DefaultPlaneStr> = {
 | 
			
		||||
            [engineCommandManager.defaultPlanes.xy]: 'XY',
 | 
			
		||||
            [engineCommandManager.defaultPlanes.xz]: 'XZ',
 | 
			
		||||
            [engineCommandManager.defaultPlanes.yz]: 'YZ',
 | 
			
		||||
            [engineCommandManager.defaultPlanes.negXy]: '-XY',
 | 
			
		||||
            [engineCommandManager.defaultPlanes.negXz]: '-XZ',
 | 
			
		||||
            [engineCommandManager.defaultPlanes.negYz]: '-YZ',
 | 
			
		||||
          }
 | 
			
		||||
          // TODO can we get this information from rust land when it creates the default planes?
 | 
			
		||||
          // maybe returned from make_default_planes (src/wasm-lib/src/wasm.rs)
 | 
			
		||||
          let zAxis: [number, number, number] = [0, 0, 1]
 | 
			
		||||
          let yAxis: [number, number, number] = [0, 1, 0]
 | 
			
		||||
 | 
			
		||||
          // get unit vector from camera position to target
 | 
			
		||||
          const camVector = sceneInfra.camControls.camera.position
 | 
			
		||||
            .clone()
 | 
			
		||||
            .sub(sceneInfra.camControls.target)
 | 
			
		||||
 | 
			
		||||
          if (engineCommandManager.defaultPlanes?.xy === _entity_id) {
 | 
			
		||||
            zAxis = [0, 0, 1]
 | 
			
		||||
            yAxis = [0, 1, 0]
 | 
			
		||||
            if (camVector.z < 0) {
 | 
			
		||||
              zAxis = [0, 0, -1]
 | 
			
		||||
              _entity_id = engineCommandManager.defaultPlanes?.negXy || ''
 | 
			
		||||
            }
 | 
			
		||||
          } else if (engineCommandManager.defaultPlanes?.yz === _entity_id) {
 | 
			
		||||
            zAxis = [1, 0, 0]
 | 
			
		||||
            yAxis = [0, 0, 1]
 | 
			
		||||
            if (camVector.x < 0) {
 | 
			
		||||
              zAxis = [-1, 0, 0]
 | 
			
		||||
              _entity_id = engineCommandManager.defaultPlanes?.negYz || ''
 | 
			
		||||
            }
 | 
			
		||||
          } else if (engineCommandManager.defaultPlanes?.xz === _entity_id) {
 | 
			
		||||
            zAxis = [0, 1, 0]
 | 
			
		||||
            yAxis = [0, 0, 1]
 | 
			
		||||
            _entity_id = engineCommandManager.defaultPlanes?.negXz || ''
 | 
			
		||||
            if (camVector.y < 0) {
 | 
			
		||||
              zAxis = [0, -1, 0]
 | 
			
		||||
              _entity_id = engineCommandManager.defaultPlanes?.xz || ''
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          sceneInfra.modelingSend({
 | 
			
		||||
            type: 'Select default plane',
 | 
			
		||||
            data: {
 | 
			
		||||
              type: 'defaultPlane',
 | 
			
		||||
              planeId: _entity_id,
 | 
			
		||||
              plane: defaultPlaneStrMap[_entity_id],
 | 
			
		||||
              zAxis,
 | 
			
		||||
              yAxis,
 | 
			
		||||
            },
 | 
			
		||||
          })
 | 
			
		||||
          return
 | 
			
		||||
        }
 | 
			
		||||
        const artifact = this.engineCommandManager.artifactMap[_entity_id]
 | 
			
		||||
        // If we clicked on an extrude wall, we climb up the parent Id
 | 
			
		||||
        // to get the sketch profile's face ID. If we clicked on an endcap,
 | 
			
		||||
        // we already have it.
 | 
			
		||||
        const pathId =
 | 
			
		||||
          artifact?.type === 'extrudeWall' || artifact?.type === 'extrudeCap'
 | 
			
		||||
            ? artifact.pathId
 | 
			
		||||
            : ''
 | 
			
		||||
 | 
			
		||||
        // tsc cannot infer that target can have extrusions
 | 
			
		||||
        // from the commandType (why?) so we need to cast it
 | 
			
		||||
        const path = this.engineCommandManager.artifactMap?.[pathId || '']
 | 
			
		||||
        const extrusionId =
 | 
			
		||||
          path?.type === 'startPath' ? path.extrusionIds[0] : ''
 | 
			
		||||
 | 
			
		||||
        // TODO: We get the first extrusion command ID,
 | 
			
		||||
        // which is fine while backend systems only support one extrusion.
 | 
			
		||||
        // but we need to more robustly handle resolving to the correct extrusion
 | 
			
		||||
        // if there are multiple.
 | 
			
		||||
        const extrusions = this.engineCommandManager.artifactMap?.[extrusionId]
 | 
			
		||||
 | 
			
		||||
        if (artifact?.type !== 'extrudeCap' && artifact?.type !== 'extrudeWall')
 | 
			
		||||
          return
 | 
			
		||||
 | 
			
		||||
        const faceInfo = await getFaceDetails(_entity_id)
 | 
			
		||||
        if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis) return
 | 
			
		||||
        const { z_axis, y_axis, origin } = faceInfo
 | 
			
		||||
        const sketchPathToNode = getNodePathFromSourceRange(
 | 
			
		||||
          kclManager.ast,
 | 
			
		||||
          artifact.range
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        const extrudePathToNode = extrusions?.range
 | 
			
		||||
          ? getNodePathFromSourceRange(kclManager.ast, extrusions.range)
 | 
			
		||||
          : []
 | 
			
		||||
 | 
			
		||||
        sceneInfra.modelingSend({
 | 
			
		||||
          type: 'Select default plane',
 | 
			
		||||
          data: {
 | 
			
		||||
            type: 'extrudeFace',
 | 
			
		||||
            zAxis: [z_axis.x, z_axis.y, z_axis.z],
 | 
			
		||||
            yAxis: [y_axis.x, y_axis.y, y_axis.z],
 | 
			
		||||
            position: [origin.x, origin.y, origin.z].map(
 | 
			
		||||
              (num) => num / sceneInfra._baseUnitMultiplier
 | 
			
		||||
            ) as [number, number, number],
 | 
			
		||||
            sketchPathToNode,
 | 
			
		||||
            extrudePathToNode,
 | 
			
		||||
            cap: artifact.type === 'extrudeCap' ? artifact.cap : 'none',
 | 
			
		||||
            faceId: _entity_id,
 | 
			
		||||
          },
 | 
			
		||||
        })
 | 
			
		||||
        return
 | 
			
		||||
      },
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
  mouseEnterLeaveCallbacks() {
 | 
			
		||||
    return {
 | 
			
		||||
      onMouseEnter: ({ selected, dragSelected }: OnMouseEnterLeaveArgs) => {
 | 
			
		||||
 | 
			
		||||
@ -11,10 +11,8 @@ import {
 | 
			
		||||
  Raycaster,
 | 
			
		||||
  Vector2,
 | 
			
		||||
  Group,
 | 
			
		||||
  PlaneGeometry,
 | 
			
		||||
  MeshBasicMaterial,
 | 
			
		||||
  Mesh,
 | 
			
		||||
  DoubleSide,
 | 
			
		||||
  Intersection,
 | 
			
		||||
  Object3D,
 | 
			
		||||
  Object3DEventMap,
 | 
			
		||||
@ -48,7 +46,6 @@ export const DEBUG_SHOW_INTERSECTION_PLANE: false = false
 | 
			
		||||
export const DEBUG_SHOW_BOTH_SCENES: false = false
 | 
			
		||||
 | 
			
		||||
export const RAYCASTABLE_PLANE = 'raycastable-plane'
 | 
			
		||||
export const DEFAULT_PLANES = 'default-planes'
 | 
			
		||||
 | 
			
		||||
export const X_AXIS = 'xAxis'
 | 
			
		||||
export const Y_AXIS = 'yAxis'
 | 
			
		||||
@ -325,16 +322,9 @@ export class SceneInfra {
 | 
			
		||||
      this.camControls.camera,
 | 
			
		||||
      this.camControls.target
 | 
			
		||||
    )
 | 
			
		||||
    const planesGroup = this.scene.getObjectByName(DEFAULT_PLANES)
 | 
			
		||||
    const axisGroup = this.scene
 | 
			
		||||
      .getObjectByName(AXIS_GROUP)
 | 
			
		||||
      ?.getObjectByName('gridHelper')
 | 
			
		||||
    planesGroup &&
 | 
			
		||||
      planesGroup.scale.set(
 | 
			
		||||
        scale / this._baseUnitMultiplier,
 | 
			
		||||
        scale / this._baseUnitMultiplier,
 | 
			
		||||
        scale / this._baseUnitMultiplier
 | 
			
		||||
      )
 | 
			
		||||
    axisGroup?.name === 'gridHelper' && axisGroup.scale.set(scale, scale, scale)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -632,59 +622,6 @@ export class SceneInfra {
 | 
			
		||||
      this.onClickCallback({ mouseEvent, intersects })
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  showDefaultPlanes() {
 | 
			
		||||
    const addPlane = (
 | 
			
		||||
      rotation: { x: number; y: number; z: number }, //
 | 
			
		||||
      type: DefaultPlane
 | 
			
		||||
    ): Mesh => {
 | 
			
		||||
      const planeGeometry = new PlaneGeometry(100, 100)
 | 
			
		||||
      const planeMaterial = new MeshBasicMaterial({
 | 
			
		||||
        color: defaultPlaneColor(type),
 | 
			
		||||
        transparent: true,
 | 
			
		||||
        opacity: 0.0,
 | 
			
		||||
        side: DoubleSide,
 | 
			
		||||
        depthTest: false, // needed to avoid transparency issues
 | 
			
		||||
      })
 | 
			
		||||
      const plane = new Mesh(planeGeometry, planeMaterial)
 | 
			
		||||
      plane.rotation.x = rotation.x
 | 
			
		||||
      plane.rotation.y = rotation.y
 | 
			
		||||
      plane.rotation.z = rotation.z
 | 
			
		||||
      plane.userData.type = type
 | 
			
		||||
      plane.name = type
 | 
			
		||||
      return plane
 | 
			
		||||
    }
 | 
			
		||||
    const planes = [
 | 
			
		||||
      addPlane({ x: 0, y: Math.PI / 2, z: 0 }, YZ_PLANE),
 | 
			
		||||
      addPlane({ x: 0, y: 0, z: 0 }, XY_PLANE),
 | 
			
		||||
      addPlane({ x: -Math.PI / 2, y: 0, z: 0 }, XZ_PLANE),
 | 
			
		||||
    ]
 | 
			
		||||
    const planesGroup = new Group()
 | 
			
		||||
    planesGroup.userData.type = DEFAULT_PLANES
 | 
			
		||||
    planesGroup.name = DEFAULT_PLANES
 | 
			
		||||
    planesGroup.add(...planes)
 | 
			
		||||
    planesGroup.traverse((child) => {
 | 
			
		||||
      if (child instanceof Mesh) {
 | 
			
		||||
        child.layers.enable(SKETCH_LAYER)
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    planesGroup.layers.enable(SKETCH_LAYER)
 | 
			
		||||
    const sceneScale = getSceneScale(
 | 
			
		||||
      this.camControls.camera,
 | 
			
		||||
      this.camControls.target
 | 
			
		||||
    )
 | 
			
		||||
    planesGroup.scale.set(
 | 
			
		||||
      sceneScale / this._baseUnitMultiplier,
 | 
			
		||||
      sceneScale / this._baseUnitMultiplier,
 | 
			
		||||
      sceneScale / this._baseUnitMultiplier
 | 
			
		||||
    )
 | 
			
		||||
    this.scene.add(planesGroup)
 | 
			
		||||
  }
 | 
			
		||||
  removeDefaultPlanes() {
 | 
			
		||||
    const planesGroup = this.scene.children.find(
 | 
			
		||||
      ({ userData }) => userData.type === DEFAULT_PLANES
 | 
			
		||||
    )
 | 
			
		||||
    if (planesGroup) this.scene.remove(planesGroup)
 | 
			
		||||
  }
 | 
			
		||||
  updateOtherSelectionColors = (otherSelections: Axis[]) => {
 | 
			
		||||
    const axisGroup = this.scene.children.find(
 | 
			
		||||
      ({ userData }) => userData?.type === AXIS_GROUP
 | 
			
		||||
@ -742,28 +679,3 @@ function baseUnitTomm(baseUnit: BaseUnit) {
 | 
			
		||||
      return 914.4
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type DefaultPlane =
 | 
			
		||||
  | 'xy-default-plane'
 | 
			
		||||
  | 'xz-default-plane'
 | 
			
		||||
  | 'yz-default-plane'
 | 
			
		||||
 | 
			
		||||
export const XY_PLANE: DefaultPlane = 'xy-default-plane'
 | 
			
		||||
export const XZ_PLANE: DefaultPlane = 'xz-default-plane'
 | 
			
		||||
export const YZ_PLANE: DefaultPlane = 'yz-default-plane'
 | 
			
		||||
 | 
			
		||||
export function defaultPlaneColor(
 | 
			
		||||
  plane: DefaultPlane,
 | 
			
		||||
  lowCh = 0.1,
 | 
			
		||||
  highCh = 0.7
 | 
			
		||||
): Color {
 | 
			
		||||
  switch (plane) {
 | 
			
		||||
    case XY_PLANE:
 | 
			
		||||
      return new Color(highCh, lowCh, lowCh)
 | 
			
		||||
    case XZ_PLANE:
 | 
			
		||||
      return new Color(lowCh, lowCh, highCh)
 | 
			
		||||
    case YZ_PLANE:
 | 
			
		||||
      return new Color(lowCh, highCh, lowCh)
 | 
			
		||||
  }
 | 
			
		||||
  return new Color(lowCh, lowCh, lowCh)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -225,7 +225,7 @@ export const Stream = () => {
 | 
			
		||||
      },
 | 
			
		||||
    })
 | 
			
		||||
    if (state.matches('Sketch')) return
 | 
			
		||||
    if (state.matches('Sketch no face')) return
 | 
			
		||||
    if (state.matches('idle.showPlanes')) return
 | 
			
		||||
 | 
			
		||||
    if (!context.store?.didDragInStream && btnName(e).left) {
 | 
			
		||||
      sendSelectEventToEngine(
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,17 @@
 | 
			
		||||
import { useEffect } from 'react'
 | 
			
		||||
import { editorManager, engineCommandManager } from 'lib/singletons'
 | 
			
		||||
import {
 | 
			
		||||
  editorManager,
 | 
			
		||||
  engineCommandManager,
 | 
			
		||||
  kclManager,
 | 
			
		||||
  sceneInfra,
 | 
			
		||||
} from 'lib/singletons'
 | 
			
		||||
import { useModelingContext } from './useModelingContext'
 | 
			
		||||
import { getEventForSelectWithPoint } from 'lib/selections'
 | 
			
		||||
import { DefaultPlaneStr, getFaceDetails } from 'clientSideScene/sceneEntities'
 | 
			
		||||
import { getNodePathFromSourceRange } from 'lang/queryAst'
 | 
			
		||||
 | 
			
		||||
export function useEngineConnectionSubscriptions() {
 | 
			
		||||
  const { send, context } = useModelingContext()
 | 
			
		||||
  const { send, context, state } = useModelingContext()
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (!engineCommandManager) return
 | 
			
		||||
@ -40,4 +47,135 @@ export function useEngineConnectionSubscriptions() {
 | 
			
		||||
      unSubClick()
 | 
			
		||||
    }
 | 
			
		||||
  }, [engineCommandManager, context?.sketchEnginePathId])
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const unSub = engineCommandManager.subscribeTo({
 | 
			
		||||
      event: 'select_with_point',
 | 
			
		||||
      callback: state.matches('Sketch no face')
 | 
			
		||||
        ? async ({ data }) => {
 | 
			
		||||
            let planeId = data.entity_id
 | 
			
		||||
            if (!planeId) return
 | 
			
		||||
            if (
 | 
			
		||||
              engineCommandManager.defaultPlanes?.xy === planeId ||
 | 
			
		||||
              engineCommandManager.defaultPlanes?.xz === planeId ||
 | 
			
		||||
              engineCommandManager.defaultPlanes?.yz === planeId ||
 | 
			
		||||
              engineCommandManager.defaultPlanes?.negXy === planeId ||
 | 
			
		||||
              engineCommandManager.defaultPlanes?.negXz === planeId ||
 | 
			
		||||
              engineCommandManager.defaultPlanes?.negYz === planeId
 | 
			
		||||
            ) {
 | 
			
		||||
              const defaultPlaneStrMap: Record<string, DefaultPlaneStr> = {
 | 
			
		||||
                [engineCommandManager.defaultPlanes.xy]: 'XY',
 | 
			
		||||
                [engineCommandManager.defaultPlanes.xz]: 'XZ',
 | 
			
		||||
                [engineCommandManager.defaultPlanes.yz]: 'YZ',
 | 
			
		||||
                [engineCommandManager.defaultPlanes.negXy]: '-XY',
 | 
			
		||||
                [engineCommandManager.defaultPlanes.negXz]: '-XZ',
 | 
			
		||||
                [engineCommandManager.defaultPlanes.negYz]: '-YZ',
 | 
			
		||||
              }
 | 
			
		||||
              // TODO can we get this information from rust land when it creates the default planes?
 | 
			
		||||
              // maybe returned from make_default_planes (src/wasm-lib/src/wasm.rs)
 | 
			
		||||
              let zAxis: [number, number, number] = [0, 0, 1]
 | 
			
		||||
              let yAxis: [number, number, number] = [0, 1, 0]
 | 
			
		||||
 | 
			
		||||
              // get unit vector from camera position to target
 | 
			
		||||
              const camVector = sceneInfra.camControls.camera.position
 | 
			
		||||
                .clone()
 | 
			
		||||
                .sub(sceneInfra.camControls.target)
 | 
			
		||||
 | 
			
		||||
              if (engineCommandManager.defaultPlanes?.xy === planeId) {
 | 
			
		||||
                zAxis = [0, 0, 1]
 | 
			
		||||
                yAxis = [0, 1, 0]
 | 
			
		||||
                if (camVector.z < 0) {
 | 
			
		||||
                  zAxis = [0, 0, -1]
 | 
			
		||||
                  planeId = engineCommandManager.defaultPlanes?.negXy || ''
 | 
			
		||||
                }
 | 
			
		||||
              } else if (engineCommandManager.defaultPlanes?.yz === planeId) {
 | 
			
		||||
                zAxis = [1, 0, 0]
 | 
			
		||||
                yAxis = [0, 0, 1]
 | 
			
		||||
                if (camVector.x < 0) {
 | 
			
		||||
                  zAxis = [-1, 0, 0]
 | 
			
		||||
                  planeId = engineCommandManager.defaultPlanes?.negYz || ''
 | 
			
		||||
                }
 | 
			
		||||
              } else if (engineCommandManager.defaultPlanes?.xz === planeId) {
 | 
			
		||||
                zAxis = [0, 1, 0]
 | 
			
		||||
                yAxis = [0, 0, 1]
 | 
			
		||||
                planeId = engineCommandManager.defaultPlanes?.negXz || ''
 | 
			
		||||
                if (camVector.y < 0) {
 | 
			
		||||
                  zAxis = [0, -1, 0]
 | 
			
		||||
                  planeId = engineCommandManager.defaultPlanes?.xz || ''
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              sceneInfra.modelingSend({
 | 
			
		||||
                type: 'Select default plane',
 | 
			
		||||
                data: {
 | 
			
		||||
                  type: 'defaultPlane',
 | 
			
		||||
                  planeId: planeId,
 | 
			
		||||
                  plane: defaultPlaneStrMap[planeId],
 | 
			
		||||
                  zAxis,
 | 
			
		||||
                  yAxis,
 | 
			
		||||
                },
 | 
			
		||||
              })
 | 
			
		||||
              return
 | 
			
		||||
            }
 | 
			
		||||
            const artifact = engineCommandManager.artifactMap[planeId]
 | 
			
		||||
            console.log('artifact', artifact)
 | 
			
		||||
            // If we clicked on an extrude wall, we climb up the parent Id
 | 
			
		||||
            // to get the sketch profile's face ID. If we clicked on an endcap,
 | 
			
		||||
            // we already have it.
 | 
			
		||||
            const pathId =
 | 
			
		||||
              artifact?.type === 'extrudeWall' ||
 | 
			
		||||
              artifact?.type === 'extrudeCap'
 | 
			
		||||
                ? artifact.pathId
 | 
			
		||||
                : ''
 | 
			
		||||
 | 
			
		||||
            const path = engineCommandManager.artifactMap?.[pathId || '']
 | 
			
		||||
            const extrusionId =
 | 
			
		||||
              path?.type === 'startPath' ? path.extrusionIds[0] : ''
 | 
			
		||||
 | 
			
		||||
            // TODO: We get the first extrusion command ID,
 | 
			
		||||
            // which is fine while backend systems only support one extrusion.
 | 
			
		||||
            // but we need to more robustly handle resolving to the correct extrusion
 | 
			
		||||
            // if there are multiple.
 | 
			
		||||
            const extrusions = engineCommandManager.artifactMap?.[extrusionId]
 | 
			
		||||
 | 
			
		||||
            if (
 | 
			
		||||
              artifact?.type !== 'extrudeCap' &&
 | 
			
		||||
              artifact?.type !== 'extrudeWall'
 | 
			
		||||
            )
 | 
			
		||||
              return
 | 
			
		||||
 | 
			
		||||
            const faceInfo = await getFaceDetails(planeId)
 | 
			
		||||
            if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis)
 | 
			
		||||
              return
 | 
			
		||||
            const { z_axis, y_axis, origin } = faceInfo
 | 
			
		||||
            const sketchPathToNode = getNodePathFromSourceRange(
 | 
			
		||||
              kclManager.ast,
 | 
			
		||||
              artifact.range
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            const extrudePathToNode = extrusions?.range
 | 
			
		||||
              ? getNodePathFromSourceRange(kclManager.ast, extrusions.range)
 | 
			
		||||
              : []
 | 
			
		||||
 | 
			
		||||
            sceneInfra.modelingSend({
 | 
			
		||||
              type: 'Select default plane',
 | 
			
		||||
              data: {
 | 
			
		||||
                type: 'extrudeFace',
 | 
			
		||||
                zAxis: [z_axis.x, z_axis.y, z_axis.z],
 | 
			
		||||
                yAxis: [y_axis.x, y_axis.y, y_axis.z],
 | 
			
		||||
                position: [origin.x, origin.y, origin.z].map(
 | 
			
		||||
                  (num) => num / sceneInfra._baseUnitMultiplier
 | 
			
		||||
                ) as [number, number, number],
 | 
			
		||||
                sketchPathToNode,
 | 
			
		||||
                extrudePathToNode,
 | 
			
		||||
                cap: artifact.type === 'extrudeCap' ? artifact.cap : 'none',
 | 
			
		||||
                faceId: planeId,
 | 
			
		||||
              },
 | 
			
		||||
            })
 | 
			
		||||
            return
 | 
			
		||||
          }
 | 
			
		||||
        : () => {},
 | 
			
		||||
    })
 | 
			
		||||
    return unSub
 | 
			
		||||
  }, [state])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ import { Program, SourceRange } from 'lang/wasm'
 | 
			
		||||
import { VITE_KC_API_WS_MODELING_URL } from 'env'
 | 
			
		||||
import { Models } from '@kittycad/lib'
 | 
			
		||||
import { exportSave } from 'lib/exportSave'
 | 
			
		||||
import { uuidv4 } from 'lib/utils'
 | 
			
		||||
import { deferExecution, uuidv4 } from 'lib/utils'
 | 
			
		||||
import { Themes, getThemeColorForEngine, getOppositeTheme } from 'lib/theme'
 | 
			
		||||
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
 | 
			
		||||
import {
 | 
			
		||||
@ -12,6 +12,7 @@ import {
 | 
			
		||||
  ResponseMap,
 | 
			
		||||
  createArtifactMap,
 | 
			
		||||
} from 'lang/std/artifactMap'
 | 
			
		||||
import { useModelingContext } from 'hooks/useModelingContext'
 | 
			
		||||
 | 
			
		||||
// TODO(paultag): This ought to be tweakable.
 | 
			
		||||
const pingIntervalMs = 10000
 | 
			
		||||
@ -1204,6 +1205,8 @@ export class EngineCommandManager extends EventTarget {
 | 
			
		||||
  private onEngineConnectionNewTrack = ({
 | 
			
		||||
    detail,
 | 
			
		||||
  }: CustomEvent<NewTrackArgs>) => {}
 | 
			
		||||
  modelingSend: ReturnType<typeof useModelingContext>['send'] =
 | 
			
		||||
    (() => {}) as any
 | 
			
		||||
 | 
			
		||||
  start({
 | 
			
		||||
    restart,
 | 
			
		||||
@ -1549,7 +1552,6 @@ export class EngineCommandManager extends EventTarget {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  async startNewSession() {
 | 
			
		||||
    this.artifactMap = {}
 | 
			
		||||
    this.orderedCommands = []
 | 
			
		||||
    this.responseMap = {}
 | 
			
		||||
    await this.initPlanes()
 | 
			
		||||
@ -1784,6 +1786,14 @@ export class EngineCommandManager extends EventTarget {
 | 
			
		||||
    this.engineConnection?.send(message.command)
 | 
			
		||||
    return promise
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  deferredArtifactPopulated = deferExecution((a?: null) => {
 | 
			
		||||
    this.modelingSend({ type: 'Artifact graph populated' })
 | 
			
		||||
  }, 200)
 | 
			
		||||
  deferredArtifactEmptied = deferExecution((a?: null) => {
 | 
			
		||||
    this.modelingSend({ type: 'Artifact graph emptied' })
 | 
			
		||||
  }, 200)
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * When an execution takes place we want to wait until we've got replies for all of the commands
 | 
			
		||||
   * When this is done when we build the artifact map synchronously.
 | 
			
		||||
@ -1795,21 +1805,16 @@ export class EngineCommandManager extends EventTarget {
 | 
			
		||||
      responseMap: this.responseMap,
 | 
			
		||||
      ast: this.getAst(),
 | 
			
		||||
    })
 | 
			
		||||
    if (Object.values(this.artifactMap).length) {
 | 
			
		||||
      this.deferredArtifactEmptied(null)
 | 
			
		||||
    } else {
 | 
			
		||||
      this.deferredArtifactPopulated(null)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  private async initPlanes() {
 | 
			
		||||
    if (this.planesInitialized()) return
 | 
			
		||||
    const planes = await this.makeDefaultPlanes()
 | 
			
		||||
    this.defaultPlanes = planes
 | 
			
		||||
 | 
			
		||||
    this.subscribeTo({
 | 
			
		||||
      event: 'select_with_point',
 | 
			
		||||
      callback: ({ data }) => {
 | 
			
		||||
        if (!data?.entity_id) return
 | 
			
		||||
        if (!planes) return
 | 
			
		||||
        if (![planes.xy, planes.yz, planes.xz].includes(data.entity_id)) return
 | 
			
		||||
        this.onPlaneSelectCallback(data.entity_id)
 | 
			
		||||
      },
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
  planesInitialized(): boolean {
 | 
			
		||||
    return (
 | 
			
		||||
@ -1820,11 +1825,6 @@ export class EngineCommandManager extends EventTarget {
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onPlaneSelectCallback = (id: string) => {}
 | 
			
		||||
  onPlaneSelected(callback: (id: string) => void) {
 | 
			
		||||
    this.onPlaneSelectCallback = callback
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async setPlaneHidden(id: string, hidden: boolean) {
 | 
			
		||||
    return await this.sendSceneCommand({
 | 
			
		||||
      type: 'modeling_cmd_req',
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Reference in New Issue
	
	Block a user