Compare commits

...

6 Commits

Author SHA1 Message Date
1953ca0607 WIP: Fix setUpDraftSegment to work without a variable
This isn't finished.  It needs to look up the SketchGroup coordinates
without a variable.
2024-08-08 15:49:15 -04:00
cb9fa71645 Fix modifyAst() to work with top-level expression statements 2024-08-08 15:49:15 -04:00
a7f0a5607d WIP: Enable Edit Sketch without variable declaration 2024-08-08 15:49:15 -04:00
eb28ed6cbf WIP: Add saving artifact from click and getting plane 2024-08-08 15:49:15 -04:00
3c84ef8592 Add id to all artifact types 2024-08-08 15:49:15 -04:00
9b966de7f0 Add ArtifactId type 2024-08-08 15:49:15 -04:00
6 changed files with 223 additions and 57 deletions

View File

@ -84,7 +84,11 @@ import {
createPipeSubstitution, createPipeSubstitution,
findUniqueName, findUniqueName,
} from 'lang/modifyAst' } from 'lang/modifyAst'
import { Selections, getEventForSegmentSelection } from 'lib/selections' import {
Selection,
Selections,
getEventForSegmentSelection,
} from 'lib/selections'
import { getTangentPointFromPreviousArc } from 'lib/utils2d' import { getTangentPointFromPreviousArc } from 'lib/utils2d'
import { createGridHelper, orthoScale, perspScale } from './helpers' import { createGridHelper, orthoScale, perspScale } from './helpers'
import { Models } from '@kittycad/lib' import { Models } from '@kittycad/lib'
@ -98,6 +102,11 @@ import {
import { getThemeColorForThreeJs } from 'lib/theme' import { getThemeColorForThreeJs } from 'lib/theme'
import { err, trap } from 'lib/trap' import { err, trap } from 'lib/trap'
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer' import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
import {
ArtifactGraph,
ArtifactId,
getPlaneOrFaceFromSelection,
} from 'lang/std/artifactGraph'
type DraftSegment = 'line' | 'tangentialArcTo' type DraftSegment = 'line' | 'tangentialArcTo'
@ -582,15 +591,20 @@ export class SceneEntities {
const _node1 = getNodeFromPath<VariableDeclaration>( const _node1 = getNodeFromPath<VariableDeclaration>(
_ast, _ast,
sketchPathToNode || [], sketchPathToNode || [],
'VariableDeclaration' ['VariableDeclaration', 'ExpressionStatement']
) ) as { node: { type: string } } | Error
if (trap(_node1)) return Promise.reject(_node1) if (trap(_node1)) return Promise.reject(_node1)
const variableDeclarationName = const variableDeclarationName = (_node1.node.type === 'VariableDeclaration') ?
_node1.node?.declarations?.[0]?.id?.name || '' (_node1.node as VariableDeclaration).declarations[0]?.id?.name || '' :
''
const sg = kclManager.programMemory.get( const sgMemItem = kclManager.programMemory.get(
variableDeclarationName variableDeclarationName
) as SketchGroup )
if (sgMemItem?.type !== 'SketchGroup') {
return Promise.reject(new Error('SketchGroup not found in programMemory'))
}
const sg: SketchGroup = sgMemItem
const lastSeg = sg.value.slice(-1)[0] || sg.start const lastSeg = sg.value.slice(-1)[0] || sg.start
const index = sg.value.length // because we've added a new segment that's not in the memory yet, no need for `-1` const index = sg.value.length // because we've added a new segment that's not in the memory yet, no need for `-1`
@ -1762,6 +1776,33 @@ export function getParentGroup(
return null return null
} }
export async function planeOrFaceFromSelection({
artifactGraph,
selection,
}: {
artifactGraph: ArtifactGraph
selection: Selection
}): Promise<{
id: ArtifactId
faceDetails: Models['GetSketchModePlane_type']
} | null> {
// If the selection doesn't have an artifactId associated with it, we can't
// do it.
if (!selection.artifactId) return null
const planeOrFace = getPlaneOrFaceFromSelection(
selection.artifactId,
artifactGraph
)
if (!planeOrFace) return null
if (planeOrFace?.type === 'plane') {
const faceDetails = await getFaceDetails(planeOrFace.id)
return { id: planeOrFace.id, faceDetails }
}
// TODO: Handle wall or cap artifact.
return null
}
export function sketchGroupFromPathToNode({ export function sketchGroupFromPathToNode({
pathToNode, pathToNode,
ast, ast,
@ -1827,11 +1868,51 @@ export function getSketchQuaternion(
return getQuaternionFromZAxis(massageFormats(zAxis)) return getQuaternionFromZAxis(massageFormats(zAxis))
} }
export async function getSketchOrientationDetails( export async function getSketchOrientationDetails(
artifactGraph: ArtifactGraph,
selection: Selection,
sketchPathToNode: PathToNode sketchPathToNode: PathToNode
): Promise<{ ): Promise<{
quat: Quaternion sketchDetails: {
sketchDetails: SketchDetails & { faceId?: string } zAxis: [number, number, number]
yAxis: [number, number, number]
origin: [number, number, number]
faceId: string
}
}> { }> {
const plane = await planeOrFaceFromSelection({
artifactGraph,
selection,
})
if (plane) {
const details = plane.faceDetails
console.warn('Found plane', plane)
const zAxis: [number, number, number] = [
details.z_axis.x,
details.z_axis.y,
details.z_axis.z,
]
const yAxis: [number, number, number] = [
details.y_axis.x,
details.y_axis.y,
details.y_axis.z,
]
const origin: [number, number, number] = [
details.origin.x,
details.origin.y,
details.origin.z,
]
return {
sketchDetails: {
zAxis,
yAxis,
origin,
faceId: plane.id,
},
}
}
// We couldn't find the plane or face, so try to look at the AST, and find it
// through there.
const sketchGroup = sketchGroupFromPathToNode({ const sketchGroup = sketchGroupFromPathToNode({
pathToNode: sketchPathToNode, pathToNode: sketchPathToNode,
ast: kclManager.ast, ast: kclManager.ast,
@ -1843,9 +1924,7 @@ export async function getSketchOrientationDetails(
if (sketchGroup.on.type === 'plane') { if (sketchGroup.on.type === 'plane') {
const zAxis = sketchGroup?.on.zAxis const zAxis = sketchGroup?.on.zAxis
return { return {
quat: getQuaternionFromZAxis(massageFormats(zAxis)),
sketchDetails: { sketchDetails: {
sketchPathToNode,
zAxis: [zAxis.x, zAxis.y, zAxis.z], zAxis: [zAxis.x, zAxis.y, zAxis.z],
yAxis: [ yAxis: [
sketchGroup.on.yAxis.x, sketchGroup.on.yAxis.x,
@ -1864,14 +1943,8 @@ export async function getSketchOrientationDetails(
if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis) if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis)
return Promise.reject('face info') return Promise.reject('face info')
const { z_axis, y_axis, origin } = faceInfo const { z_axis, y_axis, origin } = faceInfo
const quaternion = quaternionFromUpNForward(
new Vector3(y_axis.x, y_axis.y, y_axis.z),
new Vector3(z_axis.x, z_axis.y, z_axis.z)
)
return { return {
quat: quaternion,
sketchDetails: { sketchDetails: {
sketchPathToNode,
zAxis: [z_axis.x, z_axis.y, z_axis.z], zAxis: [z_axis.x, z_axis.y, z_axis.z],
yAxis: [y_axis.x, y_axis.y, y_axis.z], yAxis: [y_axis.x, y_axis.y, y_axis.z],
origin: [origin.x, origin.y, origin.z], origin: [origin.x, origin.y, origin.z],

View File

@ -589,12 +589,17 @@ export const ModelingMachineProvider = ({
} }
}, },
'animate-to-sketch': async ({ selectionRanges }) => { 'animate-to-sketch': async ({ selectionRanges }) => {
const sourceRange = selectionRanges.codeBasedSelections[0].range const selection = selectionRanges.codeBasedSelections[0]
const sourceRange = selection.range
const sketchPathToNode = getNodePathFromSourceRange( const sketchPathToNode = getNodePathFromSourceRange(
kclManager.ast, kclManager.ast,
sourceRange sourceRange
) )
const info = await getSketchOrientationDetails(sketchPathToNode || []) const info = await getSketchOrientationDetails(
engineCommandManager.artifactGraph,
selection,
sketchPathToNode || []
)
await letEngineAnimateAndSyncCamAfter( await letEngineAnimateAndSyncCamAfter(
engineCommandManager, engineCommandManager,
info?.sketchDetails?.faceId || '' info?.sketchDetails?.faceId || ''

View File

@ -18,6 +18,7 @@ import {
ProgramMemory, ProgramMemory,
SourceRange, SourceRange,
SketchGroup, SketchGroup,
ExpressionStatement,
} from './wasm' } from './wasm'
import { import {
isNodeSafeToReplacePath, isNodeSafeToReplacePath,
@ -82,15 +83,22 @@ export function addStartProfileAt(
const _node1 = getNodeFromPath<VariableDeclaration>( const _node1 = getNodeFromPath<VariableDeclaration>(
node, node,
pathToNode, pathToNode,
'VariableDeclaration' ['VariableDeclaration', 'ExpressionStatement']
) ) as { node: { type: string } } | Error
if (err(_node1)) return _node1 if (err(_node1)) return _node1
const variableDeclaration = _node1.node
if (variableDeclaration.type !== 'VariableDeclaration') {
return new Error('variableDeclaration.init.type !== PipeExpression')
}
const _node = { ...node } const _node = { ...node }
const init = variableDeclaration.declarations[0].init let expr: Value
let variableDeclaration: VariableDeclaration | undefined
if (_node1.node.type === 'VariableDeclaration') {
const node: VariableDeclaration = _node1.node as VariableDeclaration
variableDeclaration = node
expr = node.declarations[0].init
} else if (_node1.node.type === 'ExpressionStatement') {
const node: ExpressionStatement = _node1.node as ExpressionStatement
expr = node.expression
} else {
return new Error(`Unrecognized node type ${_node1.node.type}`)
}
const startProfileAt = createCallExpressionStdLib('startProfileAt', [ const startProfileAt = createCallExpressionStdLib('startProfileAt', [
createArrayExpression([ createArrayExpression([
createLiteral(roundOff(at[0])), createLiteral(roundOff(at[0])),
@ -98,11 +106,11 @@ export function addStartProfileAt(
]), ]),
createPipeSubstitution(), createPipeSubstitution(),
]) ])
if (init.type === 'PipeExpression') { if (expr.type === 'PipeExpression') {
init.body.splice(1, 0, startProfileAt) expr.body.splice(1, 0, startProfileAt)
} else { } else if (variableDeclaration) {
variableDeclaration.declarations[0].init = createPipeExpression([ variableDeclaration.declarations[0].init = createPipeExpression([
init, expr,
startProfileAt, startProfileAt,
]) ])
} }

View File

@ -791,7 +791,7 @@ export function isSingleCursorInPipe(
const pathToNode = getNodePathFromSourceRange(ast, selection.range) const pathToNode = getNodePathFromSourceRange(ast, selection.range)
const nodeTypes = pathToNode.map(([, type]) => type) const nodeTypes = pathToNode.map(([, type]) => type)
if (nodeTypes.includes('FunctionExpression')) return false if (nodeTypes.includes('FunctionExpression')) return false
if (!nodeTypes.includes('VariableDeclaration')) return false // if (!nodeTypes.includes('VariableDeclaration')) return false
if (nodeTypes.includes('PipeExpression')) return true if (nodeTypes.includes('PipeExpression')) return true
return false return false
} }

View File

@ -3,6 +3,8 @@ import { Models } from '@kittycad/lib'
import { getNodePathFromSourceRange } from 'lang/queryAst' import { getNodePathFromSourceRange } from 'lang/queryAst'
import { err } from 'lib/trap' import { err } from 'lib/trap'
export type ArtifactId = string
interface CommonCommandProperties { interface CommonCommandProperties {
range: SourceRange range: SourceRange
pathToNode: PathToNode pathToNode: PathToNode
@ -10,17 +12,20 @@ interface CommonCommandProperties {
export interface PlaneArtifact { export interface PlaneArtifact {
type: 'plane' type: 'plane'
id: ArtifactId
pathIds: Array<string> pathIds: Array<string>
codeRef: CommonCommandProperties codeRef: CommonCommandProperties
} }
export interface PlaneArtifactRich { export interface PlaneArtifactRich {
type: 'plane' type: 'plane'
id: ArtifactId
paths: Array<PathArtifact> paths: Array<PathArtifact>
codeRef: CommonCommandProperties codeRef: CommonCommandProperties
} }
export interface PathArtifact { export interface PathArtifact {
type: 'path' type: 'path'
id: ArtifactId
planeId: string planeId: string
segIds: Array<string> segIds: Array<string>
extrusionId: string extrusionId: string
@ -30,10 +35,12 @@ export interface PathArtifact {
interface solid2D { interface solid2D {
type: 'solid2D' type: 'solid2D'
id: ArtifactId
pathId: string pathId: string
} }
export interface PathArtifactRich { export interface PathArtifactRich {
type: 'path' type: 'path'
id: ArtifactId
plane: PlaneArtifact | WallArtifact plane: PlaneArtifact | WallArtifact
segments: Array<SegmentArtifact> segments: Array<SegmentArtifact>
extrusion: ExtrusionArtifact extrusion: ExtrusionArtifact
@ -42,6 +49,7 @@ export interface PathArtifactRich {
interface SegmentArtifact { interface SegmentArtifact {
type: 'segment' type: 'segment'
id: ArtifactId
pathId: string pathId: string
surfaceId: string surfaceId: string
edgeIds: Array<string> edgeIds: Array<string>
@ -50,6 +58,7 @@ interface SegmentArtifact {
} }
interface SegmentArtifactRich { interface SegmentArtifactRich {
type: 'segment' type: 'segment'
id: ArtifactId
path: PathArtifact path: PathArtifact
surf: WallArtifact surf: WallArtifact
edges: Array<ExtrudeEdge> edges: Array<ExtrudeEdge>
@ -59,6 +68,7 @@ interface SegmentArtifactRich {
interface ExtrusionArtifact { interface ExtrusionArtifact {
type: 'extrusion' type: 'extrusion'
id: ArtifactId
pathId: string pathId: string
surfaceIds: Array<string> surfaceIds: Array<string>
edgeIds: Array<string> edgeIds: Array<string>
@ -66,6 +76,7 @@ interface ExtrusionArtifact {
} }
interface ExtrusionArtifactRich { interface ExtrusionArtifactRich {
type: 'extrusion' type: 'extrusion'
id: ArtifactId
path: PathArtifact path: PathArtifact
surfaces: Array<WallArtifact | CapArtifact> surfaces: Array<WallArtifact | CapArtifact>
edges: Array<ExtrudeEdge> edges: Array<ExtrudeEdge>
@ -74,6 +85,7 @@ interface ExtrusionArtifactRich {
interface WallArtifact { interface WallArtifact {
type: 'wall' type: 'wall'
id: ArtifactId
segId: string segId: string
edgeCutEdgeIds: Array<string> edgeCutEdgeIds: Array<string>
extrusionId: string extrusionId: string
@ -81,6 +93,7 @@ interface WallArtifact {
} }
interface CapArtifact { interface CapArtifact {
type: 'cap' type: 'cap'
id: ArtifactId
subType: 'start' | 'end' subType: 'start' | 'end'
edgeCutEdgeIds: Array<string> edgeCutEdgeIds: Array<string>
extrusionId: string extrusionId: string
@ -89,6 +102,7 @@ interface CapArtifact {
interface ExtrudeEdge { interface ExtrudeEdge {
type: 'extrudeEdge' type: 'extrudeEdge'
id: ArtifactId
segId: string segId: string
extrusionId: string extrusionId: string
edgeId: string edgeId: string
@ -97,6 +111,7 @@ interface ExtrudeEdge {
/** A edgeCut is a more generic term for both fillet or chamfer */ /** A edgeCut is a more generic term for both fillet or chamfer */
interface EdgeCut { interface EdgeCut {
type: 'edgeCut' type: 'edgeCut'
id: ArtifactId
subType: 'fillet' | 'chamfer' subType: 'fillet' | 'chamfer'
consumedEdgeId: string consumedEdgeId: string
edgeIds: Array<string> edgeIds: Array<string>
@ -106,6 +121,7 @@ interface EdgeCut {
interface EdgeCutEdge { interface EdgeCutEdge {
type: 'edgeCutEdge' type: 'edgeCutEdge'
id: ArtifactId
edgeCutId: string edgeCutId: string
surfaceId: string surfaceId: string
} }
@ -122,7 +138,7 @@ export type Artifact =
| EdgeCutEdge | EdgeCutEdge
| solid2D | solid2D
export type ArtifactGraph = Map<string, Artifact> export type ArtifactGraph = Map<ArtifactId, Artifact>
export type EngineCommand = Models['WebSocketRequest_type'] export type EngineCommand = Models['WebSocketRequest_type']
@ -149,7 +165,7 @@ export function createArtifactGraph({
responseMap: ResponseMap responseMap: ResponseMap
ast: Program ast: Program
}) { }) {
const myMap = new Map<string, Artifact>() const myMap = new Map<ArtifactId, Artifact>()
/** see docstring for {@link getArtifactsToUpdate} as to why this is needed */ /** see docstring for {@link getArtifactsToUpdate} as to why this is needed */
let currentPlaneId = '' let currentPlaneId = ''
@ -166,7 +182,7 @@ export function createArtifactGraph({
const artifactsToUpdate = getArtifactsToUpdate({ const artifactsToUpdate = getArtifactsToUpdate({
orderedCommand, orderedCommand,
responseMap, responseMap,
getArtifact: (id: string) => myMap.get(id), getArtifact: (id: ArtifactId) => myMap.get(id),
currentPlaneId, currentPlaneId,
ast, ast,
}) })
@ -210,7 +226,7 @@ function mergeArtifacts(
* It does not mutate the map directly, but returns an array of artifacts to update * It does not mutate the map directly, but returns an array of artifacts to update
* *
* @param currentPlaneId is only needed for `start_path` commands because this command does not have a pathId * @param currentPlaneId is only needed for `start_path` commands because this command does not have a pathId
* instead it relies on the id used with the `enable_sketch_mode` command, so this much be kept track of * instead it relies on the id used with the `enable_sketch_mode` command, so this must be kept track of
* outside of this function. It would be good to update the `start_path` command to include the planeId so we * outside of this function. It would be good to update the `start_path` command to include the planeId so we
* can remove this. * can remove this.
*/ */
@ -224,11 +240,11 @@ export function getArtifactsToUpdate({
orderedCommand: OrderedCommand orderedCommand: OrderedCommand
responseMap: ResponseMap responseMap: ResponseMap
/** Passing in a getter because we don't wan this function to update the map directly */ /** Passing in a getter because we don't wan this function to update the map directly */
getArtifact: (id: string) => Artifact | undefined getArtifact: (id: ArtifactId) => Artifact | undefined
currentPlaneId: string currentPlaneId: string
ast: Program ast: Program
}): Array<{ }): Array<{
id: string id: ArtifactId
artifact: Artifact artifact: Artifact
}> { }> {
const pathToNode = getNodePathFromSourceRange(ast, range) const pathToNode = getNodePathFromSourceRange(ast, range)
@ -253,6 +269,7 @@ export function getArtifactsToUpdate({
id: currentPlaneId, id: currentPlaneId,
artifact: { artifact: {
type: 'wall', type: 'wall',
id: currentPlaneId,
segId: existingPlane.segId, segId: existingPlane.segId,
edgeCutEdgeIds: existingPlane.edgeCutEdgeIds, edgeCutEdgeIds: existingPlane.edgeCutEdgeIds,
extrusionId: existingPlane.extrusionId, extrusionId: existingPlane.extrusionId,
@ -262,7 +279,10 @@ export function getArtifactsToUpdate({
] ]
} else { } else {
return [ return [
{ id: currentPlaneId, artifact: { type: 'plane', pathIds, codeRef } }, {
id: currentPlaneId,
artifact: { type: 'plane', id: currentPlaneId, pathIds, codeRef },
},
] ]
} }
} else if (cmd.type === 'start_path') { } else if (cmd.type === 'start_path') {
@ -270,6 +290,7 @@ export function getArtifactsToUpdate({
id, id,
artifact: { artifact: {
type: 'path', type: 'path',
id,
segIds: [], segIds: [],
planeId: currentPlaneId, planeId: currentPlaneId,
extrusionId: '', extrusionId: '',
@ -282,7 +303,7 @@ export function getArtifactsToUpdate({
if (plane?.type === 'plane') { if (plane?.type === 'plane') {
returnArr.push({ returnArr.push({
id: currentPlaneId, id: currentPlaneId,
artifact: { type: 'plane', pathIds: [id], codeRef }, artifact: { type: 'plane', id: currentPlaneId, pathIds: [id], codeRef },
}) })
} }
if (plane?.type === 'wall') { if (plane?.type === 'wall') {
@ -290,6 +311,7 @@ export function getArtifactsToUpdate({
id: currentPlaneId, id: currentPlaneId,
artifact: { artifact: {
type: 'wall', type: 'wall',
id: currentPlaneId,
segId: plane.segId, segId: plane.segId,
edgeCutEdgeIds: plane.edgeCutEdgeIds, edgeCutEdgeIds: plane.edgeCutEdgeIds,
extrusionId: plane.extrusionId, extrusionId: plane.extrusionId,
@ -304,6 +326,7 @@ export function getArtifactsToUpdate({
id, id,
artifact: { artifact: {
type: 'segment', type: 'segment',
id,
pathId, pathId,
surfaceId: '', surfaceId: '',
edgeIds: [], edgeIds: [],
@ -313,21 +336,22 @@ export function getArtifactsToUpdate({
const path = getArtifact(pathId) const path = getArtifact(pathId)
if (path?.type === 'path') if (path?.type === 'path')
returnArr.push({ returnArr.push({
id: pathId, id: path.id,
artifact: { ...path, segIds: [id] }, artifact: { ...path, segIds: [id] },
}) })
if ( if (
response?.type === 'modeling' && response?.type === 'modeling' &&
response.data.modeling_response.type === 'close_path' response.data.modeling_response.type === 'close_path'
) { ) {
const id = response.data.modeling_response.data.face_id
returnArr.push({ returnArr.push({
id: response.data.modeling_response.data.face_id, id,
artifact: { type: 'solid2D', pathId }, artifact: { type: 'solid2D', id, pathId },
}) })
const path = getArtifact(pathId) const path = getArtifact(pathId)
if (path?.type === 'path') if (path?.type === 'path')
returnArr.push({ returnArr.push({
id: pathId, id: path.id,
artifact: { artifact: {
...path, ...path,
solid2dId: response.data.modeling_response.data.face_id, solid2dId: response.data.modeling_response.data.face_id,
@ -340,6 +364,7 @@ export function getArtifactsToUpdate({
id, id,
artifact: { artifact: {
type: 'extrusion', type: 'extrusion',
id,
pathId: cmd.target, pathId: cmd.target,
surfaceIds: [], surfaceIds: [],
edgeIds: [], edgeIds: [],
@ -349,7 +374,7 @@ export function getArtifactsToUpdate({
const path = getArtifact(cmd.target) const path = getArtifact(cmd.target)
if (path?.type === 'path') if (path?.type === 'path')
returnArr.push({ returnArr.push({
id: cmd.target, id: path.id,
artifact: { ...path, extrusionId: id }, artifact: { ...path, extrusionId: id },
}) })
return returnArr return returnArr
@ -371,6 +396,7 @@ export function getArtifactsToUpdate({
id: face_id, id: face_id,
artifact: { artifact: {
type: 'wall', type: 'wall',
id: face_id,
segId: curve_id, segId: curve_id,
edgeCutEdgeIds: [], edgeCutEdgeIds: [],
extrusionId: path.extrusionId, extrusionId: path.extrusionId,
@ -378,7 +404,7 @@ export function getArtifactsToUpdate({
}, },
}) })
returnArr.push({ returnArr.push({
id: curve_id, id: seg.id,
artifact: { ...seg, surfaceId: face_id }, artifact: { ...seg, surfaceId: face_id },
}) })
const extrusion = getArtifact(path.extrusionId) const extrusion = getArtifact(path.extrusionId)
@ -403,6 +429,7 @@ export function getArtifactsToUpdate({
id: face_id, id: face_id,
artifact: { artifact: {
type: 'cap', type: 'cap',
id: face_id,
subType: cap === 'bottom' ? 'start' : 'end', subType: cap === 'bottom' ? 'start' : 'end',
edgeCutEdgeIds: [], edgeCutEdgeIds: [],
extrusionId: path.extrusionId, extrusionId: path.extrusionId,
@ -412,7 +439,7 @@ export function getArtifactsToUpdate({
const extrusion = getArtifact(path.extrusionId) const extrusion = getArtifact(path.extrusionId)
if (extrusion?.type !== 'extrusion') return if (extrusion?.type !== 'extrusion') return
returnArr.push({ returnArr.push({
id: path.extrusionId, id: extrusion.id,
artifact: { artifact: {
...extrusion, ...extrusion,
surfaceIds: [face_id], surfaceIds: [face_id],
@ -427,6 +454,7 @@ export function getArtifactsToUpdate({
id, id,
artifact: { artifact: {
type: 'edgeCut', type: 'edgeCut',
id,
subType: cmd.cut_type, subType: cmd.cut_type,
consumedEdgeId: cmd.edge_id, consumedEdgeId: cmd.edge_id,
edgeIds: [], edgeIds: [],
@ -437,7 +465,7 @@ export function getArtifactsToUpdate({
const consumedEdge = getArtifact(cmd.edge_id) const consumedEdge = getArtifact(cmd.edge_id)
if (consumedEdge?.type === 'segment') { if (consumedEdge?.type === 'segment') {
returnArr.push({ returnArr.push({
id: cmd.edge_id, id: consumedEdge.id,
artifact: { ...consumedEdge, edgeCutId: id }, artifact: { ...consumedEdge, edgeCutId: id },
}) })
} }
@ -464,7 +492,7 @@ export function filterArtifacts<T extends Artifact['type'][]>(
(!predicate || (!predicate ||
predicate(value as Extract<Artifact, { type: T[number] }>)) predicate(value as Extract<Artifact, { type: T[number] }>))
) )
) as Map<string, Extract<Artifact, { type: T[number] }>> ) as Map<ArtifactId, Extract<Artifact, { type: T[number] }>>
} }
export function getArtifactsOfTypes<T extends Artifact['type'][]>( export function getArtifactsOfTypes<T extends Artifact['type'][]>(
@ -478,7 +506,7 @@ export function getArtifactsOfTypes<T extends Artifact['type'][]>(
predicate?: (value: Extract<Artifact, { type: T[number] }>) => boolean predicate?: (value: Extract<Artifact, { type: T[number] }>) => boolean
}, },
map: ArtifactGraph map: ArtifactGraph
): Map<string, Extract<Artifact, { type: T[number] }>> { ): Map<ArtifactId, Extract<Artifact, { type: T[number] }>> {
return new Map( return new Map(
[...map].filter( [...map].filter(
([key, value]) => ([key, value]) =>
@ -487,7 +515,7 @@ export function getArtifactsOfTypes<T extends Artifact['type'][]>(
(!predicate || (!predicate ||
predicate(value as Extract<Artifact, { type: T[number] }>)) predicate(value as Extract<Artifact, { type: T[number] }>))
) )
) as Map<string, Extract<Artifact, { type: T[number] }>> ) as Map<ArtifactId, Extract<Artifact, { type: T[number] }>>
} }
export function getArtifactOfTypes<T extends Artifact['type'][]>( export function getArtifactOfTypes<T extends Artifact['type'][]>(
@ -495,7 +523,7 @@ export function getArtifactOfTypes<T extends Artifact['type'][]>(
key, key,
types, types,
}: { }: {
key: string key: ArtifactId
types: T types: T
}, },
map: ArtifactGraph map: ArtifactGraph
@ -517,6 +545,7 @@ export function expandPlane(
) )
return { return {
type: 'plane', type: 'plane',
id: plane.id,
paths: Array.from(paths.values()), paths: Array.from(paths.values()),
codeRef: plane.codeRef, codeRef: plane.codeRef,
} }
@ -545,6 +574,7 @@ export function expandPath(
if (err(plane)) return plane if (err(plane)) return plane
return { return {
type: 'path', type: 'path',
id: path.id,
segments: Array.from(segs.values()), segments: Array.from(segs.values()),
extrusion, extrusion,
plane, plane,
@ -571,6 +601,7 @@ export function expandExtrusion(
if (err(path)) return path if (err(path)) return path
return { return {
type: 'extrusion', type: 'extrusion',
id: extrusion.id,
surfaces: Array.from(surfs.values()), surfaces: Array.from(surfs.values()),
edges: Array.from(edges.values()), edges: Array.from(edges.values()),
path, path,
@ -606,6 +637,7 @@ export function expandSegment(
return { return {
type: 'segment', type: 'segment',
id: segment.id,
path, path,
surf, surf,
edges: Array.from(edges.values()), edges: Array.from(edges.values()),
@ -656,7 +688,7 @@ export function getWallCodeRef(
} }
export function getExtrusionFromSuspectedExtrudeSurface( export function getExtrusionFromSuspectedExtrudeSurface(
id: string, id: ArtifactId,
artifactGraph: ArtifactGraph artifactGraph: ArtifactGraph
): ExtrusionArtifact | Error { ): ExtrusionArtifact | Error {
const artifact = getArtifactOfTypes( const artifact = getArtifactOfTypes(
@ -671,7 +703,7 @@ export function getExtrusionFromSuspectedExtrudeSurface(
} }
export function getExtrusionFromSuspectedPath( export function getExtrusionFromSuspectedPath(
id: string, id: ArtifactId,
artifactGraph: ArtifactGraph artifactGraph: ArtifactGraph
): ExtrusionArtifact | Error { ): ExtrusionArtifact | Error {
const path = getArtifactOfTypes({ key: id, types: ['path'] }, artifactGraph) const path = getArtifactOfTypes({ key: id, types: ['path'] }, artifactGraph)
@ -681,3 +713,33 @@ export function getExtrusionFromSuspectedPath(
artifactGraph artifactGraph
) )
} }
/**
* Get the plane or face from a selection.
*
* TODO: Handle sketch on face.
*/
export function getPlaneOrFaceFromSelection(
id: ArtifactId,
artifactGraph: ArtifactGraph
): PlaneArtifactRich | null {
const selection = artifactGraph.get(id)
if (!selection) return null
if (selection.type === 'solid2D') {
const path = artifactGraph.get(selection.pathId)
if (path?.type !== 'path') return null
const plane = artifactGraph.get(path.planeId)
if (plane?.type !== 'plane') return null
return expandPlane(plane, artifactGraph)
} else if (selection.type === 'wall' || selection.type === 'cap') {
const extrusion = artifactGraph.get(selection.extrusionId)
if (extrusion?.type !== 'extrusion') return null
const path = artifactGraph.get(extrusion.pathId)
if (path?.type !== 'path') return null
const plane = artifactGraph.get(path.planeId)
// TODO: For sketch on face, this won't be a plane.
if (plane?.type !== 'plane') return null
return expandPlane(plane, artifactGraph)
}
return null
}

View File

@ -30,6 +30,7 @@ import { AXIS_GROUP, X_AXIS } from 'clientSideScene/sceneInfra'
import { PathToNodeMap } from 'lang/std/sketchcombos' import { PathToNodeMap } from 'lang/std/sketchcombos'
import { err } from 'lib/trap' import { err } from 'lib/trap'
import { import {
ArtifactId,
getArtifactOfTypes, getArtifactOfTypes,
getArtifactsOfTypes, getArtifactsOfTypes,
getCapCodeRef, getCapCodeRef,
@ -56,6 +57,7 @@ export type Selection = {
| 'line' | 'line'
| 'arc' | 'arc'
| 'all' | 'all'
artifactId?: ArtifactId
range: SourceRange range: SourceRange
} }
export type Selections = { export type Selections = {
@ -100,7 +102,11 @@ export async function getEventForSelectWithPoint({
type: 'Set selection', type: 'Set selection',
data: { data: {
selectionType: 'singleCodeCursor', selectionType: 'singleCodeCursor',
selection: { range: codeRef.range, type: 'solid2D' }, selection: {
artifactId: data.entity_id,
range: codeRef.range,
type: 'solid2D',
},
}, },
} }
} }
@ -112,6 +118,7 @@ export async function getEventForSelectWithPoint({
data: { data: {
selectionType: 'singleCodeCursor', selectionType: 'singleCodeCursor',
selection: { selection: {
artifactId: data.entity_id,
range: codeRef.range, range: codeRef.range,
type: _artifact?.subType === 'end' ? 'end-cap' : 'start-cap', type: _artifact?.subType === 'end' ? 'end-cap' : 'start-cap',
}, },
@ -128,7 +135,11 @@ export async function getEventForSelectWithPoint({
type: 'Set selection', type: 'Set selection',
data: { data: {
selectionType: 'singleCodeCursor', selectionType: 'singleCodeCursor',
selection: { range: codeRef.range, type: 'extrude-wall' }, selection: {
artifactId: data.entity_id,
range: codeRef.range,
type: 'extrude-wall',
},
}, },
} }
} }
@ -137,7 +148,11 @@ export async function getEventForSelectWithPoint({
type: 'Set selection', type: 'Set selection',
data: { data: {
selectionType: 'singleCodeCursor', selectionType: 'singleCodeCursor',
selection: { range: _artifact.codeRef.range, type: 'default' }, selection: {
artifactId: data.entity_id,
range: _artifact.codeRef.range,
type: 'default',
},
}, },
} }
} }
@ -642,9 +657,12 @@ export function updateSelections(
const nodeMeta = getNodeFromPath<Value>(ast, pathToNode) const nodeMeta = getNodeFromPath<Value>(ast, pathToNode)
if (err(nodeMeta)) return undefined if (err(nodeMeta)) return undefined
const node = nodeMeta.node const node = nodeMeta.node
const prevCodeBasedSelection =
prevSelectionRanges.codeBasedSelections[Number(index)]
return { return {
artifactId: prevCodeBasedSelection?.artifactId,
range: [node.start, node.end], range: [node.start, node.end],
type: prevSelectionRanges.codeBasedSelections[Number(index)]?.type, type: prevCodeBasedSelection?.type,
} }
}) })
.filter((x?: Selection) => x !== undefined) as Selection[] .filter((x?: Selection) => x !== undefined) as Selection[]