diff --git a/src/clientSideScene/sceneEntities.ts b/src/clientSideScene/sceneEntities.ts index be9430f84..f263546a6 100644 --- a/src/clientSideScene/sceneEntities.ts +++ b/src/clientSideScene/sceneEntities.ts @@ -22,9 +22,6 @@ import { import { ARROWHEAD, AXIS_GROUP, - DEFAULT_PLANES, - DefaultPlane, - defaultPlaneColor, getSceneScale, INTERSECTION_PLANE_LAYER, RAYCASTABLE_PLANE, @@ -1301,150 +1298,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 = { - [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 targetId = - 'additionalData' in artifact && - artifact.additionalData?.type === 'cap' - ? _entity_id - : artifact.parentId - - // tsc cannot infer that target can have extrusions - // from the commandType (why?) so we need to cast it - const target = this.engineCommandManager.artifactMap?.[ - targetId || '' - ] as ArtifactMapCommand & { extrusions?: string[] } - - // 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?.[target?.extrusions?.[0] || ''] - - if (artifact?.commandType !== 'solid3d_get_extrusion_face_info') 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?.additionalData?.type === 'cap' - ? artifact.additionalData.info - : 'none', - faceId: _entity_id, - }, - }) - return - }, - }) - } mouseEnterLeaveCallbacks() { return { onMouseEnter: ({ selected, dragSelected }: OnMouseEnterLeaveArgs) => { diff --git a/src/clientSideScene/sceneInfra.ts b/src/clientSideScene/sceneInfra.ts index c507618a6..1c2af1e3c 100644 --- a/src/clientSideScene/sceneInfra.ts +++ b/src/clientSideScene/sceneInfra.ts @@ -591,59 +591,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 @@ -701,28 +648,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) -} diff --git a/src/components/Stream.tsx b/src/components/Stream.tsx index 31a55e831..3fc38e315 100644 --- a/src/components/Stream.tsx +++ b/src/components/Stream.tsx @@ -109,7 +109,6 @@ export const Stream = () => { }, }) if (state.matches('Sketch')) return - if (state.matches('Sketch no face')) return if (!context.store?.didDragInStream && butName(e).left) { sendSelectEventToEngine( diff --git a/src/hooks/useEngineConnectionSubscriptions.ts b/src/hooks/useEngineConnectionSubscriptions.ts index 3c6dda057..aa460f173 100644 --- a/src/hooks/useEngineConnectionSubscriptions.ts +++ b/src/hooks/useEngineConnectionSubscriptions.ts @@ -4,7 +4,7 @@ import { useModelingContext } from './useModelingContext' import { getEventForSelectWithPoint } from 'lib/selections' export function useEngineConnectionSubscriptions() { - const { send, context } = useModelingContext() + const { send, state } = useModelingContext() useEffect(() => { if (!engineCommandManager) return @@ -29,9 +29,7 @@ export function useEngineConnectionSubscriptions() { const unSubClick = engineCommandManager.subscribeTo({ event: 'select_with_point', callback: async (engineEvent) => { - const event = await getEventForSelectWithPoint(engineEvent, { - sketchEnginePathId: context.sketchEnginePathId, - }) + const event = await getEventForSelectWithPoint(engineEvent, state) event && send(event) }, }) @@ -39,5 +37,5 @@ export function useEngineConnectionSubscriptions() { unSubHover() unSubClick() } - }, [engineCommandManager, context?.sketchEnginePathId]) + }, [engineCommandManager, state]) } diff --git a/src/lib/selections.ts b/src/lib/selections.ts index 5ae4031f0..1fd16dccb 100644 --- a/src/lib/selections.ts +++ b/src/lib/selections.ts @@ -4,6 +4,7 @@ import { engineCommandManager, kclManager, sceneEntitiesManager, + sceneInfra, } from 'lib/singletons' import { CallExpression, SourceRange, Value, parse, recast } from 'lang/wasm' import { ModelingMachineEvent } from 'machines/modelingMachine' @@ -15,6 +16,7 @@ import { Program } from 'lang/wasm' import { doesPipeHaveCallExp, getNodeFromPath, + getNodePathFromSourceRange, hasSketchPipeBeenExtruded, isSingleCursorInPipe, } from 'lang/queryAst' @@ -24,11 +26,15 @@ import { TANGENTIAL_ARC_TO_SEGMENT, getParentGroup, PROFILE_START, + getFaceDetails, + DefaultPlaneStr, } from 'clientSideScene/sceneEntities' import { Mesh, Object3D, Object3DEventMap } from 'three' import { AXIS_GROUP, X_AXIS } from 'clientSideScene/sceneInfra' import { PathToNodeMap } from 'lang/std/sketchcombos' import { err } from 'lib/trap' +import { useModelingContext } from 'hooks/useModelingContext' +import { ArtifactMapCommand } from 'lang/std/engineConnection' export const X_AXIS_UUID = 'ad792545-7fd3-482a-a602-a93924e3055b' export const Y_AXIS_UUID = '680fd157-266f-4b8a-984f-cdf46b8bdf01' @@ -62,8 +68,12 @@ export async function getEventForSelectWithPoint( Models['OkModelingCmdResponse_type'], { type: 'select_with_point' } >, - { sketchEnginePathId }: { sketchEnginePathId?: string } + state: ReturnType['state'] ): Promise { + if (state.matches('Sketch no face')) + return handleSelectionInSketchNoFace(data?.entity_id || '') + + // assumes XState state is `idle` from this point if (!data?.entity_id) { return { type: 'Set selection', @@ -143,6 +153,128 @@ export async function getEventForSelectWithPoint( } } +async function handleSelectionInSketchNoFace( + entity_id: string +): Promise { + let _entity_id = entity_id + if (!_entity_id) return Promise.resolve(null) + 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 = { + [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 || '' + } + } + + return { + type: 'Select default plane', + data: { + type: 'defaultPlane', + planeId: _entity_id, + plane: defaultPlaneStrMap[_entity_id], + zAxis, + yAxis, + }, + } + } + const artifact = 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 targetId = + 'additionalData' in artifact && artifact.additionalData?.type === 'cap' + ? _entity_id + : artifact.parentId + + // tsc cannot infer that target can have extrusions + // from the commandType (why?) so we need to cast it + const target = engineCommandManager.artifactMap?.[ + targetId || '' + ] as ArtifactMapCommand & { extrusions?: string[] } + + // 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?.[target?.extrusions?.[0] || ''] + + if (artifact?.commandType !== 'solid3d_get_extrusion_face_info') return null + + const faceInfo = await getFaceDetails(_entity_id) + if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis) return null + const { z_axis, y_axis, origin } = faceInfo + const sketchPathToNode = getNodePathFromSourceRange( + kclManager.ast, + artifact.range + ) + + const extrudePathToNode = extrusions?.range + ? getNodePathFromSourceRange(kclManager.ast, extrusions.range) + : [] + + return { + 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?.additionalData?.type === 'cap' + ? artifact.additionalData.info + : 'none', + faceId: _entity_id, + }, + } +} + export function getEventForSegmentSelection( obj: Object3D ): ModelingMachineEvent | null { diff --git a/src/machines/modelingMachine.ts b/src/machines/modelingMachine.ts index 5d269631c..b28fec375 100644 --- a/src/machines/modelingMachine.ts +++ b/src/machines/modelingMachine.ts @@ -262,7 +262,6 @@ export interface ModelingMachineContext { selectionRanges: Selections sketchDetails: null | SketchDetails sketchPlaneId: string - sketchEnginePathId: string moveDescs: MoveDesc[] mouseState: MouseState segmentOverlays: SegmentOverlays @@ -293,7 +292,6 @@ export const modelingMachine = createMachine( origin: [0, 0, 0], }, sketchPlaneId: '', - sketchEnginePathId: '', moveDescs: [], mouseState: { type: 'idle' }, segmentOverlays: {}, @@ -1013,12 +1011,10 @@ export const modelingMachine = createMachine( } }), 'hide default planes': () => { - sceneInfra.removeDefaultPlanes() kclManager.hidePlanes() }, 'reset sketch metadata': assign({ sketchDetails: null, - sketchEnginePathId: '', sketchPlaneId: '', }), 'set new sketch metadata': assign((_, { data }) => ({ @@ -1173,8 +1169,6 @@ export const modelingMachine = createMachine( ) }, 'show default planes': () => { - sceneInfra.showDefaultPlanes() - sceneEntitiesManager.setupDefaultPlaneHover() kclManager.showPlanes() }, 'setup noPoints onClick listener': ({ sketchDetails }) => {