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()
|
const newCmdId = uuidv4()
|
||||||
if (context.store?.buttonDownInStream === undefined) {
|
if (state.matches('idle.showPlanes')) return
|
||||||
debounceSocketSend({
|
if (context.store?.buttonDownInStream !== undefined) return
|
||||||
type: 'modeling_cmd_req',
|
debounceSocketSend({
|
||||||
cmd: {
|
type: 'modeling_cmd_req',
|
||||||
type: 'highlight_set_entity',
|
cmd: {
|
||||||
selected_at_window: { x, y },
|
type: 'highlight_set_entity',
|
||||||
},
|
selected_at_window: { x, y },
|
||||||
cmd_id: newCmdId,
|
},
|
||||||
})
|
cmd_id: newCmdId,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -102,6 +102,7 @@ export const ClientSideScene = ({
|
|||||||
canvas.addEventListener('mousedown', sceneInfra.onMouseDown, false)
|
canvas.addEventListener('mousedown', sceneInfra.onMouseDown, false)
|
||||||
canvas.addEventListener('mouseup', sceneInfra.onMouseUp, false)
|
canvas.addEventListener('mouseup', sceneInfra.onMouseUp, false)
|
||||||
sceneInfra.setSend(send)
|
sceneInfra.setSend(send)
|
||||||
|
engineCommandManager.modelingSend = send
|
||||||
return () => {
|
return () => {
|
||||||
canvas?.removeEventListener('mousemove', sceneInfra.onMouseMove)
|
canvas?.removeEventListener('mousemove', sceneInfra.onMouseMove)
|
||||||
canvas?.removeEventListener('mousedown', sceneInfra.onMouseDown)
|
canvas?.removeEventListener('mousedown', sceneInfra.onMouseDown)
|
||||||
|
@ -22,9 +22,6 @@ import {
|
|||||||
import {
|
import {
|
||||||
ARROWHEAD,
|
ARROWHEAD,
|
||||||
AXIS_GROUP,
|
AXIS_GROUP,
|
||||||
DEFAULT_PLANES,
|
|
||||||
DefaultPlane,
|
|
||||||
defaultPlaneColor,
|
|
||||||
getSceneScale,
|
getSceneScale,
|
||||||
INTERSECTION_PLANE_LAYER,
|
INTERSECTION_PLANE_LAYER,
|
||||||
OnClickCallbackArgs,
|
OnClickCallbackArgs,
|
||||||
@ -202,6 +199,7 @@ export class SceneEntities {
|
|||||||
|
|
||||||
createIntersectionPlane() {
|
createIntersectionPlane() {
|
||||||
if (sceneInfra.scene.getObjectByName(RAYCASTABLE_PLANE)) {
|
if (sceneInfra.scene.getObjectByName(RAYCASTABLE_PLANE)) {
|
||||||
|
// this.removeIntersectionPlane()
|
||||||
console.warn('createIntersectionPlane called when it already exists')
|
console.warn('createIntersectionPlane called when it already exists')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1502,146 +1500,6 @@ export class SceneEntities {
|
|||||||
this._tearDownSketch(0, resolve, reject, { removeAxis })
|
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() {
|
mouseEnterLeaveCallbacks() {
|
||||||
return {
|
return {
|
||||||
onMouseEnter: ({ selected, dragSelected }: OnMouseEnterLeaveArgs) => {
|
onMouseEnter: ({ selected, dragSelected }: OnMouseEnterLeaveArgs) => {
|
||||||
|
@ -11,10 +11,8 @@ import {
|
|||||||
Raycaster,
|
Raycaster,
|
||||||
Vector2,
|
Vector2,
|
||||||
Group,
|
Group,
|
||||||
PlaneGeometry,
|
|
||||||
MeshBasicMaterial,
|
MeshBasicMaterial,
|
||||||
Mesh,
|
Mesh,
|
||||||
DoubleSide,
|
|
||||||
Intersection,
|
Intersection,
|
||||||
Object3D,
|
Object3D,
|
||||||
Object3DEventMap,
|
Object3DEventMap,
|
||||||
@ -48,7 +46,6 @@ export const DEBUG_SHOW_INTERSECTION_PLANE: false = false
|
|||||||
export const DEBUG_SHOW_BOTH_SCENES: false = false
|
export const DEBUG_SHOW_BOTH_SCENES: false = false
|
||||||
|
|
||||||
export const RAYCASTABLE_PLANE = 'raycastable-plane'
|
export const RAYCASTABLE_PLANE = 'raycastable-plane'
|
||||||
export const DEFAULT_PLANES = 'default-planes'
|
|
||||||
|
|
||||||
export const X_AXIS = 'xAxis'
|
export const X_AXIS = 'xAxis'
|
||||||
export const Y_AXIS = 'yAxis'
|
export const Y_AXIS = 'yAxis'
|
||||||
@ -325,16 +322,9 @@ export class SceneInfra {
|
|||||||
this.camControls.camera,
|
this.camControls.camera,
|
||||||
this.camControls.target
|
this.camControls.target
|
||||||
)
|
)
|
||||||
const planesGroup = this.scene.getObjectByName(DEFAULT_PLANES)
|
|
||||||
const axisGroup = this.scene
|
const axisGroup = this.scene
|
||||||
.getObjectByName(AXIS_GROUP)
|
.getObjectByName(AXIS_GROUP)
|
||||||
?.getObjectByName('gridHelper')
|
?.getObjectByName('gridHelper')
|
||||||
planesGroup &&
|
|
||||||
planesGroup.scale.set(
|
|
||||||
scale / this._baseUnitMultiplier,
|
|
||||||
scale / this._baseUnitMultiplier,
|
|
||||||
scale / this._baseUnitMultiplier
|
|
||||||
)
|
|
||||||
axisGroup?.name === 'gridHelper' && axisGroup.scale.set(scale, scale, scale)
|
axisGroup?.name === 'gridHelper' && axisGroup.scale.set(scale, scale, scale)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -632,59 +622,6 @@ export class SceneInfra {
|
|||||||
this.onClickCallback({ mouseEvent, intersects })
|
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[]) => {
|
updateOtherSelectionColors = (otherSelections: Axis[]) => {
|
||||||
const axisGroup = this.scene.children.find(
|
const axisGroup = this.scene.children.find(
|
||||||
({ userData }) => userData?.type === AXIS_GROUP
|
({ userData }) => userData?.type === AXIS_GROUP
|
||||||
@ -742,28 +679,3 @@ function baseUnitTomm(baseUnit: BaseUnit) {
|
|||||||
return 914.4
|
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')) return
|
||||||
if (state.matches('Sketch no face')) return
|
if (state.matches('idle.showPlanes')) return
|
||||||
|
|
||||||
if (!context.store?.didDragInStream && btnName(e).left) {
|
if (!context.store?.didDragInStream && btnName(e).left) {
|
||||||
sendSelectEventToEngine(
|
sendSelectEventToEngine(
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { editorManager, engineCommandManager } from 'lib/singletons'
|
import {
|
||||||
|
editorManager,
|
||||||
|
engineCommandManager,
|
||||||
|
kclManager,
|
||||||
|
sceneInfra,
|
||||||
|
} from 'lib/singletons'
|
||||||
import { useModelingContext } from './useModelingContext'
|
import { useModelingContext } from './useModelingContext'
|
||||||
import { getEventForSelectWithPoint } from 'lib/selections'
|
import { getEventForSelectWithPoint } from 'lib/selections'
|
||||||
|
import { DefaultPlaneStr, getFaceDetails } from 'clientSideScene/sceneEntities'
|
||||||
|
import { getNodePathFromSourceRange } from 'lang/queryAst'
|
||||||
|
|
||||||
export function useEngineConnectionSubscriptions() {
|
export function useEngineConnectionSubscriptions() {
|
||||||
const { send, context } = useModelingContext()
|
const { send, context, state } = useModelingContext()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!engineCommandManager) return
|
if (!engineCommandManager) return
|
||||||
@ -40,4 +47,135 @@ export function useEngineConnectionSubscriptions() {
|
|||||||
unSubClick()
|
unSubClick()
|
||||||
}
|
}
|
||||||
}, [engineCommandManager, context?.sketchEnginePathId])
|
}, [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 { VITE_KC_API_WS_MODELING_URL } from 'env'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import { exportSave } from 'lib/exportSave'
|
import { exportSave } from 'lib/exportSave'
|
||||||
import { uuidv4 } from 'lib/utils'
|
import { deferExecution, uuidv4 } from 'lib/utils'
|
||||||
import { Themes, getThemeColorForEngine, getOppositeTheme } from 'lib/theme'
|
import { Themes, getThemeColorForEngine, getOppositeTheme } from 'lib/theme'
|
||||||
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
||||||
import {
|
import {
|
||||||
@ -12,6 +12,7 @@ import {
|
|||||||
ResponseMap,
|
ResponseMap,
|
||||||
createArtifactMap,
|
createArtifactMap,
|
||||||
} from 'lang/std/artifactMap'
|
} from 'lang/std/artifactMap'
|
||||||
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
|
|
||||||
// TODO(paultag): This ought to be tweakable.
|
// TODO(paultag): This ought to be tweakable.
|
||||||
const pingIntervalMs = 10000
|
const pingIntervalMs = 10000
|
||||||
@ -1204,6 +1205,8 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
private onEngineConnectionNewTrack = ({
|
private onEngineConnectionNewTrack = ({
|
||||||
detail,
|
detail,
|
||||||
}: CustomEvent<NewTrackArgs>) => {}
|
}: CustomEvent<NewTrackArgs>) => {}
|
||||||
|
modelingSend: ReturnType<typeof useModelingContext>['send'] =
|
||||||
|
(() => {}) as any
|
||||||
|
|
||||||
start({
|
start({
|
||||||
restart,
|
restart,
|
||||||
@ -1549,7 +1552,6 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
async startNewSession() {
|
async startNewSession() {
|
||||||
this.artifactMap = {}
|
|
||||||
this.orderedCommands = []
|
this.orderedCommands = []
|
||||||
this.responseMap = {}
|
this.responseMap = {}
|
||||||
await this.initPlanes()
|
await this.initPlanes()
|
||||||
@ -1784,6 +1786,14 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
this.engineConnection?.send(message.command)
|
this.engineConnection?.send(message.command)
|
||||||
return promise
|
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 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.
|
* When this is done when we build the artifact map synchronously.
|
||||||
@ -1795,21 +1805,16 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
responseMap: this.responseMap,
|
responseMap: this.responseMap,
|
||||||
ast: this.getAst(),
|
ast: this.getAst(),
|
||||||
})
|
})
|
||||||
|
if (Object.values(this.artifactMap).length) {
|
||||||
|
this.deferredArtifactEmptied(null)
|
||||||
|
} else {
|
||||||
|
this.deferredArtifactPopulated(null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
private async initPlanes() {
|
private async initPlanes() {
|
||||||
if (this.planesInitialized()) return
|
if (this.planesInitialized()) return
|
||||||
const planes = await this.makeDefaultPlanes()
|
const planes = await this.makeDefaultPlanes()
|
||||||
this.defaultPlanes = planes
|
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 {
|
planesInitialized(): boolean {
|
||||||
return (
|
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) {
|
async setPlaneHidden(id: string, hidden: boolean) {
|
||||||
return await this.sendSceneCommand({
|
return await this.sendSceneCommand({
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
|
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user