2023-10-16 21:20:05 +11:00
|
|
|
import { Selection } from 'lib/selections'
|
2024-09-09 18:17:45 -04:00
|
|
|
import { err, reportRejection, trap } from 'lib/trap'
|
2023-01-06 09:29:26 +11:00
|
|
|
import {
|
|
|
|
Program,
|
|
|
|
CallExpression,
|
|
|
|
PipeExpression,
|
|
|
|
VariableDeclaration,
|
2023-02-12 10:56:45 +11:00
|
|
|
VariableDeclarator,
|
2024-08-12 15:38:42 -05:00
|
|
|
Expr,
|
2023-02-12 10:56:45 +11:00
|
|
|
Literal,
|
|
|
|
PipeSubstitution,
|
|
|
|
Identifier,
|
|
|
|
ArrayExpression,
|
|
|
|
ObjectExpression,
|
2023-03-02 21:19:11 +11:00
|
|
|
UnaryExpression,
|
|
|
|
BinaryExpression,
|
2023-09-29 11:11:01 -07:00
|
|
|
PathToNode,
|
|
|
|
ProgramMemory,
|
2024-05-24 20:54:42 +10:00
|
|
|
SourceRange,
|
Remove KclValue::SketchGroup variant (#3446)
We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.
Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.
Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.
My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
2024-08-21 11:06:48 -05:00
|
|
|
sketchGroupFromKclValue,
|
2023-09-29 11:11:01 -07:00
|
|
|
} from './wasm'
|
2023-04-01 16:47:00 +11:00
|
|
|
import {
|
2024-05-24 20:54:42 +10:00
|
|
|
isNodeSafeToReplacePath,
|
2023-04-01 16:47:00 +11:00
|
|
|
findAllPreviousVariables,
|
2024-05-24 20:54:42 +10:00
|
|
|
findAllPreviousVariablesPath,
|
2023-04-01 16:47:00 +11:00
|
|
|
getNodeFromPath,
|
|
|
|
getNodePathFromSourceRange,
|
|
|
|
isNodeSafeToReplace,
|
2024-06-29 10:36:04 -07:00
|
|
|
traverse,
|
2023-04-01 16:47:00 +11:00
|
|
|
} from './queryAst'
|
2024-05-24 20:54:42 +10:00
|
|
|
import { addTagForSketchOnFace, getConstraintInfo } from './std/sketch'
|
|
|
|
import {
|
|
|
|
PathToNodeMap,
|
|
|
|
isLiteralArrayOrStatic,
|
|
|
|
removeSingleConstraint,
|
|
|
|
transformAstSketchLines,
|
|
|
|
} from './std/sketchcombos'
|
2024-02-14 08:03:20 +11:00
|
|
|
import { DefaultPlaneStr } from 'clientSideScene/sceneEntities'
|
2024-05-24 20:54:42 +10:00
|
|
|
import { isOverlap, roundOff } from 'lib/utils'
|
2024-06-04 13:57:01 -04:00
|
|
|
import { KCL_DEFAULT_CONSTANT_PREFIXES } from 'lib/constants'
|
2024-09-13 21:14:14 +10:00
|
|
|
import { SimplifiedArgDetails } from './std/stdTypes'
|
2024-06-24 22:39:04 -07:00
|
|
|
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
|
2024-06-29 10:36:04 -07:00
|
|
|
import { Models } from '@kittycad/lib'
|
2024-09-26 18:25:05 +10:00
|
|
|
import { ExtrudeFacePlane } from 'machines/modelingMachine'
|
2023-01-06 09:29:26 +11:00
|
|
|
|
2024-02-11 12:59:00 +11:00
|
|
|
export function startSketchOnDefault(
|
2023-09-13 08:36:47 +10:00
|
|
|
node: Program,
|
2024-02-11 12:59:00 +11:00
|
|
|
axis: DefaultPlaneStr,
|
|
|
|
name = ''
|
2023-09-13 08:36:47 +10:00
|
|
|
): { modifiedAst: Program; id: string; pathToNode: PathToNode } {
|
|
|
|
const _node = { ...node }
|
2024-06-04 13:57:01 -04:00
|
|
|
const _name =
|
|
|
|
name || findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.SKETCH)
|
2023-09-13 08:36:47 +10:00
|
|
|
|
2023-10-05 14:27:48 -07:00
|
|
|
const startSketchOn = createCallExpressionStdLib('startSketchOn', [
|
2024-02-11 12:59:00 +11:00
|
|
|
createLiteral(axis),
|
2023-09-13 08:36:47 +10:00
|
|
|
])
|
|
|
|
|
2024-02-11 12:59:00 +11:00
|
|
|
const variableDeclaration = createVariableDeclaration(_name, startSketchOn)
|
2023-09-13 08:36:47 +10:00
|
|
|
_node.body = [...node.body, variableDeclaration]
|
2024-02-11 12:59:00 +11:00
|
|
|
const sketchIndex = _node.body.length - 1
|
2023-09-13 08:36:47 +10:00
|
|
|
|
|
|
|
let pathToNode: PathToNode = [
|
|
|
|
['body', ''],
|
2024-02-11 12:59:00 +11:00
|
|
|
[sketchIndex, 'index'],
|
2023-09-13 08:36:47 +10:00
|
|
|
['declarations', 'VariableDeclaration'],
|
|
|
|
['0', 'index'],
|
|
|
|
['init', 'VariableDeclarator'],
|
|
|
|
]
|
|
|
|
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
id: _name,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-11 12:59:00 +11:00
|
|
|
export function addStartProfileAt(
|
|
|
|
node: Program,
|
|
|
|
pathToNode: PathToNode,
|
|
|
|
at: [number, number]
|
2024-06-24 11:45:40 -04:00
|
|
|
): { modifiedAst: Program; pathToNode: PathToNode } | Error {
|
|
|
|
const _node1 = getNodeFromPath<VariableDeclaration>(
|
2024-02-11 12:59:00 +11:00
|
|
|
node,
|
|
|
|
pathToNode,
|
|
|
|
'VariableDeclaration'
|
2024-06-24 11:45:40 -04:00
|
|
|
)
|
|
|
|
if (err(_node1)) return _node1
|
|
|
|
const variableDeclaration = _node1.node
|
2024-02-11 12:59:00 +11:00
|
|
|
if (variableDeclaration.type !== 'VariableDeclaration') {
|
2024-06-24 11:45:40 -04:00
|
|
|
return new Error('variableDeclaration.init.type !== PipeExpression')
|
2024-02-11 12:59:00 +11:00
|
|
|
}
|
|
|
|
const _node = { ...node }
|
|
|
|
const init = variableDeclaration.declarations[0].init
|
|
|
|
const startProfileAt = createCallExpressionStdLib('startProfileAt', [
|
|
|
|
createArrayExpression([
|
|
|
|
createLiteral(roundOff(at[0])),
|
|
|
|
createLiteral(roundOff(at[1])),
|
|
|
|
]),
|
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
|
|
|
if (init.type === 'PipeExpression') {
|
|
|
|
init.body.splice(1, 0, startProfileAt)
|
|
|
|
} else {
|
|
|
|
variableDeclaration.declarations[0].init = createPipeExpression([
|
|
|
|
init,
|
|
|
|
startProfileAt,
|
|
|
|
])
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-06 09:29:26 +11:00
|
|
|
export function addSketchTo(
|
|
|
|
node: Program,
|
|
|
|
axis: 'xy' | 'xz' | 'yz',
|
|
|
|
name = ''
|
2023-04-01 16:47:00 +11:00
|
|
|
): { modifiedAst: Program; id: string; pathToNode: PathToNode } {
|
2023-01-06 09:29:26 +11:00
|
|
|
const _node = { ...node }
|
2024-06-04 13:57:01 -04:00
|
|
|
const _name =
|
|
|
|
name || findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.SKETCH)
|
2023-01-06 09:29:26 +11:00
|
|
|
|
2023-10-05 14:27:48 -07:00
|
|
|
const startSketchOn = createCallExpressionStdLib('startSketchOn', [
|
|
|
|
createLiteral(axis.toUpperCase()),
|
2023-02-12 10:56:45 +11:00
|
|
|
])
|
2023-10-05 14:27:48 -07:00
|
|
|
const startProfileAt = createCallExpressionStdLib('startProfileAt', [
|
|
|
|
createLiteral('default'),
|
2023-02-12 10:56:45 +11:00
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
2023-09-05 16:02:27 -07:00
|
|
|
const initialLineTo = createCallExpressionStdLib('line', [
|
2023-03-17 15:53:20 +11:00
|
|
|
createLiteral('default'),
|
2023-02-12 10:56:45 +11:00
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
2023-01-06 09:29:26 +11:00
|
|
|
|
2023-10-05 14:27:48 -07:00
|
|
|
const pipeBody = [startSketchOn, startProfileAt, initialLineTo]
|
2023-02-12 10:56:45 +11:00
|
|
|
|
|
|
|
const variableDeclaration = createVariableDeclaration(
|
|
|
|
_name,
|
|
|
|
createPipeExpression(pipeBody)
|
|
|
|
)
|
2023-01-06 09:29:26 +11:00
|
|
|
|
2024-03-01 17:16:18 -08:00
|
|
|
_node.body = [...node.body, variableDeclaration]
|
|
|
|
let sketchIndex = _node.body.length - 1
|
2023-04-01 16:47:00 +11:00
|
|
|
let pathToNode: PathToNode = [
|
|
|
|
['body', ''],
|
|
|
|
[sketchIndex, 'index'],
|
|
|
|
['declarations', 'VariableDeclaration'],
|
|
|
|
['0', 'index'],
|
|
|
|
['init', 'VariableDeclarator'],
|
2023-01-06 09:29:26 +11:00
|
|
|
]
|
|
|
|
if (axis !== 'xy') {
|
2023-04-01 16:47:00 +11:00
|
|
|
pathToNode = [...pathToNode, ['body', ''], ['0', 'index']]
|
2023-01-06 09:29:26 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
2024-03-01 17:16:18 -08:00
|
|
|
modifiedAst: _node,
|
2023-01-06 09:29:26 +11:00
|
|
|
id: _name,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-12 10:56:45 +11:00
|
|
|
export function findUniqueName(
|
2023-01-06 09:29:26 +11:00
|
|
|
ast: Program | string,
|
|
|
|
name: string,
|
2023-01-09 13:19:14 +11:00
|
|
|
pad = 3,
|
2023-01-06 09:29:26 +11:00
|
|
|
index = 1
|
|
|
|
): string {
|
2024-02-23 11:24:22 -05:00
|
|
|
let searchStr: string = typeof ast === 'string' ? ast : JSON.stringify(ast)
|
|
|
|
const indexStr = String(index).padStart(pad, '0')
|
|
|
|
|
|
|
|
const endingDigitsMatcher = /\d+$/
|
|
|
|
const nameEndsInDigits = name.match(endingDigitsMatcher)
|
|
|
|
let nameIsInString = searchStr.includes(`:"${name}"`)
|
|
|
|
|
|
|
|
if (nameEndsInDigits !== null) {
|
|
|
|
// base case: name is unique and ends in digits
|
|
|
|
if (!nameIsInString) return name
|
|
|
|
|
|
|
|
// recursive case: name is not unique and ends in digits
|
|
|
|
const newPad = nameEndsInDigits[1].length
|
|
|
|
const newIndex = parseInt(nameEndsInDigits[1]) + 1
|
|
|
|
const nameWithoutDigits = name.replace(endingDigitsMatcher, '')
|
|
|
|
|
|
|
|
return findUniqueName(searchStr, nameWithoutDigits, newPad, newIndex)
|
2023-01-06 09:29:26 +11:00
|
|
|
}
|
2024-02-23 11:24:22 -05:00
|
|
|
|
2023-01-06 09:29:26 +11:00
|
|
|
const newName = `${name}${indexStr}`
|
2024-02-23 11:24:22 -05:00
|
|
|
nameIsInString = searchStr.includes(`:"${newName}"`)
|
|
|
|
|
|
|
|
// base case: name is unique and does not end in digits
|
|
|
|
if (!nameIsInString) return newName
|
|
|
|
|
|
|
|
// recursive case: name is not unique and does not end in digits
|
2023-01-09 13:19:14 +11:00
|
|
|
return findUniqueName(searchStr, name, pad, index + 1)
|
2023-01-06 09:29:26 +11:00
|
|
|
}
|
|
|
|
|
2024-08-12 15:38:42 -05:00
|
|
|
export function mutateArrExp(node: Expr, updateWith: ArrayExpression): boolean {
|
2023-02-12 10:56:45 +11:00
|
|
|
if (node.type === 'ArrayExpression') {
|
|
|
|
node.elements.forEach((element, i) => {
|
2023-09-13 07:23:14 -07:00
|
|
|
if (isLiteralArrayOrStatic(element)) {
|
2023-02-12 10:56:45 +11:00
|
|
|
node.elements[i] = updateWith.elements[i]
|
|
|
|
}
|
|
|
|
})
|
|
|
|
return true
|
2023-01-06 09:29:26 +11:00
|
|
|
}
|
2023-02-12 10:56:45 +11:00
|
|
|
return false
|
2023-01-06 09:29:26 +11:00
|
|
|
}
|
|
|
|
|
2023-02-12 10:56:45 +11:00
|
|
|
export function mutateObjExpProp(
|
2024-08-12 15:38:42 -05:00
|
|
|
node: Expr,
|
2023-02-12 10:56:45 +11:00
|
|
|
updateWith: Literal | ArrayExpression,
|
|
|
|
key: string
|
|
|
|
): boolean {
|
|
|
|
if (node.type === 'ObjectExpression') {
|
|
|
|
const keyIndex = node.properties.findIndex((a) => a.key.name === key)
|
|
|
|
if (keyIndex !== -1) {
|
|
|
|
if (
|
2023-09-13 07:23:14 -07:00
|
|
|
isLiteralArrayOrStatic(updateWith) &&
|
|
|
|
isLiteralArrayOrStatic(node.properties[keyIndex].value)
|
2023-02-12 10:56:45 +11:00
|
|
|
) {
|
|
|
|
node.properties[keyIndex].value = updateWith
|
|
|
|
return true
|
|
|
|
} else if (
|
|
|
|
node.properties[keyIndex].value.type === 'ArrayExpression' &&
|
|
|
|
updateWith.type === 'ArrayExpression'
|
|
|
|
) {
|
|
|
|
const arrExp = node.properties[keyIndex].value as ArrayExpression
|
|
|
|
arrExp.elements.forEach((element, i) => {
|
2023-09-13 07:23:14 -07:00
|
|
|
if (isLiteralArrayOrStatic(element)) {
|
2023-02-12 10:56:45 +11:00
|
|
|
arrExp.elements[i] = updateWith.elements[i]
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
} else {
|
|
|
|
node.properties.push({
|
|
|
|
type: 'ObjectProperty',
|
|
|
|
key: createIdentifier(key),
|
|
|
|
value: updateWith,
|
|
|
|
start: 0,
|
|
|
|
end: 0,
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2023-02-12 10:56:45 +11:00
|
|
|
})
|
|
|
|
}
|
2023-01-06 09:29:26 +11:00
|
|
|
}
|
2023-02-12 10:56:45 +11:00
|
|
|
return false
|
2023-01-06 09:29:26 +11:00
|
|
|
}
|
2023-01-06 12:45:34 +11:00
|
|
|
|
|
|
|
export function extrudeSketch(
|
|
|
|
node: Program,
|
2023-04-01 16:47:00 +11:00
|
|
|
pathToNode: PathToNode,
|
2024-06-04 13:57:01 -04:00
|
|
|
shouldPipe = false,
|
2024-09-17 08:29:52 -05:00
|
|
|
distance: Expr = createLiteral(4)
|
2024-06-24 11:45:40 -04:00
|
|
|
):
|
|
|
|
| {
|
|
|
|
modifiedAst: Program
|
|
|
|
pathToNode: PathToNode
|
|
|
|
pathToExtrudeArg: PathToNode
|
|
|
|
}
|
|
|
|
| Error {
|
2024-09-17 08:29:52 -05:00
|
|
|
const _node = structuredClone(node)
|
2024-06-24 11:45:40 -04:00
|
|
|
const _node1 = getNodeFromPath(_node, pathToNode)
|
|
|
|
if (err(_node1)) return _node1
|
|
|
|
const { node: sketchExpression } = _node1
|
2023-01-06 12:45:34 +11:00
|
|
|
|
|
|
|
// determine if sketchExpression is in a pipeExpression or not
|
2024-06-24 11:45:40 -04:00
|
|
|
const _node2 = getNodeFromPath<PipeExpression>(
|
2023-01-06 12:45:34 +11:00
|
|
|
_node,
|
|
|
|
pathToNode,
|
2023-01-13 17:58:37 +11:00
|
|
|
'PipeExpression'
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(_node2)) return _node2
|
|
|
|
const { node: pipeExpression } = _node2
|
|
|
|
|
2023-01-13 17:58:37 +11:00
|
|
|
const isInPipeExpression = pipeExpression.type === 'PipeExpression'
|
|
|
|
|
2024-06-24 11:45:40 -04:00
|
|
|
const _node3 = getNodeFromPath<VariableDeclarator>(
|
|
|
|
_node,
|
|
|
|
pathToNode,
|
|
|
|
'VariableDeclarator'
|
|
|
|
)
|
|
|
|
if (err(_node3)) return _node3
|
|
|
|
const { node: variableDeclarator, shallowPath: pathToDecleration } = _node3
|
2023-01-06 12:45:34 +11:00
|
|
|
|
2023-09-05 16:02:27 -07:00
|
|
|
const extrudeCall = createCallExpressionStdLib('extrude', [
|
2024-02-23 11:24:22 -05:00
|
|
|
distance,
|
2023-03-02 21:19:11 +11:00
|
|
|
shouldPipe
|
|
|
|
? createPipeSubstitution()
|
2024-04-11 10:35:23 +10:00
|
|
|
: createIdentifier(variableDeclarator.id.name),
|
2023-03-02 21:19:11 +11:00
|
|
|
])
|
|
|
|
|
|
|
|
if (shouldPipe) {
|
|
|
|
const pipeChain = createPipeExpression(
|
|
|
|
isInPipeExpression
|
|
|
|
? [...pipeExpression.body, extrudeCall]
|
|
|
|
: [sketchExpression as any, extrudeCall]
|
|
|
|
)
|
2023-01-06 12:45:34 +11:00
|
|
|
|
2024-04-11 10:35:23 +10:00
|
|
|
variableDeclarator.init = pipeChain
|
2023-04-01 16:47:00 +11:00
|
|
|
const pathToExtrudeArg: PathToNode = [
|
2023-01-13 17:58:37 +11:00
|
|
|
...pathToDecleration,
|
2023-04-01 16:47:00 +11:00
|
|
|
['init', 'VariableDeclarator'],
|
|
|
|
['body', ''],
|
|
|
|
[pipeChain.body.length - 1, 'index'],
|
|
|
|
['arguments', 'CallExpression'],
|
|
|
|
[0, 'index'],
|
2023-01-13 17:58:37 +11:00
|
|
|
]
|
2023-01-06 12:45:34 +11:00
|
|
|
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
2023-01-13 17:58:37 +11:00
|
|
|
pathToExtrudeArg,
|
2023-01-06 12:45:34 +11:00
|
|
|
}
|
|
|
|
}
|
2024-06-04 13:57:01 -04:00
|
|
|
|
|
|
|
// We're not creating a pipe expression,
|
|
|
|
// but rather a separate constant for the extrusion
|
|
|
|
const name = findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.EXTRUDE)
|
2023-03-02 21:19:11 +11:00
|
|
|
const VariableDeclaration = createVariableDeclaration(name, extrudeCall)
|
2024-06-04 13:57:01 -04:00
|
|
|
|
|
|
|
const sketchIndexInPathToNode =
|
|
|
|
pathToDecleration.findIndex((a) => a[0] === 'body') + 1
|
|
|
|
const sketchIndexInBody = pathToDecleration[
|
|
|
|
sketchIndexInPathToNode
|
|
|
|
][0] as number
|
|
|
|
_node.body.splice(sketchIndexInBody + 1, 0, VariableDeclaration)
|
|
|
|
|
2023-04-01 16:47:00 +11:00
|
|
|
const pathToExtrudeArg: PathToNode = [
|
|
|
|
['body', ''],
|
2024-06-04 13:57:01 -04:00
|
|
|
[sketchIndexInBody + 1, 'index'],
|
2023-04-01 16:47:00 +11:00
|
|
|
['declarations', 'VariableDeclaration'],
|
|
|
|
[0, 'index'],
|
|
|
|
['init', 'VariableDeclarator'],
|
|
|
|
['arguments', 'CallExpression'],
|
|
|
|
[0, 'index'],
|
2023-01-13 17:58:37 +11:00
|
|
|
]
|
2023-01-06 12:45:34 +11:00
|
|
|
return {
|
2024-06-04 13:57:01 -04:00
|
|
|
modifiedAst: _node,
|
2024-03-01 17:16:18 -08:00
|
|
|
pathToNode: [...pathToNode.slice(0, -1), [-1, 'index']],
|
2023-01-13 17:58:37 +11:00
|
|
|
pathToExtrudeArg,
|
2023-01-06 12:45:34 +11:00
|
|
|
}
|
|
|
|
}
|
2023-01-09 13:19:14 +11:00
|
|
|
|
2024-09-17 08:29:52 -05:00
|
|
|
export function revolveSketch(
|
|
|
|
node: Program,
|
|
|
|
pathToNode: PathToNode,
|
|
|
|
shouldPipe = false,
|
|
|
|
angle: Expr = createLiteral(4)
|
|
|
|
):
|
|
|
|
| {
|
|
|
|
modifiedAst: Program
|
|
|
|
pathToNode: PathToNode
|
|
|
|
pathToRevolveArg: PathToNode
|
|
|
|
}
|
|
|
|
| Error {
|
|
|
|
const _node = structuredClone(node)
|
|
|
|
const _node1 = getNodeFromPath(_node, pathToNode)
|
|
|
|
if (err(_node1)) return _node1
|
|
|
|
const { node: sketchExpression } = _node1
|
|
|
|
|
|
|
|
// determine if sketchExpression is in a pipeExpression or not
|
|
|
|
const _node2 = getNodeFromPath<PipeExpression>(
|
|
|
|
_node,
|
|
|
|
pathToNode,
|
|
|
|
'PipeExpression'
|
|
|
|
)
|
|
|
|
if (err(_node2)) return _node2
|
|
|
|
const { node: pipeExpression } = _node2
|
|
|
|
|
|
|
|
const isInPipeExpression = pipeExpression.type === 'PipeExpression'
|
|
|
|
|
|
|
|
const _node3 = getNodeFromPath<VariableDeclarator>(
|
|
|
|
_node,
|
|
|
|
pathToNode,
|
|
|
|
'VariableDeclarator'
|
|
|
|
)
|
|
|
|
if (err(_node3)) return _node3
|
|
|
|
const { node: variableDeclarator, shallowPath: pathToDecleration } = _node3
|
|
|
|
|
|
|
|
const revolveCall = createCallExpressionStdLib('revolve', [
|
|
|
|
createObjectExpression({
|
|
|
|
angle: angle,
|
|
|
|
// TODO: hard coded 'X' axis for revolve MVP, should be changed.
|
|
|
|
axis: createLiteral('X'),
|
|
|
|
}),
|
|
|
|
createIdentifier(variableDeclarator.id.name),
|
|
|
|
])
|
|
|
|
|
|
|
|
if (shouldPipe) {
|
|
|
|
const pipeChain = createPipeExpression(
|
|
|
|
isInPipeExpression
|
|
|
|
? [...pipeExpression.body, revolveCall]
|
|
|
|
: [sketchExpression as any, revolveCall]
|
|
|
|
)
|
|
|
|
|
|
|
|
variableDeclarator.init = pipeChain
|
|
|
|
const pathToRevolveArg: PathToNode = [
|
|
|
|
...pathToDecleration,
|
|
|
|
['init', 'VariableDeclarator'],
|
|
|
|
['body', ''],
|
|
|
|
[pipeChain.body.length - 1, 'index'],
|
|
|
|
['arguments', 'CallExpression'],
|
|
|
|
[0, 'index'],
|
|
|
|
]
|
|
|
|
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
pathToRevolveArg,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We're not creating a pipe expression,
|
|
|
|
// but rather a separate constant for the extrusion
|
|
|
|
const name = findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.REVOLVE)
|
|
|
|
const VariableDeclaration = createVariableDeclaration(name, revolveCall)
|
|
|
|
const sketchIndexInPathToNode =
|
|
|
|
pathToDecleration.findIndex((a) => a[0] === 'body') + 1
|
|
|
|
const sketchIndexInBody = pathToDecleration[sketchIndexInPathToNode][0]
|
|
|
|
if (typeof sketchIndexInBody !== 'number')
|
|
|
|
return new Error('expected sketchIndexInBody to be a number')
|
|
|
|
_node.body.splice(sketchIndexInBody + 1, 0, VariableDeclaration)
|
|
|
|
|
|
|
|
const pathToRevolveArg: PathToNode = [
|
|
|
|
['body', ''],
|
|
|
|
[sketchIndexInBody + 1, 'index'],
|
|
|
|
['declarations', 'VariableDeclaration'],
|
|
|
|
[0, 'index'],
|
|
|
|
['init', 'VariableDeclarator'],
|
|
|
|
['arguments', 'CallExpression'],
|
|
|
|
[0, 'index'],
|
|
|
|
]
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode: [...pathToNode.slice(0, -1), [-1, 'index']],
|
|
|
|
pathToRevolveArg,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-09 13:19:14 +11:00
|
|
|
export function sketchOnExtrudedFace(
|
|
|
|
node: Program,
|
2024-06-04 13:57:01 -04:00
|
|
|
sketchPathToNode: PathToNode,
|
|
|
|
extrudePathToNode: PathToNode,
|
2024-09-26 18:25:05 +10:00
|
|
|
info: ExtrudeFacePlane['faceInfo'] = { type: 'wall' }
|
2024-06-24 11:45:40 -04:00
|
|
|
): { modifiedAst: Program; pathToNode: PathToNode } | Error {
|
2023-02-12 10:56:45 +11:00
|
|
|
let _node = { ...node }
|
2024-06-04 13:57:01 -04:00
|
|
|
const newSketchName = findUniqueName(
|
|
|
|
node,
|
|
|
|
KCL_DEFAULT_CONSTANT_PREFIXES.SKETCH
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
const _node1 = getNodeFromPath<VariableDeclarator>(
|
2024-03-22 10:23:04 +11:00
|
|
|
_node,
|
2024-06-04 13:57:01 -04:00
|
|
|
sketchPathToNode,
|
2024-03-22 10:23:04 +11:00
|
|
|
'VariableDeclarator',
|
|
|
|
true
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(_node1)) return _node1
|
|
|
|
const { node: oldSketchNode } = _node1
|
|
|
|
|
2023-02-12 10:56:45 +11:00
|
|
|
const oldSketchName = oldSketchNode.id.name
|
2024-06-24 11:45:40 -04:00
|
|
|
const _node2 = getNodeFromPath<CallExpression>(
|
2023-01-10 15:40:34 +11:00
|
|
|
_node,
|
2024-06-04 13:57:01 -04:00
|
|
|
sketchPathToNode,
|
2023-02-12 10:56:45 +11:00
|
|
|
'CallExpression'
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(_node2)) return _node2
|
|
|
|
const { node: expression } = _node2
|
|
|
|
|
|
|
|
const _node3 = getNodeFromPath<VariableDeclarator>(
|
2024-06-04 13:57:01 -04:00
|
|
|
_node,
|
|
|
|
extrudePathToNode,
|
|
|
|
'VariableDeclarator'
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(_node3)) return _node3
|
|
|
|
const { node: extrudeVarDec } = _node3
|
2024-06-04 13:57:01 -04:00
|
|
|
const extrudeName = extrudeVarDec.id?.name
|
2023-01-09 13:19:14 +11:00
|
|
|
|
2024-09-26 18:25:05 +10:00
|
|
|
let _tag
|
|
|
|
if (info.type !== 'cap') {
|
2024-06-24 11:45:40 -04:00
|
|
|
const __tag = addTagForSketchOnFace(
|
2024-03-22 10:23:04 +11:00
|
|
|
{
|
2024-06-04 13:57:01 -04:00
|
|
|
pathToNode: sketchPathToNode,
|
2024-03-22 10:23:04 +11:00
|
|
|
node: _node,
|
|
|
|
},
|
2024-09-26 18:25:05 +10:00
|
|
|
expression.callee.name,
|
|
|
|
info.type === 'edgeCut' ? info : null
|
2024-03-22 10:23:04 +11:00
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(__tag)) return __tag
|
|
|
|
const { modifiedAst, tag } = __tag
|
2024-06-24 22:39:04 -07:00
|
|
|
_tag = createIdentifier(tag)
|
2024-03-22 10:23:04 +11:00
|
|
|
_node = modifiedAst
|
|
|
|
} else {
|
2024-09-26 18:25:05 +10:00
|
|
|
_tag = createLiteral(info.subType.toUpperCase())
|
2024-03-22 10:23:04 +11:00
|
|
|
}
|
2023-02-12 10:56:45 +11:00
|
|
|
|
|
|
|
const newSketch = createVariableDeclaration(
|
|
|
|
newSketchName,
|
2024-03-22 10:23:04 +11:00
|
|
|
createCallExpressionStdLib('startSketchOn', [
|
2024-06-04 13:57:01 -04:00
|
|
|
createIdentifier(extrudeName ? extrudeName : oldSketchName),
|
2024-06-24 22:39:04 -07:00
|
|
|
_tag,
|
2023-02-12 10:56:45 +11:00
|
|
|
]),
|
|
|
|
'const'
|
|
|
|
)
|
2024-03-22 10:23:04 +11:00
|
|
|
|
2024-06-04 13:57:01 -04:00
|
|
|
const expressionIndex = Math.max(
|
|
|
|
sketchPathToNode[1][0] as number,
|
|
|
|
extrudePathToNode[1][0] as number
|
|
|
|
)
|
2023-02-12 10:56:45 +11:00
|
|
|
_node.body.splice(expressionIndex + 1, 0, newSketch)
|
2024-03-22 10:23:04 +11:00
|
|
|
const newpathToNode: PathToNode = [
|
|
|
|
['body', ''],
|
|
|
|
[expressionIndex + 1, 'index'],
|
|
|
|
['declarations', 'VariableDeclaration'],
|
|
|
|
[0, 'index'],
|
|
|
|
['init', 'VariableDeclarator'],
|
|
|
|
]
|
2023-02-12 10:56:45 +11:00
|
|
|
|
|
|
|
return {
|
2024-03-01 17:16:18 -08:00
|
|
|
modifiedAst: _node,
|
2024-03-22 10:23:04 +11:00
|
|
|
pathToNode: newpathToNode,
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
|
|
|
}
|
2023-01-09 13:19:14 +11:00
|
|
|
|
2023-03-10 08:35:30 +11:00
|
|
|
export const getLastIndex = (pathToNode: PathToNode): number =>
|
|
|
|
splitPathAtLastIndex(pathToNode).index
|
|
|
|
|
|
|
|
export function splitPathAtLastIndex(pathToNode: PathToNode): {
|
|
|
|
path: PathToNode
|
|
|
|
index: number
|
|
|
|
} {
|
2023-02-12 10:56:45 +11:00
|
|
|
const last = pathToNode[pathToNode.length - 1]
|
2023-04-01 16:47:00 +11:00
|
|
|
if (last && typeof last[0] === 'number') {
|
2023-03-10 08:35:30 +11:00
|
|
|
return {
|
|
|
|
path: pathToNode.slice(0, -1),
|
2023-04-01 16:47:00 +11:00
|
|
|
index: last[0],
|
2023-03-10 08:35:30 +11:00
|
|
|
}
|
2023-03-21 19:02:18 +11:00
|
|
|
} else if (pathToNode.length === 0) {
|
|
|
|
return {
|
|
|
|
path: [],
|
|
|
|
index: -1,
|
|
|
|
}
|
2023-01-09 13:19:14 +11:00
|
|
|
}
|
2023-03-10 08:35:30 +11:00
|
|
|
return splitPathAtLastIndex(pathToNode.slice(0, -1))
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
2023-01-09 13:19:14 +11:00
|
|
|
|
2023-04-01 16:47:00 +11:00
|
|
|
export function splitPathAtPipeExpression(pathToNode: PathToNode): {
|
|
|
|
path: PathToNode
|
|
|
|
index: number
|
|
|
|
} {
|
|
|
|
const last = pathToNode[pathToNode.length - 1]
|
|
|
|
|
|
|
|
if (
|
|
|
|
last &&
|
|
|
|
last[1] === 'index' &&
|
|
|
|
pathToNode?.[pathToNode.length - 2]?.[1] === 'PipeExpression' &&
|
|
|
|
typeof last[0] === 'number'
|
|
|
|
) {
|
|
|
|
return {
|
|
|
|
path: pathToNode.slice(0, -1),
|
|
|
|
index: last[0],
|
|
|
|
}
|
|
|
|
} else if (pathToNode.length === 0) {
|
|
|
|
return {
|
|
|
|
path: [],
|
|
|
|
index: -1,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return splitPathAtPipeExpression(pathToNode.slice(0, -1))
|
|
|
|
}
|
|
|
|
|
2023-02-12 10:56:45 +11:00
|
|
|
export function createLiteral(value: string | number): Literal {
|
|
|
|
return {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 0,
|
|
|
|
end: 0,
|
|
|
|
value,
|
|
|
|
raw: `${value}`,
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-24 22:39:04 -07:00
|
|
|
export function createTagDeclarator(value: string): TagDeclarator {
|
|
|
|
return {
|
|
|
|
type: 'TagDeclarator',
|
|
|
|
start: 0,
|
|
|
|
end: 0,
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2024-06-24 22:39:04 -07:00
|
|
|
value,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-12 10:56:45 +11:00
|
|
|
export function createIdentifier(name: string): Identifier {
|
|
|
|
return {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 0,
|
|
|
|
end: 0,
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2023-02-12 10:56:45 +11:00
|
|
|
name,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function createPipeSubstitution(): PipeSubstitution {
|
|
|
|
return {
|
|
|
|
type: 'PipeSubstitution',
|
|
|
|
start: 0,
|
|
|
|
end: 0,
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-05 16:02:27 -07:00
|
|
|
export function createCallExpressionStdLib(
|
|
|
|
name: string,
|
|
|
|
args: CallExpression['arguments']
|
|
|
|
): CallExpression {
|
|
|
|
return {
|
|
|
|
type: 'CallExpression',
|
|
|
|
start: 0,
|
|
|
|
end: 0,
|
|
|
|
callee: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 0,
|
|
|
|
end: 0,
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2023-09-05 16:02:27 -07:00
|
|
|
name,
|
|
|
|
},
|
|
|
|
optional: false,
|
|
|
|
arguments: args,
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2023-09-05 16:02:27 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-12 10:56:45 +11:00
|
|
|
export function createCallExpression(
|
|
|
|
name: string,
|
|
|
|
args: CallExpression['arguments']
|
|
|
|
): CallExpression {
|
|
|
|
return {
|
|
|
|
type: 'CallExpression',
|
|
|
|
start: 0,
|
|
|
|
end: 0,
|
|
|
|
callee: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 0,
|
|
|
|
end: 0,
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2023-02-12 10:56:45 +11:00
|
|
|
name,
|
|
|
|
},
|
|
|
|
optional: false,
|
|
|
|
arguments: args,
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function createArrayExpression(
|
|
|
|
elements: ArrayExpression['elements']
|
|
|
|
): ArrayExpression {
|
|
|
|
return {
|
|
|
|
type: 'ArrayExpression',
|
|
|
|
start: 0,
|
|
|
|
end: 0,
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2024-08-22 13:54:59 -05:00
|
|
|
nonCodeMeta: nonCodeMetaEmpty(),
|
2023-02-12 10:56:45 +11:00
|
|
|
elements,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function createPipeExpression(
|
|
|
|
body: PipeExpression['body']
|
|
|
|
): PipeExpression {
|
|
|
|
return {
|
2023-01-09 13:19:14 +11:00
|
|
|
type: 'PipeExpression',
|
2023-02-12 10:56:45 +11:00
|
|
|
start: 0,
|
|
|
|
end: 0,
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2023-02-12 10:56:45 +11:00
|
|
|
body,
|
2024-08-22 13:54:59 -05:00
|
|
|
nonCodeMeta: nonCodeMetaEmpty(),
|
2023-01-09 13:19:14 +11:00
|
|
|
}
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
export function createVariableDeclaration(
|
|
|
|
varName: string,
|
|
|
|
init: VariableDeclarator['init'],
|
|
|
|
kind: VariableDeclaration['kind'] = 'const'
|
|
|
|
): VariableDeclaration {
|
|
|
|
return {
|
2023-01-09 13:19:14 +11:00
|
|
|
type: 'VariableDeclaration',
|
2023-02-12 10:56:45 +11:00
|
|
|
start: 0,
|
|
|
|
end: 0,
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2023-01-09 13:19:14 +11:00
|
|
|
declarations: [
|
|
|
|
{
|
|
|
|
type: 'VariableDeclarator',
|
2023-02-12 10:56:45 +11:00
|
|
|
start: 0,
|
|
|
|
end: 0,
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2023-02-12 10:56:45 +11:00
|
|
|
id: createIdentifier(varName),
|
|
|
|
init,
|
2023-01-09 13:19:14 +11:00
|
|
|
},
|
|
|
|
],
|
2023-02-12 10:56:45 +11:00
|
|
|
kind,
|
2023-01-09 13:19:14 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-12 10:56:45 +11:00
|
|
|
export function createObjectExpression(properties: {
|
2024-08-12 15:38:42 -05:00
|
|
|
[key: string]: Expr
|
2023-02-12 10:56:45 +11:00
|
|
|
}): ObjectExpression {
|
|
|
|
return {
|
|
|
|
type: 'ObjectExpression',
|
|
|
|
start: 0,
|
|
|
|
end: 0,
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2024-08-22 13:54:59 -05:00
|
|
|
nonCodeMeta: nonCodeMetaEmpty(),
|
2023-02-12 10:56:45 +11:00
|
|
|
properties: Object.entries(properties).map(([key, value]) => ({
|
|
|
|
type: 'ObjectProperty',
|
|
|
|
start: 0,
|
|
|
|
end: 0,
|
|
|
|
key: createIdentifier(key),
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2023-02-12 10:56:45 +11:00
|
|
|
value,
|
|
|
|
})),
|
2023-01-09 13:19:14 +11:00
|
|
|
}
|
|
|
|
}
|
2023-03-02 21:19:11 +11:00
|
|
|
|
|
|
|
export function createUnaryExpression(
|
|
|
|
argument: UnaryExpression['argument'],
|
|
|
|
operator: UnaryExpression['operator'] = '-'
|
|
|
|
): UnaryExpression {
|
|
|
|
return {
|
|
|
|
type: 'UnaryExpression',
|
|
|
|
start: 0,
|
|
|
|
end: 0,
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2023-03-02 21:19:11 +11:00
|
|
|
operator,
|
|
|
|
argument,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function createBinaryExpression([left, operator, right]: [
|
|
|
|
BinaryExpression['left'],
|
|
|
|
BinaryExpression['operator'],
|
|
|
|
BinaryExpression['right']
|
|
|
|
]): BinaryExpression {
|
|
|
|
return {
|
|
|
|
type: 'BinaryExpression',
|
|
|
|
start: 0,
|
|
|
|
end: 0,
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2023-03-02 21:19:11 +11:00
|
|
|
operator,
|
|
|
|
left,
|
|
|
|
right,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-02 17:20:11 +10:00
|
|
|
export function createBinaryExpressionWithUnary([left, right]: [
|
|
|
|
BinaryExpression['left'],
|
|
|
|
BinaryExpression['right']
|
|
|
|
]): BinaryExpression {
|
|
|
|
if (right.type === 'UnaryExpression' && right.operator === '-')
|
|
|
|
return createBinaryExpression([left, '-', right.argument])
|
|
|
|
return createBinaryExpression([left, '+', right])
|
|
|
|
}
|
|
|
|
|
2023-03-02 21:19:11 +11:00
|
|
|
export function giveSketchFnCallTag(
|
|
|
|
ast: Program,
|
2023-04-03 16:05:25 +10:00
|
|
|
range: Selection['range'],
|
2023-03-07 15:45:59 +11:00
|
|
|
tag?: string
|
2024-06-24 11:45:40 -04:00
|
|
|
):
|
|
|
|
| {
|
|
|
|
modifiedAst: Program
|
|
|
|
tag: string
|
|
|
|
isTagExisting: boolean
|
|
|
|
pathToNode: PathToNode
|
|
|
|
}
|
|
|
|
| Error {
|
2023-04-14 07:49:36 +10:00
|
|
|
const path = getNodePathFromSourceRange(ast, range)
|
2024-06-24 11:45:40 -04:00
|
|
|
const _node1 = getNodeFromPath<CallExpression>(ast, path, 'CallExpression')
|
|
|
|
if (err(_node1)) return _node1
|
|
|
|
const { node: primaryCallExp } = _node1
|
|
|
|
|
2024-03-15 17:03:42 -04:00
|
|
|
// Tag is always 3rd expression now, using arg index feels brittle
|
|
|
|
// but we can come up with a better way to identify tag later.
|
|
|
|
const thirdArg = primaryCallExp.arguments?.[2]
|
2024-06-24 22:39:04 -07:00
|
|
|
const tagDeclarator =
|
|
|
|
thirdArg ||
|
|
|
|
(createTagDeclarator(tag || findUniqueName(ast, 'seg', 2)) as TagDeclarator)
|
2024-03-15 17:03:42 -04:00
|
|
|
const isTagExisting = !!thirdArg
|
|
|
|
if (!isTagExisting) {
|
2024-06-24 22:39:04 -07:00
|
|
|
primaryCallExp.arguments[2] = tagDeclarator
|
2024-03-15 17:03:42 -04:00
|
|
|
}
|
2024-06-24 22:39:04 -07:00
|
|
|
if ('value' in tagDeclarator) {
|
|
|
|
// Now TypeScript knows tagDeclarator has a value property
|
2024-03-15 17:03:42 -04:00
|
|
|
return {
|
|
|
|
modifiedAst: ast,
|
2024-06-24 22:39:04 -07:00
|
|
|
tag: String(tagDeclarator.value),
|
2024-03-15 17:03:42 -04:00
|
|
|
isTagExisting,
|
|
|
|
pathToNode: path,
|
|
|
|
}
|
|
|
|
} else {
|
2024-06-24 11:45:40 -04:00
|
|
|
return new Error('Unable to assign tag without value')
|
2023-03-02 21:19:11 +11:00
|
|
|
}
|
|
|
|
}
|
2023-04-01 16:47:00 +11:00
|
|
|
|
2024-05-24 20:54:42 +10:00
|
|
|
export function moveValueIntoNewVariablePath(
|
|
|
|
ast: Program,
|
|
|
|
programMemory: ProgramMemory,
|
|
|
|
pathToNode: PathToNode,
|
|
|
|
variableName: string
|
|
|
|
): {
|
|
|
|
modifiedAst: Program
|
|
|
|
pathToReplacedNode?: PathToNode
|
|
|
|
} {
|
2024-06-24 11:45:40 -04:00
|
|
|
const meta = isNodeSafeToReplacePath(ast, pathToNode)
|
|
|
|
if (trap(meta)) return { modifiedAst: ast }
|
|
|
|
const { isSafe, value, replacer } = meta
|
|
|
|
|
2024-05-24 20:54:42 +10:00
|
|
|
if (!isSafe || value.type === 'Identifier') return { modifiedAst: ast }
|
|
|
|
|
|
|
|
const { insertIndex } = findAllPreviousVariablesPath(
|
|
|
|
ast,
|
|
|
|
programMemory,
|
|
|
|
pathToNode
|
|
|
|
)
|
2024-07-25 20:11:46 -04:00
|
|
|
let _node = structuredClone(ast)
|
2024-05-24 20:54:42 +10:00
|
|
|
const boop = replacer(_node, variableName)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (trap(boop)) return { modifiedAst: ast }
|
|
|
|
|
2024-05-24 20:54:42 +10:00
|
|
|
_node = boop.modifiedAst
|
|
|
|
_node.body.splice(
|
|
|
|
insertIndex,
|
|
|
|
0,
|
|
|
|
createVariableDeclaration(variableName, value)
|
|
|
|
)
|
|
|
|
return { modifiedAst: _node, pathToReplacedNode: boop.pathToReplaced }
|
|
|
|
}
|
|
|
|
|
2023-04-01 16:47:00 +11:00
|
|
|
export function moveValueIntoNewVariable(
|
|
|
|
ast: Program,
|
|
|
|
programMemory: ProgramMemory,
|
2023-04-03 16:05:25 +10:00
|
|
|
sourceRange: Selection['range'],
|
2023-04-01 16:47:00 +11:00
|
|
|
variableName: string
|
|
|
|
): {
|
|
|
|
modifiedAst: Program
|
2024-05-24 20:54:42 +10:00
|
|
|
pathToReplacedNode?: PathToNode
|
2023-04-01 16:47:00 +11:00
|
|
|
} {
|
2024-06-24 11:45:40 -04:00
|
|
|
const meta = isNodeSafeToReplace(ast, sourceRange)
|
|
|
|
if (trap(meta)) return { modifiedAst: ast }
|
|
|
|
const { isSafe, value, replacer } = meta
|
2023-04-01 16:47:00 +11:00
|
|
|
if (!isSafe || value.type === 'Identifier') return { modifiedAst: ast }
|
|
|
|
|
|
|
|
const { insertIndex } = findAllPreviousVariables(
|
|
|
|
ast,
|
|
|
|
programMemory,
|
|
|
|
sourceRange
|
|
|
|
)
|
2024-07-25 20:11:46 -04:00
|
|
|
let _node = structuredClone(ast)
|
2024-06-24 11:45:40 -04:00
|
|
|
const replaced = replacer(_node, variableName)
|
|
|
|
if (trap(replaced)) return { modifiedAst: ast }
|
|
|
|
|
|
|
|
const { modifiedAst, pathToReplaced } = replaced
|
2024-05-24 20:54:42 +10:00
|
|
|
_node = modifiedAst
|
2023-04-01 16:47:00 +11:00
|
|
|
_node.body.splice(
|
|
|
|
insertIndex,
|
|
|
|
0,
|
|
|
|
createVariableDeclaration(variableName, value)
|
|
|
|
)
|
2024-05-24 20:54:42 +10:00
|
|
|
return { modifiedAst: _node, pathToReplacedNode: pathToReplaced }
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Deletes a segment from a pipe expression, if the segment has a tag that other segments use, it will remove that value and replace it with the equivalent literal
|
|
|
|
* @param dependentRanges - The ranges of the segments that are dependent on the segment being deleted, this is usually the output of `findUsesOfTagInPipe`
|
|
|
|
*/
|
|
|
|
export function deleteSegmentFromPipeExpression(
|
|
|
|
dependentRanges: SourceRange[],
|
|
|
|
modifiedAst: Program,
|
|
|
|
programMemory: ProgramMemory,
|
|
|
|
code: string,
|
|
|
|
pathToNode: PathToNode
|
2024-06-24 11:45:40 -04:00
|
|
|
): Program | Error {
|
2024-07-25 20:11:46 -04:00
|
|
|
let _modifiedAst = structuredClone(modifiedAst)
|
2024-05-24 20:54:42 +10:00
|
|
|
|
|
|
|
dependentRanges.forEach((range) => {
|
|
|
|
const path = getNodePathFromSourceRange(_modifiedAst, range)
|
|
|
|
|
|
|
|
const callExp = getNodeFromPath<CallExpression>(
|
|
|
|
_modifiedAst,
|
|
|
|
path,
|
|
|
|
'CallExpression',
|
|
|
|
true
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(callExp)) return callExp
|
|
|
|
|
2024-05-24 20:54:42 +10:00
|
|
|
const constraintInfo = getConstraintInfo(callExp.node, code, path).find(
|
|
|
|
({ sourceRange }) => isOverlap(sourceRange, range)
|
|
|
|
)
|
|
|
|
if (!constraintInfo) return
|
2024-06-24 11:45:40 -04:00
|
|
|
|
2024-09-13 21:14:14 +10:00
|
|
|
if (!constraintInfo.argPosition) return
|
2024-05-24 20:54:42 +10:00
|
|
|
const transform = removeSingleConstraintInfo(
|
2024-09-13 21:14:14 +10:00
|
|
|
callExp.shallowPath,
|
|
|
|
constraintInfo.argPosition,
|
2024-05-24 20:54:42 +10:00
|
|
|
_modifiedAst,
|
|
|
|
programMemory
|
|
|
|
)
|
|
|
|
if (!transform) return
|
|
|
|
_modifiedAst = transform.modifiedAst
|
|
|
|
})
|
|
|
|
|
|
|
|
const pipeExpression = getNodeFromPath<PipeExpression>(
|
|
|
|
_modifiedAst,
|
|
|
|
pathToNode,
|
|
|
|
'PipeExpression'
|
2024-06-24 11:45:40 -04:00
|
|
|
)
|
|
|
|
if (err(pipeExpression)) return pipeExpression
|
2024-05-24 20:54:42 +10:00
|
|
|
|
|
|
|
const pipeInPathIndex = pathToNode.findIndex(
|
|
|
|
([_, desc]) => desc === 'PipeExpression'
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
const segmentIndexInPipe = pathToNode[pipeInPathIndex + 1]
|
|
|
|
pipeExpression.node.body.splice(segmentIndexInPipe[0] as number, 1)
|
|
|
|
|
|
|
|
// Move up to the next segment.
|
|
|
|
segmentIndexInPipe[0] = Math.max((segmentIndexInPipe[0] as number) - 1, 0)
|
2024-05-24 20:54:42 +10:00
|
|
|
|
|
|
|
return _modifiedAst
|
|
|
|
}
|
|
|
|
|
|
|
|
export function removeSingleConstraintInfo(
|
2024-09-13 21:14:14 +10:00
|
|
|
pathToCallExp: PathToNode,
|
|
|
|
argDetails: SimplifiedArgDetails,
|
2024-05-24 20:54:42 +10:00
|
|
|
ast: Program,
|
|
|
|
programMemory: ProgramMemory
|
|
|
|
):
|
|
|
|
| {
|
|
|
|
modifiedAst: Program
|
|
|
|
pathToNodeMap: PathToNodeMap
|
|
|
|
}
|
|
|
|
| false {
|
|
|
|
const transform = removeSingleConstraint({
|
|
|
|
pathToCallExp,
|
2024-09-13 21:14:14 +10:00
|
|
|
inputDetails: argDetails,
|
2024-05-24 20:54:42 +10:00
|
|
|
ast,
|
|
|
|
})
|
|
|
|
if (!transform) return false
|
2024-06-24 11:45:40 -04:00
|
|
|
const retval = transformAstSketchLines({
|
2024-05-24 20:54:42 +10:00
|
|
|
ast,
|
|
|
|
selectionRanges: [pathToCallExp],
|
|
|
|
transformInfos: [transform],
|
|
|
|
programMemory,
|
|
|
|
referenceSegName: '',
|
|
|
|
})
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(retval)) return false
|
|
|
|
return retval
|
2023-04-01 16:47:00 +11:00
|
|
|
}
|
2024-06-29 10:36:04 -07:00
|
|
|
|
|
|
|
export async function deleteFromSelection(
|
|
|
|
ast: Program,
|
|
|
|
selection: Selection,
|
|
|
|
programMemory: ProgramMemory,
|
|
|
|
getFaceDetails: (id: string) => Promise<Models['FaceIsPlanar_type']> = () =>
|
|
|
|
({} as any)
|
|
|
|
): Promise<Program | Error> {
|
2024-07-25 20:11:46 -04:00
|
|
|
const astClone = structuredClone(ast)
|
2024-06-29 10:36:04 -07:00
|
|
|
const range = selection.range
|
|
|
|
const path = getNodePathFromSourceRange(ast, range)
|
|
|
|
const varDec = getNodeFromPath<VariableDeclarator>(
|
|
|
|
ast,
|
|
|
|
path,
|
|
|
|
'VariableDeclarator'
|
|
|
|
)
|
|
|
|
if (err(varDec)) return varDec
|
|
|
|
if (
|
|
|
|
(selection.type === 'extrude-wall' ||
|
|
|
|
selection.type === 'end-cap' ||
|
|
|
|
selection.type === 'start-cap') &&
|
|
|
|
varDec.node.init.type === 'PipeExpression'
|
|
|
|
) {
|
|
|
|
const varDecName = varDec.node.id.name
|
|
|
|
let pathToNode: PathToNode | null = null
|
|
|
|
let extrudeNameToDelete = ''
|
|
|
|
traverse(astClone, {
|
|
|
|
enter: (node, path) => {
|
|
|
|
if (node.type === 'VariableDeclaration') {
|
|
|
|
const dec = node.declarations[0]
|
|
|
|
if (
|
|
|
|
dec.init.type === 'CallExpression' &&
|
|
|
|
(dec.init.callee.name === 'extrude' ||
|
|
|
|
dec.init.callee.name === 'revolve') &&
|
|
|
|
dec.init.arguments?.[1].type === 'Identifier' &&
|
|
|
|
dec.init.arguments?.[1].name === varDecName
|
|
|
|
) {
|
|
|
|
pathToNode = path
|
|
|
|
extrudeNameToDelete = dec.id.name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
})
|
|
|
|
if (!pathToNode) return new Error('Could not find extrude variable')
|
|
|
|
|
|
|
|
const expressionIndex = pathToNode[1][0] as number
|
|
|
|
astClone.body.splice(expressionIndex, 1)
|
|
|
|
if (extrudeNameToDelete) {
|
2024-09-09 18:17:45 -04:00
|
|
|
await new Promise((resolve) => {
|
|
|
|
;(async () => {
|
|
|
|
let currentVariableName = ''
|
|
|
|
const pathsDependingOnExtrude: Array<{
|
|
|
|
path: PathToNode
|
|
|
|
sketchName: string
|
|
|
|
}> = []
|
|
|
|
traverse(astClone, {
|
|
|
|
leave: (node) => {
|
|
|
|
if (node.type === 'VariableDeclaration') {
|
|
|
|
currentVariableName = ''
|
|
|
|
}
|
|
|
|
},
|
|
|
|
enter: (node, path) => {
|
|
|
|
;(async () => {
|
|
|
|
if (node.type === 'VariableDeclaration') {
|
|
|
|
currentVariableName = node.declarations[0].id.name
|
|
|
|
}
|
|
|
|
if (
|
|
|
|
// match startSketchOn(${extrudeNameToDelete})
|
|
|
|
node.type === 'CallExpression' &&
|
|
|
|
node.callee.name === 'startSketchOn' &&
|
|
|
|
node.arguments[0].type === 'Identifier' &&
|
|
|
|
node.arguments[0].name === extrudeNameToDelete
|
|
|
|
) {
|
|
|
|
pathsDependingOnExtrude.push({
|
|
|
|
path,
|
|
|
|
sketchName: currentVariableName,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})().catch(reportRejection)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
const roundLiteral = (x: number) => createLiteral(roundOff(x))
|
|
|
|
const modificationDetails: {
|
|
|
|
parent: PipeExpression['body']
|
|
|
|
faceDetails: Models['FaceIsPlanar_type']
|
|
|
|
lastKey: number
|
|
|
|
}[] = []
|
|
|
|
for (const { path, sketchName } of pathsDependingOnExtrude) {
|
|
|
|
const parent = getNodeFromPath<PipeExpression['body']>(
|
|
|
|
astClone,
|
|
|
|
path.slice(0, -1)
|
|
|
|
)
|
|
|
|
if (err(parent)) {
|
|
|
|
return
|
2024-06-29 10:36:04 -07:00
|
|
|
}
|
2024-09-09 18:17:45 -04:00
|
|
|
const sketchToPreserve = sketchGroupFromKclValue(
|
|
|
|
programMemory.get(sketchName),
|
|
|
|
sketchName
|
|
|
|
)
|
|
|
|
if (err(sketchToPreserve)) return sketchToPreserve
|
|
|
|
console.log('sketchName', sketchName)
|
|
|
|
// Can't kick off multiple requests at once as getFaceDetails
|
|
|
|
// is three engine calls in one and they conflict
|
|
|
|
const faceDetails = await getFaceDetails(sketchToPreserve.on.id)
|
2024-06-29 10:36:04 -07:00
|
|
|
if (
|
2024-09-09 18:17:45 -04:00
|
|
|
!(
|
|
|
|
faceDetails.origin &&
|
|
|
|
faceDetails.x_axis &&
|
|
|
|
faceDetails.y_axis &&
|
|
|
|
faceDetails.z_axis
|
|
|
|
)
|
2024-06-29 10:36:04 -07:00
|
|
|
) {
|
2024-09-09 18:17:45 -04:00
|
|
|
return
|
2024-06-29 10:36:04 -07:00
|
|
|
}
|
2024-09-09 18:17:45 -04:00
|
|
|
const lastKey = Number(path.slice(-1)[0][0])
|
|
|
|
modificationDetails.push({
|
|
|
|
parent: parent.node,
|
|
|
|
faceDetails,
|
|
|
|
lastKey,
|
|
|
|
})
|
2024-06-29 10:36:04 -07:00
|
|
|
}
|
2024-09-09 18:17:45 -04:00
|
|
|
for (const { parent, faceDetails, lastKey } of modificationDetails) {
|
|
|
|
if (
|
|
|
|
!(
|
|
|
|
faceDetails.origin &&
|
|
|
|
faceDetails.x_axis &&
|
|
|
|
faceDetails.y_axis &&
|
|
|
|
faceDetails.z_axis
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
parent[lastKey] = createCallExpressionStdLib('startSketchOn', [
|
|
|
|
createObjectExpression({
|
|
|
|
plane: createObjectExpression({
|
|
|
|
origin: createObjectExpression({
|
|
|
|
x: roundLiteral(faceDetails.origin.x),
|
|
|
|
y: roundLiteral(faceDetails.origin.y),
|
|
|
|
z: roundLiteral(faceDetails.origin.z),
|
|
|
|
}),
|
|
|
|
x_axis: createObjectExpression({
|
|
|
|
x: roundLiteral(faceDetails.x_axis.x),
|
|
|
|
y: roundLiteral(faceDetails.x_axis.y),
|
|
|
|
z: roundLiteral(faceDetails.x_axis.z),
|
|
|
|
}),
|
|
|
|
y_axis: createObjectExpression({
|
|
|
|
x: roundLiteral(faceDetails.y_axis.x),
|
|
|
|
y: roundLiteral(faceDetails.y_axis.y),
|
|
|
|
z: roundLiteral(faceDetails.y_axis.z),
|
|
|
|
}),
|
|
|
|
z_axis: createObjectExpression({
|
|
|
|
x: roundLiteral(faceDetails.z_axis.x),
|
|
|
|
y: roundLiteral(faceDetails.z_axis.y),
|
|
|
|
z: roundLiteral(faceDetails.z_axis.z),
|
|
|
|
}),
|
2024-06-29 10:36:04 -07:00
|
|
|
}),
|
|
|
|
}),
|
2024-09-09 18:17:45 -04:00
|
|
|
])
|
|
|
|
}
|
|
|
|
resolve(true)
|
|
|
|
})().catch(reportRejection)
|
2024-06-29 10:36:04 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
// await prom
|
|
|
|
return astClone
|
|
|
|
} else if (varDec.node.init.type === 'PipeExpression') {
|
|
|
|
const pipeBody = varDec.node.init.body
|
|
|
|
if (
|
|
|
|
pipeBody[0].type === 'CallExpression' &&
|
|
|
|
pipeBody[0].callee.name === 'startSketchOn'
|
|
|
|
) {
|
|
|
|
// remove varDec
|
|
|
|
const varDecIndex = varDec.shallowPath[1][0] as number
|
|
|
|
astClone.body.splice(varDecIndex, 1)
|
|
|
|
return astClone
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return new Error('Selection not recognised, could not delete')
|
|
|
|
}
|
2024-08-22 13:54:59 -05:00
|
|
|
|
|
|
|
const nonCodeMetaEmpty = () => {
|
|
|
|
return { nonCodeNodes: {}, start: [], digest: null }
|
|
|
|
}
|