2023-02-21 10:50:45 +11:00
|
|
|
import {
|
|
|
|
ProgramMemory,
|
|
|
|
Path,
|
|
|
|
SketchGroup,
|
|
|
|
SourceRange,
|
|
|
|
PathToNode,
|
2023-02-12 10:56:45 +11:00
|
|
|
Program,
|
|
|
|
PipeExpression,
|
|
|
|
CallExpression,
|
|
|
|
VariableDeclarator,
|
2024-08-12 15:38:42 -05:00
|
|
|
Expr,
|
2023-03-19 18:46:39 +11:00
|
|
|
VariableDeclaration,
|
2024-06-24 22:39:04 -07:00
|
|
|
Identifier,
|
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,
|
2024-05-24 20:54:42 +10:00
|
|
|
} from 'lang/wasm'
|
2023-03-03 20:35:48 +11:00
|
|
|
import {
|
|
|
|
getNodeFromPath,
|
|
|
|
getNodeFromPathCurry,
|
|
|
|
getNodePathFromSourceRange,
|
2024-09-23 22:42:51 +10:00
|
|
|
getObjExprProperty,
|
2024-05-24 20:54:42 +10:00
|
|
|
} from 'lang/queryAst'
|
|
|
|
import {
|
|
|
|
isLiteralArrayOrStatic,
|
|
|
|
isNotLiteralArrayOrStatic,
|
|
|
|
} from 'lang/std/sketchcombos'
|
2024-07-02 17:16:27 +10:00
|
|
|
import { toolTips, ToolTip } from 'lang/langHelpers'
|
2024-06-30 04:53:47 +10:00
|
|
|
import { createPipeExpression, splitPathAtPipeExpression } from '../modifyAst'
|
2023-02-12 10:56:45 +11:00
|
|
|
|
2024-05-24 20:54:42 +10:00
|
|
|
import {
|
|
|
|
SketchLineHelper,
|
|
|
|
ConstrainInfo,
|
|
|
|
ArrayItemInput,
|
|
|
|
ObjectPropertyInput,
|
|
|
|
SingleValueInput,
|
2024-07-15 19:20:32 +10:00
|
|
|
AddTagInfo,
|
2024-09-13 21:14:14 +10:00
|
|
|
SegmentInputs,
|
|
|
|
SimplifiedArgDetails,
|
|
|
|
RawArgs,
|
|
|
|
CreatedSketchExprResult,
|
2024-05-24 20:54:42 +10:00
|
|
|
} from 'lang/std/stdTypes'
|
2023-02-12 10:56:45 +11:00
|
|
|
|
|
|
|
import {
|
|
|
|
createLiteral,
|
2024-06-24 22:39:04 -07:00
|
|
|
createTagDeclarator,
|
2023-02-12 10:56:45 +11:00
|
|
|
createCallExpression,
|
|
|
|
createArrayExpression,
|
|
|
|
createPipeSubstitution,
|
|
|
|
createObjectExpression,
|
|
|
|
mutateArrExp,
|
|
|
|
mutateObjExpProp,
|
|
|
|
findUniqueName,
|
2024-05-24 20:54:42 +10:00
|
|
|
} from 'lang/modifyAst'
|
|
|
|
import { roundOff, getLength, getAngle } from 'lib/utils'
|
2024-06-24 11:45:40 -04:00
|
|
|
import { err } from 'lib/trap'
|
2023-08-24 17:28:59 -07:00
|
|
|
import { perpendicularDistance } from 'sketch-helpers'
|
2024-06-24 22:39:04 -07:00
|
|
|
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
|
2024-09-26 18:25:05 +10:00
|
|
|
import { EdgeCutInfo } from 'machines/modelingMachine'
|
2023-02-12 10:56:45 +11:00
|
|
|
|
2024-09-13 21:14:14 +10:00
|
|
|
const STRAIGHT_SEGMENT_ERR = new Error(
|
|
|
|
'Invalid input, expected "straight-segment"'
|
|
|
|
)
|
2024-09-23 22:42:51 +10:00
|
|
|
const ARC_SEGMENT_ERR = new Error('Invalid input, expected "arc-segment"')
|
2024-09-13 21:14:14 +10:00
|
|
|
|
2023-02-12 10:56:45 +11:00
|
|
|
export type Coords2d = [number, number]
|
|
|
|
|
|
|
|
export function getCoordsFromPaths(skGroup: SketchGroup, index = 0): Coords2d {
|
|
|
|
const currentPath = skGroup?.value?.[index]
|
|
|
|
if (!currentPath && skGroup?.start) {
|
2023-03-17 08:27:40 +11:00
|
|
|
return skGroup.start.to
|
2023-02-12 10:56:45 +11:00
|
|
|
} else if (!currentPath) {
|
|
|
|
return [0, 0]
|
|
|
|
}
|
2024-02-11 12:59:00 +11:00
|
|
|
if (currentPath.type === 'ToPoint') {
|
2023-02-12 10:56:45 +11:00
|
|
|
return [currentPath.to[0], currentPath.to[1]]
|
|
|
|
}
|
|
|
|
return [0, 0]
|
|
|
|
}
|
|
|
|
|
2023-03-02 21:19:11 +11:00
|
|
|
export function createFirstArg(
|
2023-09-15 11:48:23 -04:00
|
|
|
sketchFn: ToolTip,
|
2024-08-12 15:38:42 -05:00
|
|
|
val: Expr | [Expr, Expr] | [Expr, Expr, Expr]
|
|
|
|
): Expr | Error {
|
2023-02-21 10:50:45 +11:00
|
|
|
if (Array.isArray(val)) {
|
|
|
|
if (
|
2024-03-15 17:03:42 -04:00
|
|
|
[
|
|
|
|
'angledLine',
|
|
|
|
'angledLineOfXLength',
|
|
|
|
'angledLineOfYLength',
|
|
|
|
'angledLineToX',
|
|
|
|
'angledLineToY',
|
|
|
|
'line',
|
|
|
|
'lineTo',
|
|
|
|
].includes(sketchFn)
|
2023-02-21 10:50:45 +11:00
|
|
|
)
|
2024-03-15 17:03:42 -04:00
|
|
|
return createArrayExpression(val)
|
2023-04-03 16:05:25 +10:00
|
|
|
if (['angledLineThatIntersects'].includes(sketchFn) && val[2])
|
|
|
|
return createObjectExpression({
|
|
|
|
angle: val[0],
|
|
|
|
offset: val[1],
|
|
|
|
intersectTag: val[2],
|
|
|
|
})
|
2023-02-21 10:50:45 +11:00
|
|
|
} else {
|
2024-03-15 17:03:42 -04:00
|
|
|
if (
|
|
|
|
['startSketchAt', 'xLine', 'xLineTo', 'yLine', 'yLineTo'].includes(
|
|
|
|
sketchFn
|
|
|
|
)
|
|
|
|
)
|
|
|
|
return val
|
2023-02-21 10:50:45 +11:00
|
|
|
}
|
2024-06-24 11:45:40 -04:00
|
|
|
return new Error('Missing sketch line type')
|
2023-02-21 10:50:45 +11:00
|
|
|
}
|
|
|
|
|
2024-05-24 20:54:42 +10:00
|
|
|
type AbbreviatedInput =
|
|
|
|
| ArrayItemInput<any>['index']
|
|
|
|
| ObjectPropertyInput<any>['key']
|
|
|
|
| SingleValueInput<any>['type']
|
2024-09-13 21:14:14 +10:00
|
|
|
| SimplifiedArgDetails
|
2024-05-24 20:54:42 +10:00
|
|
|
| undefined
|
|
|
|
|
|
|
|
const constrainInfo = (
|
|
|
|
a: ConstrainInfo['type'],
|
|
|
|
b: ConstrainInfo['isConstrained'],
|
|
|
|
c: ConstrainInfo['value'],
|
|
|
|
f: ConstrainInfo['stdLibFnName'],
|
|
|
|
g: AbbreviatedInput,
|
|
|
|
d: ConstrainInfo['sourceRange'],
|
|
|
|
e: ConstrainInfo['pathToNode']
|
|
|
|
): ConstrainInfo => ({
|
|
|
|
type: a,
|
|
|
|
isConstrained: b,
|
|
|
|
value: c,
|
|
|
|
sourceRange: d,
|
|
|
|
argPosition:
|
|
|
|
g === 'singleValue'
|
|
|
|
? { type: 'singleValue' }
|
|
|
|
: typeof g === 'number'
|
|
|
|
? { type: 'arrayItem', index: g }
|
|
|
|
: typeof g === 'string'
|
|
|
|
? { type: 'objectProperty', key: g }
|
|
|
|
: undefined,
|
|
|
|
pathToNode: e,
|
|
|
|
stdLibFnName: f,
|
|
|
|
})
|
|
|
|
|
|
|
|
const commonConstraintInfoHelper = (
|
|
|
|
callExp: CallExpression,
|
|
|
|
inputConstrainTypes: [ConstrainInfo['type'], ConstrainInfo['type']],
|
|
|
|
stdLibFnName: ConstrainInfo['stdLibFnName'],
|
|
|
|
abbreviatedInputs: [
|
|
|
|
{
|
|
|
|
arrayInput?: 0 | 1
|
|
|
|
objInput?: ObjectPropertyInput<any>['key']
|
|
|
|
},
|
|
|
|
{
|
|
|
|
arrayInput?: 0 | 1
|
|
|
|
objInput?: ObjectPropertyInput<any>['key']
|
|
|
|
}
|
|
|
|
],
|
|
|
|
code: string,
|
|
|
|
pathToNode: PathToNode
|
|
|
|
) => {
|
|
|
|
if (callExp.type !== 'CallExpression') return []
|
|
|
|
const firstArg = callExp.arguments?.[0]
|
|
|
|
const isArr = firstArg.type === 'ArrayExpression'
|
|
|
|
if (!isArr && firstArg.type !== 'ObjectExpression') return []
|
2024-09-13 21:14:14 +10:00
|
|
|
const pipeExpressionIndex = pathToNode.findIndex(
|
|
|
|
([_, nodeName]) => nodeName === 'PipeExpression'
|
|
|
|
)
|
|
|
|
const pathToBase = pathToNode.slice(0, pipeExpressionIndex + 2)
|
2024-05-24 20:54:42 +10:00
|
|
|
const pathToArrayExpression: PathToNode = [
|
2024-09-13 21:14:14 +10:00
|
|
|
...pathToBase,
|
2024-05-24 20:54:42 +10:00
|
|
|
['arguments', 'CallExpression'],
|
|
|
|
[0, 'index'],
|
|
|
|
isArr
|
|
|
|
? ['elements', 'ArrayExpression']
|
|
|
|
: ['properties', 'ObjectExpression'],
|
|
|
|
]
|
|
|
|
const pathToFirstArg: PathToNode = isArr
|
|
|
|
? [...pathToArrayExpression, [0, 'index']]
|
|
|
|
: [
|
|
|
|
...pathToArrayExpression,
|
|
|
|
[
|
|
|
|
firstArg.properties.findIndex(
|
|
|
|
(a) => a.key.name === abbreviatedInputs[0].objInput
|
|
|
|
),
|
|
|
|
'index',
|
|
|
|
],
|
|
|
|
['value', 'Property'],
|
|
|
|
]
|
|
|
|
|
|
|
|
const pathToSecondArg: PathToNode = isArr
|
|
|
|
? [...pathToArrayExpression, [1, 'index']]
|
|
|
|
: [
|
|
|
|
...pathToArrayExpression,
|
|
|
|
[
|
|
|
|
firstArg.properties.findIndex(
|
|
|
|
(a) => a.key.name === abbreviatedInputs[1].objInput
|
|
|
|
),
|
|
|
|
'index',
|
|
|
|
],
|
|
|
|
['value', 'Property'],
|
|
|
|
]
|
|
|
|
|
|
|
|
const input1 = isArr
|
|
|
|
? firstArg.elements[0]
|
|
|
|
: firstArg.properties.find(
|
|
|
|
(a) => a.key.name === abbreviatedInputs[0].objInput
|
|
|
|
)?.value
|
|
|
|
const input2 = isArr
|
|
|
|
? firstArg.elements[1]
|
|
|
|
: firstArg.properties.find(
|
|
|
|
(a) => a.key.name === abbreviatedInputs[1].objInput
|
|
|
|
)?.value
|
|
|
|
|
|
|
|
const constraints: ConstrainInfo[] = []
|
|
|
|
if (input1)
|
|
|
|
constraints.push(
|
|
|
|
constrainInfo(
|
|
|
|
inputConstrainTypes[0],
|
|
|
|
isNotLiteralArrayOrStatic(input1),
|
|
|
|
code.slice(input1.start, input1.end),
|
|
|
|
stdLibFnName,
|
|
|
|
isArr ? abbreviatedInputs[0].arrayInput : abbreviatedInputs[0].objInput,
|
|
|
|
[input1.start, input1.end],
|
|
|
|
pathToFirstArg
|
|
|
|
)
|
|
|
|
)
|
|
|
|
if (input2)
|
|
|
|
constraints.push(
|
|
|
|
constrainInfo(
|
|
|
|
inputConstrainTypes[1],
|
|
|
|
isNotLiteralArrayOrStatic(input2),
|
|
|
|
code.slice(input2.start, input2.end),
|
|
|
|
stdLibFnName,
|
|
|
|
isArr ? abbreviatedInputs[1].arrayInput : abbreviatedInputs[1].objInput,
|
|
|
|
[input2.start, input2.end],
|
|
|
|
pathToSecondArg
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
return constraints
|
|
|
|
}
|
|
|
|
|
|
|
|
const horzVertConstraintInfoHelper = (
|
|
|
|
callExp: CallExpression,
|
|
|
|
inputConstrainTypes: [ConstrainInfo['type'], ConstrainInfo['type']],
|
|
|
|
stdLibFnName: ConstrainInfo['stdLibFnName'],
|
|
|
|
abbreviatedInput: AbbreviatedInput,
|
|
|
|
code: string,
|
|
|
|
pathToNode: PathToNode
|
|
|
|
) => {
|
|
|
|
if (callExp.type !== 'CallExpression') return []
|
|
|
|
const firstArg = callExp.arguments?.[0]
|
|
|
|
const callee = callExp.callee
|
|
|
|
const pathToFirstArg: PathToNode = [
|
|
|
|
...pathToNode,
|
|
|
|
['arguments', 'CallExpression'],
|
|
|
|
[0, 'index'],
|
|
|
|
]
|
|
|
|
const pathToCallee: PathToNode = [...pathToNode, ['callee', 'CallExpression']]
|
|
|
|
return [
|
|
|
|
constrainInfo(
|
|
|
|
inputConstrainTypes[0],
|
|
|
|
true,
|
|
|
|
callee.name,
|
|
|
|
stdLibFnName,
|
|
|
|
undefined,
|
|
|
|
[callee.start, callee.end],
|
|
|
|
pathToCallee
|
|
|
|
),
|
|
|
|
constrainInfo(
|
|
|
|
inputConstrainTypes[1],
|
|
|
|
isNotLiteralArrayOrStatic(callExp.arguments?.[0]),
|
|
|
|
code.slice(firstArg.start, firstArg.end),
|
|
|
|
stdLibFnName,
|
|
|
|
abbreviatedInput,
|
|
|
|
[firstArg.start, firstArg.end],
|
|
|
|
pathToFirstArg
|
|
|
|
),
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
2024-07-15 19:20:32 +10:00
|
|
|
function getTag(index = 2): SketchLineHelper['getTag'] {
|
|
|
|
return (callExp: CallExpression) => {
|
|
|
|
if (callExp.type !== 'CallExpression')
|
|
|
|
return new Error('Not a CallExpression')
|
|
|
|
const arg = callExp.arguments?.[index]
|
|
|
|
if (!arg) return new Error('No argument')
|
|
|
|
if (arg.type !== 'TagDeclarator')
|
|
|
|
return new Error('Tag not a TagDeclarator')
|
|
|
|
return arg.value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-12 10:56:45 +11:00
|
|
|
export const lineTo: SketchLineHelper = {
|
2024-09-13 21:14:14 +10:00
|
|
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
|
|
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const to = segmentInput.to
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<PipeExpression>(
|
2023-02-12 10:56:45 +11:00
|
|
|
_node,
|
|
|
|
pathToNode,
|
|
|
|
'PipeExpression'
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
const { node: pipe } = nodeMeta
|
2023-03-05 07:34:56 +11:00
|
|
|
|
2024-08-12 15:38:42 -05:00
|
|
|
const newVals: [Expr, Expr] = [
|
2023-03-05 07:34:56 +11:00
|
|
|
createLiteral(roundOff(to[0], 2)),
|
|
|
|
createLiteral(roundOff(to[1], 2)),
|
|
|
|
]
|
|
|
|
|
2023-03-07 15:45:59 +11:00
|
|
|
const newLine = createCallExpression('lineTo', [
|
|
|
|
createArrayExpression(newVals),
|
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
2023-04-01 16:47:00 +11:00
|
|
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
2024-09-13 21:14:14 +10:00
|
|
|
if (replaceExistingCallback) {
|
|
|
|
const result = replaceExistingCallback([
|
|
|
|
{
|
|
|
|
type: 'arrayItem',
|
|
|
|
index: 0,
|
|
|
|
argType: 'xAbsolute',
|
|
|
|
expr: createLiteral(roundOff(to[0], 2)),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'arrayItem',
|
|
|
|
index: 1,
|
|
|
|
argType: 'yAbsolute',
|
|
|
|
expr: createLiteral(roundOff(to[1], 2)),
|
|
|
|
},
|
|
|
|
])
|
|
|
|
if (err(result)) return result
|
|
|
|
const { callExp, valueUsedInTransform } = result
|
2023-03-10 08:48:50 +11:00
|
|
|
pipe.body[callIndex] = callExp
|
2023-03-07 15:45:59 +11:00
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
2023-03-10 08:48:50 +11:00
|
|
|
valueUsedInTransform: valueUsedInTransform,
|
2023-03-07 15:45:59 +11:00
|
|
|
}
|
2023-03-05 07:34:56 +11:00
|
|
|
} else {
|
|
|
|
pipe.body = [...pipe.body, newLine]
|
|
|
|
}
|
2023-02-12 10:56:45 +11:00
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-09-13 21:14:14 +10:00
|
|
|
updateArgs: ({ node, pathToNode, input }) => {
|
|
|
|
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { to } = input
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
const { node: callExpression } = nodeMeta
|
2023-02-12 10:56:45 +11:00
|
|
|
|
|
|
|
const toArrExp = createArrayExpression([
|
|
|
|
createLiteral(to[0]),
|
|
|
|
createLiteral(to[1]),
|
|
|
|
])
|
|
|
|
|
|
|
|
mutateArrExp(callExpression.arguments?.[0], toArrExp) ||
|
|
|
|
mutateObjExpProp(callExpression.arguments?.[0], toArrExp, 'to')
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-07-15 19:20:32 +10:00
|
|
|
getTag: getTag(),
|
2024-03-17 18:24:03 +11:00
|
|
|
addTag: addTag(),
|
2024-05-24 20:54:42 +10:00
|
|
|
getConstraintInfo: (callExp, ...args) =>
|
|
|
|
commonConstraintInfoHelper(
|
|
|
|
callExp,
|
|
|
|
['xAbsolute', 'yAbsolute'],
|
|
|
|
'lineTo',
|
|
|
|
[{ arrayInput: 0 }, { arrayInput: 1 }],
|
|
|
|
...args
|
|
|
|
),
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
export const line: SketchLineHelper = {
|
|
|
|
add: ({
|
|
|
|
node,
|
|
|
|
previousProgramMemory,
|
|
|
|
pathToNode,
|
2024-09-13 21:14:14 +10:00
|
|
|
segmentInput,
|
|
|
|
replaceExistingCallback,
|
2024-04-03 13:22:56 +11:00
|
|
|
spliceBetween,
|
2023-02-12 10:56:45 +11:00
|
|
|
}) => {
|
2024-09-13 21:14:14 +10:00
|
|
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { from, to } = segmentInput
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<PipeExpression | CallExpression>(
|
2023-02-12 10:56:45 +11:00
|
|
|
_node,
|
|
|
|
pathToNode,
|
|
|
|
'PipeExpression'
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
const { node: pipe } = nodeMeta
|
|
|
|
const nodeMeta2 = getNodeFromPath<VariableDeclarator>(
|
2023-02-12 10:56:45 +11:00
|
|
|
_node,
|
|
|
|
pathToNode,
|
|
|
|
'VariableDeclarator'
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(nodeMeta2)) return nodeMeta2
|
|
|
|
const { node: varDec } = nodeMeta2
|
2023-03-02 21:19:11 +11:00
|
|
|
|
|
|
|
const newXVal = createLiteral(roundOff(to[0] - from[0], 2))
|
|
|
|
const newYVal = createLiteral(roundOff(to[1] - from[1], 2))
|
|
|
|
|
2024-09-13 21:14:14 +10:00
|
|
|
if (
|
|
|
|
spliceBetween &&
|
|
|
|
!replaceExistingCallback &&
|
|
|
|
pipe.type === 'PipeExpression'
|
|
|
|
) {
|
2024-04-03 13:22:56 +11:00
|
|
|
const callExp = createCallExpression('line', [
|
|
|
|
createArrayExpression([newXVal, newYVal]),
|
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
|
|
|
const pathToNodeIndex = pathToNode.findIndex(
|
|
|
|
(x) => x[1] === 'PipeExpression'
|
|
|
|
)
|
|
|
|
const pipeIndex = pathToNode[pathToNodeIndex + 1][0]
|
|
|
|
if (typeof pipeIndex === 'undefined' || typeof pipeIndex === 'string') {
|
2024-06-24 11:45:40 -04:00
|
|
|
return new Error('pipeIndex is undefined')
|
2024-04-03 13:22:56 +11:00
|
|
|
}
|
|
|
|
pipe.body = [
|
|
|
|
...pipe.body.slice(0, pipeIndex),
|
|
|
|
callExp,
|
|
|
|
...pipe.body.slice(pipeIndex),
|
|
|
|
]
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-13 21:14:14 +10:00
|
|
|
if (replaceExistingCallback && pipe.type !== 'CallExpression') {
|
2023-04-01 16:47:00 +11:00
|
|
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
2024-09-13 21:14:14 +10:00
|
|
|
const result = replaceExistingCallback([
|
|
|
|
{
|
|
|
|
type: 'arrayItem',
|
|
|
|
index: 0,
|
|
|
|
argType: 'xRelative',
|
|
|
|
expr: createLiteral(roundOff(to[0] - from[0], 2)),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'arrayItem',
|
|
|
|
index: 1,
|
|
|
|
argType: 'yRelative',
|
|
|
|
expr: createLiteral(roundOff(to[1] - from[1], 2)),
|
|
|
|
},
|
|
|
|
])
|
|
|
|
if (err(result)) return result
|
|
|
|
const { callExp, valueUsedInTransform } = result
|
2023-03-17 21:15:46 +11:00
|
|
|
pipe.body[callIndex] = callExp
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
2024-06-24 11:45:40 -04:00
|
|
|
pathToNode: [...pathToNode],
|
2023-03-17 21:15:46 +11:00
|
|
|
valueUsedInTransform,
|
|
|
|
}
|
2023-03-02 21:19:11 +11:00
|
|
|
}
|
|
|
|
|
2023-03-17 21:15:46 +11:00
|
|
|
const callExp = createCallExpression('line', [
|
|
|
|
createArrayExpression([newXVal, newYVal]),
|
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
2023-09-13 08:36:47 +10:00
|
|
|
if (pipe.type === 'PipeExpression') {
|
|
|
|
pipe.body = [...pipe.body, callExp]
|
2023-10-14 03:47:46 +11:00
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode: [
|
|
|
|
...pathToNode,
|
|
|
|
['body', 'PipeExpression'],
|
|
|
|
[pipe.body.length - 1, 'CallExpression'],
|
|
|
|
],
|
|
|
|
}
|
2023-09-13 08:36:47 +10:00
|
|
|
} else {
|
|
|
|
varDec.init = createPipeExpression([varDec.init, callExp])
|
|
|
|
}
|
2023-02-12 10:56:45 +11:00
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-09-13 21:14:14 +10:00
|
|
|
updateArgs: ({ node, pathToNode, input }) => {
|
|
|
|
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { to, from } = input
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
const { node: callExpression } = nodeMeta
|
2023-02-12 10:56:45 +11:00
|
|
|
|
|
|
|
const toArrExp = createArrayExpression([
|
|
|
|
createLiteral(roundOff(to[0] - from[0], 2)),
|
|
|
|
createLiteral(roundOff(to[1] - from[1], 2)),
|
|
|
|
])
|
|
|
|
|
2023-09-13 08:36:47 +10:00
|
|
|
if (callExpression.arguments?.[0].type === 'ObjectExpression') {
|
2023-03-19 18:46:39 +11:00
|
|
|
mutateObjExpProp(callExpression.arguments?.[0], toArrExp, 'to')
|
2023-03-17 15:53:20 +11:00
|
|
|
} else {
|
2023-03-19 18:46:39 +11:00
|
|
|
mutateArrExp(callExpression.arguments?.[0], toArrExp)
|
2023-03-17 15:53:20 +11:00
|
|
|
}
|
2023-02-12 10:56:45 +11:00
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-07-15 19:20:32 +10:00
|
|
|
getTag: getTag(),
|
2024-03-17 18:24:03 +11:00
|
|
|
addTag: addTag(),
|
2024-05-24 20:54:42 +10:00
|
|
|
getConstraintInfo: (callExp, ...args) =>
|
|
|
|
commonConstraintInfoHelper(
|
|
|
|
callExp,
|
|
|
|
['xRelative', 'yRelative'],
|
|
|
|
'line',
|
|
|
|
[{ arrayInput: 0 }, { arrayInput: 1 }],
|
|
|
|
...args
|
|
|
|
),
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
export const xLineTo: SketchLineHelper = {
|
2024-09-13 21:14:14 +10:00
|
|
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
|
|
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { to } = segmentInput
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2023-02-21 10:50:45 +11:00
|
|
|
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
2024-06-24 11:45:40 -04:00
|
|
|
const _node1 = getNode<PipeExpression>('PipeExpression')
|
|
|
|
if (err(_node1)) return _node1
|
|
|
|
const { node: pipe } = _node1
|
2023-02-21 10:50:45 +11:00
|
|
|
|
|
|
|
const newVal = createLiteral(roundOff(to[0], 2))
|
|
|
|
|
2024-09-13 21:14:14 +10:00
|
|
|
if (replaceExistingCallback) {
|
2023-04-01 16:47:00 +11:00
|
|
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
2024-09-13 21:14:14 +10:00
|
|
|
const result = replaceExistingCallback([
|
|
|
|
{
|
|
|
|
type: 'singleValue',
|
|
|
|
argType: 'xAbsolute',
|
|
|
|
expr: createLiteral(roundOff(to[0], 2)),
|
|
|
|
},
|
|
|
|
])
|
|
|
|
if (err(result)) return result
|
|
|
|
const { callExp, valueUsedInTransform } = result
|
2023-03-17 21:15:46 +11:00
|
|
|
pipe.body[callIndex] = callExp
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
valueUsedInTransform,
|
|
|
|
}
|
2023-02-21 10:50:45 +11:00
|
|
|
}
|
2023-03-17 21:15:46 +11:00
|
|
|
const callExp = createCallExpression('xLineTo', [
|
|
|
|
newVal,
|
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
|
|
|
pipe.body = [...pipe.body, callExp]
|
2023-02-12 10:56:45 +11:00
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-09-13 21:14:14 +10:00
|
|
|
updateArgs: ({ node, pathToNode, input }) => {
|
|
|
|
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { to } = input
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
const { node: callExpression } = nodeMeta
|
2023-02-12 10:56:45 +11:00
|
|
|
const newX = createLiteral(roundOff(to[0], 2))
|
2023-09-13 07:23:14 -07:00
|
|
|
if (isLiteralArrayOrStatic(callExpression.arguments?.[0])) {
|
2023-02-12 10:56:45 +11:00
|
|
|
callExpression.arguments[0] = newX
|
|
|
|
} else {
|
|
|
|
mutateObjExpProp(callExpression.arguments?.[0], newX, 'to')
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-07-15 19:20:32 +10:00
|
|
|
getTag: getTag(),
|
2024-03-17 18:24:03 +11:00
|
|
|
addTag: addTag(),
|
2024-05-24 20:54:42 +10:00
|
|
|
getConstraintInfo: (callExp, ...args) =>
|
|
|
|
horzVertConstraintInfoHelper(
|
|
|
|
callExp,
|
|
|
|
['horizontal', 'xAbsolute'],
|
|
|
|
'xLineTo',
|
|
|
|
'singleValue',
|
|
|
|
...args
|
|
|
|
),
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
export const yLineTo: SketchLineHelper = {
|
2024-09-13 21:14:14 +10:00
|
|
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
|
|
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { to } = segmentInput
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2023-02-21 10:50:45 +11:00
|
|
|
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
2024-06-24 11:45:40 -04:00
|
|
|
const _node1 = getNode<PipeExpression>('PipeExpression')
|
|
|
|
if (err(_node1)) return _node1
|
|
|
|
const { node: pipe } = _node1
|
2023-02-21 10:50:45 +11:00
|
|
|
|
|
|
|
const newVal = createLiteral(roundOff(to[1], 2))
|
2023-03-17 21:15:46 +11:00
|
|
|
|
2024-09-13 21:14:14 +10:00
|
|
|
if (replaceExistingCallback) {
|
2023-04-01 16:47:00 +11:00
|
|
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
2024-09-13 21:14:14 +10:00
|
|
|
const result = replaceExistingCallback([
|
|
|
|
{
|
|
|
|
type: 'singleValue',
|
|
|
|
argType: 'yAbsolute',
|
|
|
|
expr: newVal,
|
|
|
|
},
|
|
|
|
])
|
|
|
|
if (err(result)) return result
|
|
|
|
const { callExp, valueUsedInTransform } = result
|
2023-03-17 21:15:46 +11:00
|
|
|
pipe.body[callIndex] = callExp
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
valueUsedInTransform,
|
|
|
|
}
|
2023-02-21 10:50:45 +11:00
|
|
|
}
|
2023-03-17 21:15:46 +11:00
|
|
|
const callExp = createCallExpression('yLineTo', [
|
|
|
|
newVal,
|
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
|
|
|
pipe.body = [...pipe.body, callExp]
|
2023-02-12 10:56:45 +11:00
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-09-13 21:14:14 +10:00
|
|
|
updateArgs: ({ node, pathToNode, input }) => {
|
|
|
|
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { to } = input
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
const { node: callExpression } = nodeMeta
|
2023-02-12 10:56:45 +11:00
|
|
|
const newY = createLiteral(roundOff(to[1], 2))
|
2023-09-13 07:23:14 -07:00
|
|
|
if (isLiteralArrayOrStatic(callExpression.arguments?.[0])) {
|
2023-02-12 10:56:45 +11:00
|
|
|
callExpression.arguments[0] = newY
|
|
|
|
} else {
|
|
|
|
mutateObjExpProp(callExpression.arguments?.[0], newY, 'to')
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-07-15 19:20:32 +10:00
|
|
|
getTag: getTag(),
|
2024-03-17 18:24:03 +11:00
|
|
|
addTag: addTag(),
|
2024-05-24 20:54:42 +10:00
|
|
|
getConstraintInfo: (callExp, ...args) =>
|
|
|
|
horzVertConstraintInfoHelper(
|
|
|
|
callExp,
|
|
|
|
['vertical', 'yAbsolute'],
|
|
|
|
'yLineTo',
|
|
|
|
'singleValue',
|
|
|
|
...args
|
|
|
|
),
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
export const xLine: SketchLineHelper = {
|
2024-09-13 21:14:14 +10:00
|
|
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
|
|
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { from, to } = segmentInput
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2023-02-21 10:50:45 +11:00
|
|
|
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
2024-06-24 11:45:40 -04:00
|
|
|
const _node1 = getNode<PipeExpression>('PipeExpression')
|
|
|
|
if (err(_node1)) return _node1
|
|
|
|
const { node: pipe } = _node1
|
2023-02-21 10:50:45 +11:00
|
|
|
|
|
|
|
const newVal = createLiteral(roundOff(to[0] - from[0], 2))
|
2023-03-10 14:55:16 +11:00
|
|
|
|
2024-09-13 21:14:14 +10:00
|
|
|
if (replaceExistingCallback) {
|
2023-04-01 16:47:00 +11:00
|
|
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
2024-09-13 21:14:14 +10:00
|
|
|
const result = replaceExistingCallback([
|
|
|
|
{
|
|
|
|
type: 'singleValue',
|
|
|
|
argType: 'xRelative',
|
|
|
|
expr: newVal,
|
|
|
|
},
|
|
|
|
])
|
|
|
|
if (err(result)) return result
|
|
|
|
const { callExp, valueUsedInTransform } = result
|
2023-03-10 14:55:16 +11:00
|
|
|
pipe.body[callIndex] = callExp
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
valueUsedInTransform,
|
|
|
|
}
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
2023-03-10 14:55:16 +11:00
|
|
|
|
|
|
|
const newLine = createCallExpression('xLine', [
|
2024-09-13 21:14:14 +10:00
|
|
|
newVal,
|
2023-03-10 14:55:16 +11:00
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
|
|
|
pipe.body = [...pipe.body, newLine]
|
2023-02-21 10:50:45 +11:00
|
|
|
return { modifiedAst: _node, pathToNode }
|
2023-02-12 10:56:45 +11:00
|
|
|
},
|
2024-09-13 21:14:14 +10:00
|
|
|
updateArgs: ({ node, pathToNode, input }) => {
|
|
|
|
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { to, from } = input
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
const { node: callExpression } = nodeMeta
|
2023-02-12 10:56:45 +11:00
|
|
|
const newX = createLiteral(roundOff(to[0] - from[0], 2))
|
2023-09-13 07:23:14 -07:00
|
|
|
if (isLiteralArrayOrStatic(callExpression.arguments?.[0])) {
|
2023-02-12 10:56:45 +11:00
|
|
|
callExpression.arguments[0] = newX
|
|
|
|
} else {
|
|
|
|
mutateObjExpProp(callExpression.arguments?.[0], newX, 'length')
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-07-15 19:20:32 +10:00
|
|
|
getTag: getTag(),
|
2024-03-17 18:24:03 +11:00
|
|
|
addTag: addTag(),
|
2024-05-24 20:54:42 +10:00
|
|
|
getConstraintInfo: (callExp, ...args) =>
|
|
|
|
horzVertConstraintInfoHelper(
|
|
|
|
callExp,
|
|
|
|
['horizontal', 'xRelative'],
|
|
|
|
'xLine',
|
|
|
|
'singleValue',
|
|
|
|
...args
|
|
|
|
),
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
export const yLine: SketchLineHelper = {
|
2024-09-13 21:14:14 +10:00
|
|
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
|
|
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { from, to } = segmentInput
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2023-02-21 10:50:45 +11:00
|
|
|
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
2024-06-24 11:45:40 -04:00
|
|
|
const _node1 = getNode<PipeExpression>('PipeExpression')
|
|
|
|
if (err(_node1)) return _node1
|
|
|
|
const { node: pipe } = _node1
|
2023-02-21 10:50:45 +11:00
|
|
|
const newVal = createLiteral(roundOff(to[1] - from[1], 2))
|
2024-09-13 21:14:14 +10:00
|
|
|
if (replaceExistingCallback) {
|
2023-04-01 16:47:00 +11:00
|
|
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
2024-09-13 21:14:14 +10:00
|
|
|
const result = replaceExistingCallback([
|
|
|
|
{
|
|
|
|
type: 'singleValue',
|
|
|
|
argType: 'yRelative',
|
|
|
|
expr: newVal,
|
|
|
|
},
|
|
|
|
])
|
|
|
|
if (err(result)) return result
|
|
|
|
const { callExp, valueUsedInTransform } = result
|
2023-03-10 14:55:16 +11:00
|
|
|
pipe.body[callIndex] = callExp
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
valueUsedInTransform,
|
|
|
|
}
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
2023-03-10 14:55:16 +11:00
|
|
|
|
|
|
|
const newLine = createCallExpression('yLine', [
|
|
|
|
newVal,
|
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
|
|
|
pipe.body = [...pipe.body, newLine]
|
2023-02-21 10:50:45 +11:00
|
|
|
return { modifiedAst: _node, pathToNode }
|
2023-02-12 10:56:45 +11:00
|
|
|
},
|
2024-09-13 21:14:14 +10:00
|
|
|
updateArgs: ({ node, pathToNode, input }) => {
|
|
|
|
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { to, from } = input
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
const { node: callExpression } = nodeMeta
|
2023-02-12 10:56:45 +11:00
|
|
|
const newY = createLiteral(roundOff(to[1] - from[1], 2))
|
2023-09-13 07:23:14 -07:00
|
|
|
if (isLiteralArrayOrStatic(callExpression.arguments?.[0])) {
|
2023-02-12 10:56:45 +11:00
|
|
|
callExpression.arguments[0] = newY
|
|
|
|
} else {
|
|
|
|
mutateObjExpProp(callExpression.arguments?.[0], newY, 'length')
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-07-15 19:20:32 +10:00
|
|
|
getTag: getTag(),
|
2024-03-17 18:24:03 +11:00
|
|
|
addTag: addTag(),
|
2024-05-24 20:54:42 +10:00
|
|
|
getConstraintInfo: (callExp, ...args) =>
|
|
|
|
horzVertConstraintInfoHelper(
|
|
|
|
callExp,
|
|
|
|
['vertical', 'yRelative'],
|
|
|
|
'yLine',
|
|
|
|
'singleValue',
|
|
|
|
...args
|
|
|
|
),
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
|
|
|
|
2024-02-11 12:59:00 +11:00
|
|
|
export const tangentialArcTo: SketchLineHelper = {
|
2024-09-13 21:14:14 +10:00
|
|
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
|
|
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { to } = segmentInput
|
2024-02-11 12:59:00 +11:00
|
|
|
const _node = { ...node }
|
|
|
|
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
2024-06-24 11:45:40 -04:00
|
|
|
const _node1 = getNode<PipeExpression | CallExpression>('PipeExpression')
|
|
|
|
if (err(_node1)) return _node1
|
|
|
|
const { node: pipe } = _node1
|
|
|
|
const _node2 = getNodeFromPath<VariableDeclarator>(
|
2024-02-11 12:59:00 +11:00
|
|
|
_node,
|
|
|
|
pathToNode,
|
|
|
|
'VariableDeclarator'
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(_node2)) return _node2
|
|
|
|
const { node: varDec } = _node2
|
2024-02-11 12:59:00 +11:00
|
|
|
|
|
|
|
const toX = createLiteral(roundOff(to[0], 2))
|
|
|
|
const toY = createLiteral(roundOff(to[1], 2))
|
|
|
|
|
2024-09-13 21:14:14 +10:00
|
|
|
if (replaceExistingCallback && pipe.type !== 'CallExpression') {
|
2024-02-11 12:59:00 +11:00
|
|
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
2024-09-13 21:14:14 +10:00
|
|
|
const result = replaceExistingCallback([
|
|
|
|
{
|
|
|
|
type: 'arrayItem',
|
|
|
|
index: 0,
|
|
|
|
argType: 'xRelative',
|
|
|
|
expr: toX,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'arrayItem',
|
|
|
|
index: 1,
|
|
|
|
argType: 'yAbsolute',
|
|
|
|
expr: toY,
|
|
|
|
},
|
|
|
|
])
|
|
|
|
if (err(result)) return result
|
|
|
|
const { callExp, valueUsedInTransform } = result
|
2024-02-11 12:59:00 +11:00
|
|
|
pipe.body[callIndex] = callExp
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
valueUsedInTransform,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const newLine = createCallExpression('tangentialArcTo', [
|
|
|
|
createArrayExpression([toX, toY]),
|
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
|
|
|
if (pipe.type === 'PipeExpression') {
|
|
|
|
pipe.body = [...pipe.body, newLine]
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode: [
|
|
|
|
...pathToNode,
|
|
|
|
['body', 'PipeExpression'],
|
|
|
|
[pipe.body.length - 1, 'CallExpression'],
|
|
|
|
],
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
varDec.init = createPipeExpression([varDec.init, newLine])
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-09-13 21:14:14 +10:00
|
|
|
updateArgs: ({ node, pathToNode, input }) => {
|
|
|
|
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { to } = input
|
2024-02-11 12:59:00 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
const { node: callExpression } = nodeMeta
|
2024-02-11 12:59:00 +11:00
|
|
|
const x = createLiteral(roundOff(to[0], 2))
|
|
|
|
const y = createLiteral(roundOff(to[1], 2))
|
|
|
|
|
|
|
|
const firstArg = callExpression.arguments?.[0]
|
|
|
|
if (!mutateArrExp(firstArg, createArrayExpression([x, y]))) {
|
|
|
|
mutateObjExpProp(firstArg, createArrayExpression([x, y]), 'to')
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-07-15 19:20:32 +10:00
|
|
|
getTag: getTag(),
|
2024-03-17 18:24:03 +11:00
|
|
|
addTag: addTag(),
|
2024-05-24 20:54:42 +10:00
|
|
|
getConstraintInfo: (callExp: CallExpression, code, pathToNode) => {
|
|
|
|
if (callExp.type !== 'CallExpression') return []
|
|
|
|
const firstArg = callExp.arguments?.[0]
|
|
|
|
if (firstArg.type !== 'ArrayExpression') return []
|
|
|
|
const callee = callExp.callee
|
|
|
|
const pathToCallee: PathToNode = [
|
|
|
|
...pathToNode,
|
|
|
|
['callee', 'CallExpression'],
|
|
|
|
]
|
|
|
|
const pathToArrayExpression: PathToNode = [
|
|
|
|
...pathToNode,
|
|
|
|
['arguments', 'CallExpression'],
|
|
|
|
[0, 'index'],
|
|
|
|
['elements', 'ArrayExpression'],
|
|
|
|
]
|
|
|
|
const pathToFirstArg: PathToNode = [...pathToArrayExpression, [0, 'index']]
|
|
|
|
const pathToSecondArg: PathToNode = [...pathToArrayExpression, [1, 'index']]
|
|
|
|
return [
|
|
|
|
constrainInfo(
|
|
|
|
'tangentialWithPrevious',
|
|
|
|
true,
|
|
|
|
callee.name,
|
|
|
|
'tangentialArcTo',
|
|
|
|
undefined,
|
|
|
|
[callee.start, callee.end],
|
|
|
|
pathToCallee
|
|
|
|
),
|
|
|
|
constrainInfo(
|
|
|
|
'xAbsolute',
|
|
|
|
isNotLiteralArrayOrStatic(firstArg.elements[0]),
|
|
|
|
code.slice(firstArg.elements[0].start, firstArg.elements[0].end),
|
|
|
|
'tangentialArcTo',
|
|
|
|
0,
|
|
|
|
[firstArg.elements[0].start, firstArg.elements[0].end],
|
|
|
|
pathToFirstArg
|
|
|
|
),
|
|
|
|
constrainInfo(
|
|
|
|
'yAbsolute',
|
|
|
|
isNotLiteralArrayOrStatic(firstArg.elements[1]),
|
|
|
|
code.slice(firstArg.elements[1].start, firstArg.elements[1].end),
|
|
|
|
'tangentialArcTo',
|
|
|
|
1,
|
|
|
|
[firstArg.elements[1].start, firstArg.elements[1].end],
|
|
|
|
pathToSecondArg
|
|
|
|
),
|
|
|
|
]
|
|
|
|
},
|
2024-02-11 12:59:00 +11:00
|
|
|
}
|
2024-09-23 22:42:51 +10:00
|
|
|
export const circle: SketchLineHelper = {
|
|
|
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
|
|
|
if (segmentInput.type !== 'arc-segment') return ARC_SEGMENT_ERR
|
|
|
|
|
|
|
|
const { center, radius } = segmentInput
|
|
|
|
const _node = { ...node }
|
|
|
|
const nodeMeta = getNodeFromPath<PipeExpression>(
|
|
|
|
_node,
|
|
|
|
pathToNode,
|
|
|
|
'PipeExpression'
|
|
|
|
)
|
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
|
|
|
|
const { node: pipe } = nodeMeta
|
|
|
|
|
|
|
|
const x = createLiteral(roundOff(center[0], 2))
|
|
|
|
const y = createLiteral(roundOff(center[1], 2))
|
|
|
|
|
|
|
|
const radiusExp = createLiteral(roundOff(radius, 2))
|
|
|
|
|
|
|
|
if (replaceExistingCallback) {
|
|
|
|
const result = replaceExistingCallback([
|
|
|
|
{
|
|
|
|
type: 'arrayInObject',
|
|
|
|
index: 0,
|
|
|
|
key: 'center',
|
|
|
|
argType: 'xAbsolute',
|
|
|
|
expr: x,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'arrayInObject',
|
|
|
|
index: 1,
|
|
|
|
key: 'center',
|
|
|
|
argType: 'yAbsolute',
|
|
|
|
expr: y,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'objectProperty',
|
|
|
|
key: 'radius',
|
|
|
|
argType: 'radius',
|
|
|
|
expr: radiusExp,
|
|
|
|
},
|
|
|
|
])
|
|
|
|
if (err(result)) return result
|
|
|
|
const { callExp, valueUsedInTransform } = result
|
|
|
|
|
|
|
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
|
|
|
pipe.body[callIndex] = callExp
|
|
|
|
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
valueUsedInTransform,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return new Error('not implemented')
|
|
|
|
},
|
|
|
|
updateArgs: ({ node, pathToNode, input }) => {
|
|
|
|
if (input.type !== 'arc-segment') return ARC_SEGMENT_ERR
|
|
|
|
const { center, radius } = input
|
|
|
|
const _node = { ...node }
|
|
|
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
|
|
|
|
const { node: callExpression, shallowPath } = nodeMeta
|
|
|
|
|
|
|
|
const firstArg = callExpression.arguments?.[0]
|
|
|
|
const newCenter = createArrayExpression([
|
|
|
|
createLiteral(roundOff(center[0])),
|
|
|
|
createLiteral(roundOff(center[1])),
|
|
|
|
])
|
|
|
|
mutateObjExpProp(firstArg, newCenter, 'center')
|
|
|
|
const newRadius = createLiteral(roundOff(radius))
|
|
|
|
mutateObjExpProp(firstArg, newRadius, 'radius')
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode: shallowPath,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
getTag: getTag(),
|
|
|
|
addTag: addTag(),
|
|
|
|
getConstraintInfo: (callExp: CallExpression, code, pathToNode) => {
|
|
|
|
if (callExp.type !== 'CallExpression') return []
|
|
|
|
const firstArg = callExp.arguments?.[0]
|
|
|
|
if (firstArg.type !== 'ObjectExpression') return []
|
|
|
|
const centerDetails = getObjExprProperty(firstArg, 'center')
|
|
|
|
const radiusDetails = getObjExprProperty(firstArg, 'radius')
|
|
|
|
if (!centerDetails || !radiusDetails) return []
|
|
|
|
if (centerDetails.expr.type !== 'ArrayExpression') return []
|
|
|
|
|
|
|
|
const pathToCenterArrayExpression: PathToNode = [
|
|
|
|
...pathToNode,
|
|
|
|
['arguments', 'CallExpression'],
|
|
|
|
[0, 'index'],
|
|
|
|
['properties', 'ObjectExpression'],
|
|
|
|
[centerDetails.index, 'index'],
|
|
|
|
['value', 'Property'],
|
|
|
|
['elements', 'ArrayExpression'],
|
|
|
|
]
|
|
|
|
const pathToRadiusLiteral: PathToNode = [
|
|
|
|
...pathToNode,
|
|
|
|
['arguments', 'CallExpression'],
|
|
|
|
[0, 'index'],
|
|
|
|
['properties', 'ObjectExpression'],
|
|
|
|
[radiusDetails.index, 'index'],
|
|
|
|
['value', 'Property'],
|
|
|
|
]
|
|
|
|
const pathToXArg: PathToNode = [
|
|
|
|
...pathToCenterArrayExpression,
|
|
|
|
[0, 'index'],
|
|
|
|
]
|
|
|
|
const pathToYArg: PathToNode = [
|
|
|
|
...pathToCenterArrayExpression,
|
|
|
|
[1, 'index'],
|
|
|
|
]
|
|
|
|
|
|
|
|
return [
|
|
|
|
constrainInfo(
|
|
|
|
'radius',
|
|
|
|
isNotLiteralArrayOrStatic(radiusDetails.expr),
|
|
|
|
code.slice(radiusDetails.expr.start, radiusDetails.expr.end),
|
|
|
|
'circle',
|
|
|
|
'radius',
|
|
|
|
[radiusDetails.expr.start, radiusDetails.expr.end],
|
|
|
|
pathToRadiusLiteral
|
|
|
|
),
|
|
|
|
{
|
|
|
|
stdLibFnName: 'circle',
|
|
|
|
type: 'xAbsolute',
|
|
|
|
isConstrained: isNotLiteralArrayOrStatic(
|
|
|
|
centerDetails.expr.elements[0]
|
|
|
|
),
|
|
|
|
sourceRange: [
|
|
|
|
centerDetails.expr.elements[0].start,
|
|
|
|
centerDetails.expr.elements[0].end,
|
|
|
|
],
|
|
|
|
pathToNode: pathToXArg,
|
|
|
|
value: code.slice(
|
|
|
|
centerDetails.expr.elements[0].start,
|
|
|
|
centerDetails.expr.elements[0].end
|
|
|
|
),
|
|
|
|
argPosition: {
|
|
|
|
type: 'arrayInObject',
|
|
|
|
index: 0,
|
|
|
|
key: 'center',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
stdLibFnName: 'circle',
|
|
|
|
type: 'yAbsolute',
|
|
|
|
isConstrained: isNotLiteralArrayOrStatic(
|
|
|
|
centerDetails.expr.elements[1]
|
|
|
|
),
|
|
|
|
sourceRange: [
|
|
|
|
centerDetails.expr.elements[1].start,
|
|
|
|
centerDetails.expr.elements[1].end,
|
|
|
|
],
|
|
|
|
pathToNode: pathToYArg,
|
|
|
|
value: code.slice(
|
|
|
|
centerDetails.expr.elements[1].start,
|
|
|
|
centerDetails.expr.elements[1].end
|
|
|
|
),
|
|
|
|
argPosition: {
|
|
|
|
type: 'arrayInObject',
|
|
|
|
index: 1,
|
|
|
|
key: 'center',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]
|
|
|
|
},
|
|
|
|
}
|
2023-02-12 10:56:45 +11:00
|
|
|
export const angledLine: SketchLineHelper = {
|
2024-09-13 21:14:14 +10:00
|
|
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
|
|
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { from, to } = segmentInput
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2023-03-02 21:19:11 +11:00
|
|
|
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
2024-06-24 11:45:40 -04:00
|
|
|
const _node1 = getNode<PipeExpression>('PipeExpression')
|
|
|
|
if (err(_node1)) return _node1
|
|
|
|
const { node: pipe } = _node1
|
2023-03-02 21:19:11 +11:00
|
|
|
|
|
|
|
const newAngleVal = createLiteral(roundOff(getAngle(from, to), 0))
|
|
|
|
const newLengthVal = createLiteral(roundOff(getLength(from, to), 2))
|
2023-03-10 08:48:50 +11:00
|
|
|
const newLine = createCallExpression('angledLine', [
|
|
|
|
createArrayExpression([newAngleVal, newLengthVal]),
|
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
2023-03-02 21:19:11 +11:00
|
|
|
|
2024-09-13 21:14:14 +10:00
|
|
|
if (replaceExistingCallback) {
|
2023-04-01 16:47:00 +11:00
|
|
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
2024-09-13 21:14:14 +10:00
|
|
|
const result = replaceExistingCallback([
|
|
|
|
{
|
|
|
|
type: 'arrayOrObjItem',
|
|
|
|
index: 0,
|
|
|
|
key: 'angle',
|
|
|
|
argType: 'angle',
|
|
|
|
expr: newAngleVal,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'arrayOrObjItem',
|
|
|
|
index: 1,
|
|
|
|
key: 'length',
|
|
|
|
argType: 'length',
|
|
|
|
expr: newLengthVal,
|
|
|
|
},
|
|
|
|
])
|
|
|
|
if (err(result)) return result
|
|
|
|
const { callExp, valueUsedInTransform } = result
|
2023-03-10 08:48:50 +11:00
|
|
|
pipe.body[callIndex] = callExp
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
valueUsedInTransform,
|
|
|
|
}
|
2023-03-02 21:19:11 +11:00
|
|
|
} else {
|
|
|
|
pipe.body = [...pipe.body, newLine]
|
|
|
|
}
|
2023-02-12 10:56:45 +11:00
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-09-13 21:14:14 +10:00
|
|
|
updateArgs: ({ node, pathToNode, input }) => {
|
|
|
|
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { to, from } = input
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
const { node: callExpression } = nodeMeta
|
2023-02-12 10:56:45 +11:00
|
|
|
const angle = roundOff(getAngle(from, to), 0)
|
|
|
|
const lineLength = roundOff(getLength(from, to), 2)
|
|
|
|
|
|
|
|
const angleLit = createLiteral(angle)
|
|
|
|
const lengthLit = createLiteral(lineLength)
|
|
|
|
|
|
|
|
const firstArg = callExpression.arguments?.[0]
|
|
|
|
if (!mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit]))) {
|
|
|
|
mutateObjExpProp(firstArg, angleLit, 'angle')
|
|
|
|
mutateObjExpProp(firstArg, lengthLit, 'length')
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-07-15 19:20:32 +10:00
|
|
|
getTag: getTag(),
|
2024-03-17 18:24:03 +11:00
|
|
|
addTag: addTag(),
|
2024-05-24 20:54:42 +10:00
|
|
|
getConstraintInfo: (callExp, ...args) =>
|
|
|
|
commonConstraintInfoHelper(
|
|
|
|
callExp,
|
|
|
|
['angle', 'length'],
|
|
|
|
'angledLine',
|
|
|
|
[
|
|
|
|
{ arrayInput: 0, objInput: 'angle' },
|
|
|
|
{ arrayInput: 1, objInput: 'length' },
|
|
|
|
],
|
|
|
|
...args
|
|
|
|
),
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
export const angledLineOfXLength: SketchLineHelper = {
|
|
|
|
add: ({
|
|
|
|
node,
|
|
|
|
previousProgramMemory,
|
|
|
|
pathToNode,
|
2024-09-13 21:14:14 +10:00
|
|
|
segmentInput,
|
|
|
|
replaceExistingCallback,
|
2023-02-12 10:56:45 +11:00
|
|
|
}) => {
|
2024-09-13 21:14:14 +10:00
|
|
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { from, to } = segmentInput
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<PipeExpression>(
|
2023-02-12 10:56:45 +11:00
|
|
|
_node,
|
|
|
|
pathToNode,
|
|
|
|
'PipeExpression'
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
const { node: pipe } = nodeMeta
|
|
|
|
const nodeMeta2 = getNodeFromPath<VariableDeclarator>(
|
2023-02-12 10:56:45 +11:00
|
|
|
_node,
|
|
|
|
pathToNode,
|
|
|
|
'VariableDeclarator'
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(nodeMeta2)) return nodeMeta2
|
|
|
|
const { node: varDec } = nodeMeta2
|
|
|
|
|
2023-02-12 10:56:45 +11:00
|
|
|
const variableName = varDec.id.name
|
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
|
|
|
const sketch = sketchGroupFromKclValue(
|
|
|
|
previousProgramMemory?.get(variableName),
|
|
|
|
variableName
|
|
|
|
)
|
|
|
|
if (err(sketch)) {
|
|
|
|
return sketch
|
2024-06-24 11:45:40 -04:00
|
|
|
}
|
2023-03-02 21:19:11 +11:00
|
|
|
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
|
|
|
const xLength = createLiteral(roundOff(Math.abs(from[0] - to[0]), 2) || 0.1)
|
2024-09-13 21:14:14 +10:00
|
|
|
let newLine: Expr
|
|
|
|
if (replaceExistingCallback) {
|
|
|
|
const result = replaceExistingCallback([
|
|
|
|
{
|
|
|
|
type: 'arrayOrObjItem',
|
|
|
|
index: 0,
|
|
|
|
key: 'angle',
|
|
|
|
argType: 'angle',
|
|
|
|
expr: angle,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'arrayOrObjItem',
|
|
|
|
index: 1,
|
|
|
|
key: 'length',
|
|
|
|
argType: 'xRelative',
|
|
|
|
expr: xLength,
|
|
|
|
},
|
|
|
|
])
|
|
|
|
if (err(result)) return result
|
|
|
|
newLine = result.callExp
|
|
|
|
} else {
|
|
|
|
newLine = createCallExpression('angledLineOfXLength', [
|
|
|
|
createArrayExpression([angle, xLength]),
|
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
|
|
|
}
|
2023-04-01 16:47:00 +11:00
|
|
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
2024-09-13 21:14:14 +10:00
|
|
|
if (replaceExistingCallback) {
|
2023-03-02 21:19:11 +11:00
|
|
|
pipe.body[callIndex] = newLine
|
|
|
|
} else {
|
|
|
|
pipe.body = [...pipe.body, newLine]
|
|
|
|
}
|
2023-02-12 10:56:45 +11:00
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-09-13 21:14:14 +10:00
|
|
|
updateArgs: ({ node, pathToNode, input }) => {
|
|
|
|
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { to, from } = input
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
const { node: callExpression } = nodeMeta
|
2023-02-12 10:56:45 +11:00
|
|
|
const angle = roundOff(getAngle(from, to), 0)
|
|
|
|
const xLength = roundOff(Math.abs(to[0] - from[0]), 2)
|
|
|
|
|
|
|
|
const firstArg = callExpression.arguments?.[0]
|
|
|
|
const adjustedXLength = isAngleLiteral(firstArg)
|
|
|
|
? Math.abs(xLength)
|
|
|
|
: xLength // todo make work for variable angle > 180
|
|
|
|
|
|
|
|
const angleLit = createLiteral(angle)
|
|
|
|
const lengthLit = createLiteral(adjustedXLength)
|
|
|
|
|
|
|
|
if (!mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit]))) {
|
|
|
|
mutateObjExpProp(firstArg, angleLit, 'angle')
|
|
|
|
mutateObjExpProp(firstArg, lengthLit, 'length')
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-07-15 19:20:32 +10:00
|
|
|
getTag: getTag(),
|
2024-03-17 18:24:03 +11:00
|
|
|
addTag: addTag(),
|
2024-05-24 20:54:42 +10:00
|
|
|
getConstraintInfo: (callExp, ...args) =>
|
|
|
|
commonConstraintInfoHelper(
|
|
|
|
callExp,
|
|
|
|
['angle', 'xRelative'],
|
|
|
|
'angledLineOfXLength',
|
|
|
|
[
|
|
|
|
{ arrayInput: 0, objInput: 'angle' },
|
|
|
|
{ arrayInput: 1, objInput: 'length' },
|
|
|
|
],
|
|
|
|
...args
|
|
|
|
),
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
export const angledLineOfYLength: SketchLineHelper = {
|
|
|
|
add: ({
|
|
|
|
node,
|
|
|
|
previousProgramMemory,
|
|
|
|
pathToNode,
|
2024-09-13 21:14:14 +10:00
|
|
|
segmentInput,
|
|
|
|
replaceExistingCallback,
|
2023-02-12 10:56:45 +11:00
|
|
|
}) => {
|
2024-09-13 21:14:14 +10:00
|
|
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { from, to } = segmentInput
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<PipeExpression>(
|
2023-02-12 10:56:45 +11:00
|
|
|
_node,
|
|
|
|
pathToNode,
|
|
|
|
'PipeExpression'
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
const { node: pipe } = nodeMeta
|
|
|
|
const nodeMeta2 = getNodeFromPath<VariableDeclarator>(
|
2023-02-12 10:56:45 +11:00
|
|
|
_node,
|
|
|
|
pathToNode,
|
|
|
|
'VariableDeclarator'
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(nodeMeta2)) return nodeMeta2
|
|
|
|
const { node: varDec } = nodeMeta2
|
2023-02-12 10:56:45 +11:00
|
|
|
const variableName = varDec.id.name
|
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
|
|
|
const sketch = sketchGroupFromKclValue(
|
|
|
|
previousProgramMemory?.get(variableName),
|
|
|
|
variableName
|
|
|
|
)
|
|
|
|
if (err(sketch)) return sketch
|
2023-03-02 21:19:11 +11:00
|
|
|
|
|
|
|
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
|
|
|
const yLength = createLiteral(roundOff(Math.abs(from[1] - to[1]), 2) || 0.1)
|
2024-09-13 21:14:14 +10:00
|
|
|
let newLine: Expr
|
|
|
|
if (replaceExistingCallback) {
|
|
|
|
const result = replaceExistingCallback([
|
|
|
|
{
|
|
|
|
type: 'arrayOrObjItem',
|
|
|
|
index: 0,
|
|
|
|
key: 'angle',
|
|
|
|
argType: 'angle',
|
|
|
|
expr: angle,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'arrayOrObjItem',
|
|
|
|
index: 1,
|
|
|
|
key: 'length',
|
|
|
|
argType: 'yRelative',
|
|
|
|
expr: yLength,
|
|
|
|
},
|
|
|
|
])
|
|
|
|
if (err(result)) return result
|
|
|
|
newLine = result.callExp
|
|
|
|
} else {
|
|
|
|
newLine = createCallExpression('angledLineOfYLength', [
|
|
|
|
createArrayExpression([angle, yLength]),
|
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
|
|
|
}
|
2023-04-01 16:47:00 +11:00
|
|
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
2024-09-13 21:14:14 +10:00
|
|
|
if (replaceExistingCallback) {
|
2023-03-02 21:19:11 +11:00
|
|
|
pipe.body[callIndex] = newLine
|
|
|
|
} else {
|
|
|
|
pipe.body = [...pipe.body, newLine]
|
|
|
|
}
|
2023-02-12 10:56:45 +11:00
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-09-13 21:14:14 +10:00
|
|
|
updateArgs: ({ node, pathToNode, input }) => {
|
|
|
|
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { to, from } = input
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
const { node: callExpression } = nodeMeta
|
2023-02-12 10:56:45 +11:00
|
|
|
const angle = roundOff(getAngle(from, to), 0)
|
|
|
|
const yLength = roundOff(to[1] - from[1], 2)
|
|
|
|
|
|
|
|
const firstArg = callExpression.arguments?.[0]
|
|
|
|
const adjustedYLength = isAngleLiteral(firstArg)
|
|
|
|
? Math.abs(yLength)
|
|
|
|
: yLength // todo make work for variable angle > 180
|
|
|
|
|
|
|
|
const angleLit = createLiteral(angle)
|
|
|
|
const lengthLit = createLiteral(adjustedYLength)
|
|
|
|
|
|
|
|
if (!mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit]))) {
|
|
|
|
mutateObjExpProp(firstArg, angleLit, 'angle')
|
|
|
|
mutateObjExpProp(firstArg, lengthLit, 'length')
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-07-15 19:20:32 +10:00
|
|
|
getTag: getTag(),
|
2024-03-17 18:24:03 +11:00
|
|
|
addTag: addTag(),
|
2024-05-24 20:54:42 +10:00
|
|
|
getConstraintInfo: (callExp, ...args) =>
|
|
|
|
commonConstraintInfoHelper(
|
|
|
|
callExp,
|
|
|
|
['angle', 'yRelative'],
|
|
|
|
'angledLineOfYLength',
|
|
|
|
[
|
|
|
|
{ arrayInput: 0, objInput: 'angle' },
|
|
|
|
{ arrayInput: 1, objInput: 'length' },
|
|
|
|
],
|
|
|
|
...args
|
|
|
|
),
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
export const angledLineToX: SketchLineHelper = {
|
2024-09-13 21:14:14 +10:00
|
|
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
|
|
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { from, to } = segmentInput
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<PipeExpression>(
|
2023-02-12 10:56:45 +11:00
|
|
|
_node,
|
|
|
|
pathToNode,
|
|
|
|
'PipeExpression'
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
|
|
|
|
const { node: pipe } = nodeMeta
|
2023-03-02 21:19:11 +11:00
|
|
|
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
|
|
|
const xArg = createLiteral(roundOff(to[0], 2))
|
2024-09-13 21:14:14 +10:00
|
|
|
if (replaceExistingCallback) {
|
|
|
|
const result = replaceExistingCallback([
|
|
|
|
{
|
|
|
|
type: 'arrayOrObjItem',
|
|
|
|
index: 0,
|
|
|
|
key: 'angle',
|
|
|
|
argType: 'angle',
|
|
|
|
expr: angle,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'arrayOrObjItem',
|
|
|
|
index: 1,
|
|
|
|
key: 'to',
|
|
|
|
argType: 'xAbsolute',
|
|
|
|
expr: xArg,
|
|
|
|
},
|
|
|
|
])
|
|
|
|
if (err(result)) return result
|
|
|
|
const { callExp, valueUsedInTransform } = result
|
2023-04-01 16:47:00 +11:00
|
|
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
2023-03-17 08:27:40 +11:00
|
|
|
pipe.body[callIndex] = callExp
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
valueUsedInTransform,
|
|
|
|
}
|
2023-03-02 21:19:11 +11:00
|
|
|
}
|
2023-03-17 08:27:40 +11:00
|
|
|
|
|
|
|
const callExp = createCallExpression('angledLineToX', [
|
|
|
|
createArrayExpression([angle, xArg]),
|
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
|
|
|
pipe.body = [...pipe.body, callExp]
|
2023-02-12 10:56:45 +11:00
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-09-13 21:14:14 +10:00
|
|
|
updateArgs: ({ node, pathToNode, input }) => {
|
|
|
|
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { to, from } = input
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
|
|
|
|
const { node: callExpression } = nodeMeta
|
2023-02-12 10:56:45 +11:00
|
|
|
const angle = roundOff(getAngle(from, to), 0)
|
|
|
|
const xLength = roundOff(to[0], 2)
|
|
|
|
|
|
|
|
const firstArg = callExpression.arguments?.[0]
|
|
|
|
const adjustedXLength = xLength
|
|
|
|
|
|
|
|
const angleLit = createLiteral(angle)
|
|
|
|
const lengthLit = createLiteral(adjustedXLength)
|
|
|
|
|
|
|
|
if (!mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit]))) {
|
|
|
|
mutateObjExpProp(firstArg, angleLit, 'angle')
|
|
|
|
mutateObjExpProp(firstArg, lengthLit, 'to')
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-07-15 19:20:32 +10:00
|
|
|
getTag: getTag(),
|
2024-03-17 18:24:03 +11:00
|
|
|
addTag: addTag(),
|
2024-05-24 20:54:42 +10:00
|
|
|
getConstraintInfo: (callExp, ...args) =>
|
|
|
|
commonConstraintInfoHelper(
|
|
|
|
callExp,
|
|
|
|
['angle', 'xAbsolute'],
|
|
|
|
'angledLineToX',
|
|
|
|
[
|
|
|
|
{ arrayInput: 0, objInput: 'angle' },
|
|
|
|
{ arrayInput: 1, objInput: 'to' },
|
|
|
|
],
|
|
|
|
...args
|
|
|
|
),
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
export const angledLineToY: SketchLineHelper = {
|
2024-09-13 21:14:14 +10:00
|
|
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
|
|
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { from, to } = segmentInput
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<PipeExpression>(
|
2023-02-12 10:56:45 +11:00
|
|
|
_node,
|
|
|
|
pathToNode,
|
|
|
|
'PipeExpression'
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
|
|
|
|
const { node: pipe } = nodeMeta
|
|
|
|
|
2023-03-02 21:19:11 +11:00
|
|
|
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
|
|
|
const yArg = createLiteral(roundOff(to[1], 2))
|
2023-03-10 14:55:16 +11:00
|
|
|
|
2024-09-13 21:14:14 +10:00
|
|
|
if (replaceExistingCallback) {
|
|
|
|
const result = replaceExistingCallback([
|
|
|
|
{
|
|
|
|
type: 'arrayOrObjItem',
|
|
|
|
index: 0,
|
|
|
|
key: 'angle',
|
|
|
|
argType: 'angle',
|
|
|
|
expr: angle,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'arrayOrObjItem',
|
|
|
|
index: 1,
|
|
|
|
key: 'to',
|
|
|
|
argType: 'yAbsolute',
|
|
|
|
expr: yArg,
|
|
|
|
},
|
|
|
|
])
|
|
|
|
if (err(result)) return result
|
|
|
|
const { callExp, valueUsedInTransform } = result
|
2023-04-01 16:47:00 +11:00
|
|
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
2023-03-10 14:55:16 +11:00
|
|
|
pipe.body[callIndex] = callExp
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
valueUsedInTransform,
|
|
|
|
}
|
2023-03-02 21:19:11 +11:00
|
|
|
}
|
2023-03-10 14:55:16 +11:00
|
|
|
|
|
|
|
const newLine = createCallExpression('angledLineToY', [
|
|
|
|
createArrayExpression([angle, yArg]),
|
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
|
|
|
pipe.body = [...pipe.body, newLine]
|
2023-02-12 10:56:45 +11:00
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-09-13 21:14:14 +10:00
|
|
|
updateArgs: ({ node, pathToNode, input }) => {
|
|
|
|
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { to, from } = input
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
|
|
|
|
const { node: callExpression } = nodeMeta
|
2023-02-12 10:56:45 +11:00
|
|
|
const angle = roundOff(getAngle(from, to), 0)
|
|
|
|
const xLength = roundOff(to[1], 2)
|
|
|
|
|
|
|
|
const firstArg = callExpression.arguments?.[0]
|
|
|
|
const adjustedXLength = xLength
|
|
|
|
|
|
|
|
const angleLit = createLiteral(angle)
|
|
|
|
const lengthLit = createLiteral(adjustedXLength)
|
|
|
|
|
|
|
|
if (!mutateArrExp(firstArg, createArrayExpression([angleLit, lengthLit]))) {
|
|
|
|
mutateObjExpProp(firstArg, angleLit, 'angle')
|
|
|
|
mutateObjExpProp(firstArg, lengthLit, 'to')
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-07-15 19:20:32 +10:00
|
|
|
getTag: getTag(),
|
2024-03-17 18:24:03 +11:00
|
|
|
addTag: addTag(),
|
2024-05-24 20:54:42 +10:00
|
|
|
getConstraintInfo: (callExp, ...args) =>
|
|
|
|
commonConstraintInfoHelper(
|
|
|
|
callExp,
|
|
|
|
['angle', 'yAbsolute'],
|
|
|
|
'angledLineToY',
|
|
|
|
[
|
|
|
|
{ arrayInput: 0, objInput: 'angle' },
|
|
|
|
{ arrayInput: 1, objInput: 'to' },
|
|
|
|
],
|
|
|
|
...args
|
|
|
|
),
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
|
|
|
|
2023-03-19 18:46:39 +11:00
|
|
|
export const angledLineThatIntersects: SketchLineHelper = {
|
|
|
|
add: ({
|
|
|
|
node,
|
|
|
|
pathToNode,
|
2024-09-13 21:14:14 +10:00
|
|
|
segmentInput,
|
|
|
|
replaceExistingCallback,
|
2023-03-19 18:46:39 +11:00
|
|
|
referencedSegment,
|
|
|
|
}) => {
|
2024-09-13 21:14:14 +10:00
|
|
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { from, to } = segmentInput
|
2023-03-19 18:46:39 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<PipeExpression>(
|
2023-03-19 18:46:39 +11:00
|
|
|
_node,
|
|
|
|
pathToNode,
|
|
|
|
'PipeExpression'
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
|
|
|
|
const { node: pipe } = nodeMeta
|
|
|
|
|
2023-03-19 18:46:39 +11:00
|
|
|
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
2024-06-24 11:45:40 -04:00
|
|
|
if (!referencedSegment) {
|
|
|
|
return new Error('referencedSegment must be provided')
|
|
|
|
}
|
|
|
|
|
2023-03-19 18:46:39 +11:00
|
|
|
const offset = createLiteral(
|
|
|
|
roundOff(
|
|
|
|
perpendicularDistance(
|
|
|
|
referencedSegment?.from,
|
|
|
|
referencedSegment?.to,
|
|
|
|
to
|
|
|
|
),
|
|
|
|
2
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2024-09-13 21:14:14 +10:00
|
|
|
if (replaceExistingCallback) {
|
|
|
|
const result = replaceExistingCallback([
|
|
|
|
{
|
|
|
|
type: 'objectProperty',
|
|
|
|
key: 'angle',
|
|
|
|
argType: 'angle',
|
|
|
|
expr: angle,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'objectProperty',
|
|
|
|
key: 'offset',
|
|
|
|
argType: 'intersectionOffset',
|
|
|
|
expr: offset,
|
|
|
|
},
|
|
|
|
])
|
|
|
|
if (err(result)) return result
|
|
|
|
const { callExp, valueUsedInTransform } = result
|
2023-04-01 16:47:00 +11:00
|
|
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
2023-03-19 18:46:39 +11:00
|
|
|
pipe.body[callIndex] = callExp
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
valueUsedInTransform,
|
|
|
|
}
|
|
|
|
}
|
2024-06-24 11:45:40 -04:00
|
|
|
return new Error('not implemented')
|
2023-03-19 18:46:39 +11:00
|
|
|
},
|
2024-09-13 21:14:14 +10:00
|
|
|
updateArgs: ({ node, pathToNode, input, previousProgramMemory }) => {
|
|
|
|
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { to, from } = input
|
2023-03-19 18:46:39 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
|
|
|
|
const { node: callExpression } = nodeMeta
|
2023-03-19 18:46:39 +11:00
|
|
|
const angle = roundOff(getAngle(from, to), 0)
|
|
|
|
|
|
|
|
const firstArg = callExpression.arguments?.[0]
|
|
|
|
const intersectTag =
|
|
|
|
firstArg.type === 'ObjectExpression'
|
|
|
|
? firstArg.properties.find((p) => p.key.name === 'intersectTag')
|
|
|
|
?.value || createLiteral('')
|
|
|
|
: createLiteral('')
|
|
|
|
const intersectTagName =
|
2024-06-24 22:39:04 -07:00
|
|
|
intersectTag.type === 'Identifier' ? intersectTag.name : ''
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta2 = getNodeFromPath<VariableDeclaration>(
|
2023-03-19 18:46:39 +11:00
|
|
|
_node,
|
|
|
|
pathToNode,
|
|
|
|
'VariableDeclaration'
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(nodeMeta2)) return nodeMeta2
|
2023-03-19 18:46:39 +11:00
|
|
|
|
2024-06-24 11:45:40 -04:00
|
|
|
const { node: varDec } = nodeMeta2
|
2023-03-19 18:46:39 +11:00
|
|
|
const varName = varDec.declarations[0].id.name
|
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
|
|
|
const sketchGroup = sketchGroupFromKclValue(
|
|
|
|
previousProgramMemory.get(varName),
|
|
|
|
varName
|
|
|
|
)
|
|
|
|
if (err(sketchGroup)) return sketchGroup
|
2023-03-19 18:46:39 +11:00
|
|
|
const intersectPath = sketchGroup.value.find(
|
2024-06-24 14:45:07 -07:00
|
|
|
({ tag }: Path) => tag && tag.value === intersectTagName
|
2023-03-19 18:46:39 +11:00
|
|
|
)
|
|
|
|
let offset = 0
|
|
|
|
if (intersectPath) {
|
|
|
|
offset = roundOff(
|
|
|
|
perpendicularDistance(intersectPath?.from, intersectPath?.to, to),
|
|
|
|
2
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const angleLit = createLiteral(angle)
|
|
|
|
|
|
|
|
mutateObjExpProp(firstArg, angleLit, 'angle')
|
|
|
|
mutateObjExpProp(firstArg, createLiteral(offset), 'offset')
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
},
|
2024-07-15 19:20:32 +10:00
|
|
|
getTag: getTag(),
|
2024-05-24 20:54:42 +10:00
|
|
|
addTag: addTag(),
|
|
|
|
getConstraintInfo: (callExp: CallExpression, code, pathToNode) => {
|
|
|
|
if (callExp.type !== 'CallExpression') return []
|
|
|
|
const firstArg = callExp.arguments?.[0]
|
|
|
|
if (firstArg.type !== 'ObjectExpression') return []
|
|
|
|
const angleIndex = firstArg.properties.findIndex(
|
|
|
|
(p) => p.key.name === 'angle'
|
|
|
|
)
|
|
|
|
const offsetIndex = firstArg.properties.findIndex(
|
|
|
|
(p) => p.key.name === 'offset'
|
|
|
|
)
|
|
|
|
const intersectTag = firstArg.properties.findIndex(
|
|
|
|
(p) => p.key.name === 'intersectTag'
|
|
|
|
)
|
|
|
|
const returnVal = []
|
|
|
|
const pathToObjectExp: PathToNode = [
|
|
|
|
...pathToNode,
|
|
|
|
['arguments', 'CallExpression'],
|
|
|
|
[0, 'index'],
|
|
|
|
['properties', 'ObjectExpression'],
|
|
|
|
]
|
|
|
|
if (angleIndex !== -1) {
|
|
|
|
const angle = firstArg.properties[angleIndex]?.value
|
|
|
|
const pathToAngleProp: PathToNode = [
|
|
|
|
...pathToObjectExp,
|
|
|
|
[angleIndex, 'index'],
|
|
|
|
['value', 'Property'],
|
|
|
|
]
|
|
|
|
returnVal.push(
|
|
|
|
constrainInfo(
|
|
|
|
'angle',
|
|
|
|
isNotLiteralArrayOrStatic(angle),
|
|
|
|
code.slice(angle.start, angle.end),
|
|
|
|
'angledLineThatIntersects',
|
|
|
|
'angle',
|
|
|
|
[angle.start, angle.end],
|
|
|
|
pathToAngleProp
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
if (offsetIndex !== -1) {
|
|
|
|
const offset = firstArg.properties[offsetIndex]?.value
|
|
|
|
const pathToOffsetProp: PathToNode = [
|
|
|
|
...pathToObjectExp,
|
|
|
|
[offsetIndex, 'index'],
|
|
|
|
['value', 'Property'],
|
|
|
|
]
|
|
|
|
returnVal.push(
|
|
|
|
constrainInfo(
|
|
|
|
'intersectionOffset',
|
|
|
|
isNotLiteralArrayOrStatic(offset),
|
|
|
|
code.slice(offset.start, offset.end),
|
|
|
|
'angledLineThatIntersects',
|
|
|
|
'offset',
|
|
|
|
[offset.start, offset.end],
|
|
|
|
pathToOffsetProp
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
if (intersectTag !== -1) {
|
2024-06-24 22:39:04 -07:00
|
|
|
const tag = firstArg.properties[intersectTag]?.value as Identifier
|
2024-05-24 20:54:42 +10:00
|
|
|
const pathToTagProp: PathToNode = [
|
|
|
|
...pathToObjectExp,
|
|
|
|
[intersectTag, 'index'],
|
|
|
|
['value', 'Property'],
|
|
|
|
]
|
2024-06-24 22:39:04 -07:00
|
|
|
const info = constrainInfo(
|
|
|
|
'intersectionTag',
|
|
|
|
// This will always be a tag identifier.
|
|
|
|
false,
|
|
|
|
code.slice(tag.start, tag.end),
|
|
|
|
'angledLineThatIntersects',
|
|
|
|
'intersectTag',
|
|
|
|
[tag.start, tag.end],
|
|
|
|
pathToTagProp
|
2024-05-24 20:54:42 +10:00
|
|
|
)
|
2024-06-24 22:39:04 -07:00
|
|
|
returnVal.push(info)
|
2024-05-24 20:54:42 +10:00
|
|
|
}
|
|
|
|
return returnVal
|
|
|
|
},
|
2023-03-19 18:46:39 +11:00
|
|
|
}
|
|
|
|
|
2024-03-02 08:48:30 +11:00
|
|
|
export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({
|
|
|
|
node,
|
|
|
|
pathToNode,
|
2024-09-13 21:14:14 +10:00
|
|
|
input,
|
2024-03-02 08:48:30 +11:00
|
|
|
}) => {
|
2024-09-13 21:14:14 +10:00
|
|
|
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
|
|
|
const { to } = input
|
2024-03-02 08:48:30 +11:00
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
|
|
|
if (err(nodeMeta)) {
|
|
|
|
console.error(nodeMeta)
|
|
|
|
return {
|
|
|
|
modifiedAst: {
|
|
|
|
start: 0,
|
|
|
|
end: 0,
|
|
|
|
body: [],
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2024-06-24 11:45:40 -04:00
|
|
|
nonCodeMeta: {
|
|
|
|
start: [],
|
|
|
|
nonCodeNodes: [],
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2024-06-24 11:45:40 -04:00
|
|
|
},
|
|
|
|
},
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
}
|
2024-03-02 08:48:30 +11:00
|
|
|
|
2024-06-24 11:45:40 -04:00
|
|
|
const { node: callExpression } = nodeMeta
|
2024-03-02 08:48:30 +11:00
|
|
|
const toArrExp = createArrayExpression([
|
|
|
|
createLiteral(roundOff(to[0])),
|
|
|
|
createLiteral(roundOff(to[1])),
|
|
|
|
])
|
|
|
|
|
|
|
|
mutateArrExp(callExpression.arguments?.[0], toArrExp) ||
|
|
|
|
mutateObjExpProp(callExpression.arguments?.[0], toArrExp, 'to')
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
pathToNode,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-12 10:56:45 +11:00
|
|
|
export const sketchLineHelperMap: { [key: string]: SketchLineHelper } = {
|
|
|
|
line,
|
|
|
|
lineTo,
|
|
|
|
xLine,
|
|
|
|
yLine,
|
|
|
|
xLineTo,
|
|
|
|
yLineTo,
|
|
|
|
angledLine,
|
|
|
|
angledLineOfXLength,
|
|
|
|
angledLineOfYLength,
|
|
|
|
angledLineToX,
|
|
|
|
angledLineToY,
|
2023-03-19 18:46:39 +11:00
|
|
|
angledLineThatIntersects,
|
2024-02-11 12:59:00 +11:00
|
|
|
tangentialArcTo,
|
2024-09-23 22:42:51 +10:00
|
|
|
circle,
|
2023-02-12 10:56:45 +11:00
|
|
|
} as const
|
|
|
|
|
|
|
|
export function changeSketchArguments(
|
|
|
|
node: Program,
|
|
|
|
programMemory: ProgramMemory,
|
2024-09-23 22:42:51 +10:00
|
|
|
sourceRangeOrPath:
|
|
|
|
| {
|
|
|
|
type: 'sourceRange'
|
|
|
|
sourceRange: SourceRange
|
|
|
|
}
|
|
|
|
| {
|
|
|
|
type: 'path'
|
|
|
|
pathToNode: PathToNode
|
|
|
|
},
|
2024-09-13 21:14:14 +10:00
|
|
|
input: SegmentInputs
|
2024-06-24 11:45:40 -04:00
|
|
|
): { modifiedAst: Program; pathToNode: PathToNode } | Error {
|
2023-02-12 10:56:45 +11:00
|
|
|
const _node = { ...node }
|
2024-09-23 22:42:51 +10:00
|
|
|
const thePath =
|
|
|
|
sourceRangeOrPath.type === 'sourceRange'
|
|
|
|
? getNodePathFromSourceRange(_node, sourceRangeOrPath.sourceRange)
|
|
|
|
: sourceRangeOrPath.pathToNode
|
2024-06-24 11:45:40 -04:00
|
|
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, thePath)
|
|
|
|
if (err(nodeMeta)) return nodeMeta
|
|
|
|
|
|
|
|
const { node: callExpression, shallowPath } = nodeMeta
|
2023-02-12 10:56:45 +11:00
|
|
|
|
|
|
|
if (callExpression?.callee?.name in sketchLineHelperMap) {
|
|
|
|
const { updateArgs } = sketchLineHelperMap[callExpression.callee.name]
|
2024-06-24 11:45:40 -04:00
|
|
|
if (!updateArgs) {
|
|
|
|
return new Error('not a sketch line helper')
|
|
|
|
}
|
|
|
|
|
2023-02-12 10:56:45 +11:00
|
|
|
return updateArgs({
|
|
|
|
node: _node,
|
|
|
|
previousProgramMemory: programMemory,
|
2023-04-01 16:47:00 +11:00
|
|
|
pathToNode: shallowPath,
|
2024-09-13 21:14:14 +10:00
|
|
|
input,
|
2023-02-12 10:56:45 +11:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-06-24 11:45:40 -04:00
|
|
|
return new Error(`not a sketch line helper: ${callExpression?.callee?.name}`)
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
|
|
|
|
2024-05-24 20:54:42 +10:00
|
|
|
export function getConstraintInfo(
|
|
|
|
callExpression: CallExpression,
|
|
|
|
code: string,
|
|
|
|
pathToNode: PathToNode
|
|
|
|
): ConstrainInfo[] {
|
|
|
|
const fnName = callExpression?.callee?.name || ''
|
|
|
|
if (!(fnName in sketchLineHelperMap)) return []
|
|
|
|
return sketchLineHelperMap[fnName].getConstraintInfo(
|
|
|
|
callExpression,
|
|
|
|
code,
|
|
|
|
pathToNode
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-09-14 09:34:37 -04:00
|
|
|
export function compareVec2Epsilon(
|
|
|
|
vec1: [number, number],
|
2024-02-11 12:59:00 +11:00
|
|
|
vec2: [number, number],
|
|
|
|
compareEpsilon = 0.015625 // or 2^-6
|
2023-09-14 09:34:37 -04:00
|
|
|
) {
|
|
|
|
const xDifference = Math.abs(vec1[0] - vec2[0])
|
2023-11-08 20:27:53 +11:00
|
|
|
const yDifference = Math.abs(vec1[1] - vec2[1])
|
2023-09-14 09:34:37 -04:00
|
|
|
return xDifference < compareEpsilon && yDifference < compareEpsilon
|
|
|
|
}
|
|
|
|
|
2024-02-11 12:59:00 +11:00
|
|
|
// this version uses this distance of the two points instead of comparing x and y separately
|
|
|
|
export function compareVec2Epsilon2(
|
|
|
|
vec1: [number, number],
|
|
|
|
vec2: [number, number],
|
|
|
|
compareEpsilon = 0.015625 // or 2^-6
|
|
|
|
) {
|
|
|
|
const xDifference = Math.abs(vec1[0] - vec2[0])
|
|
|
|
const yDifference = Math.abs(vec1[1] - vec2[1])
|
|
|
|
const distance = Math.sqrt(
|
|
|
|
xDifference * xDifference + yDifference * yDifference
|
|
|
|
)
|
|
|
|
return distance < compareEpsilon
|
|
|
|
}
|
|
|
|
|
2024-04-03 13:22:56 +11:00
|
|
|
interface CreateLineFnCallArgs {
|
|
|
|
node: Program
|
|
|
|
programMemory: ProgramMemory
|
2024-09-13 21:14:14 +10:00
|
|
|
input: SegmentInputs
|
2024-04-03 13:22:56 +11:00
|
|
|
fnName: ToolTip
|
|
|
|
pathToNode: PathToNode
|
|
|
|
spliceBetween?: boolean
|
|
|
|
}
|
|
|
|
|
2023-02-21 10:50:45 +11:00
|
|
|
export function addNewSketchLn({
|
2023-03-17 15:53:20 +11:00
|
|
|
node: _node,
|
2023-02-21 10:50:45 +11:00
|
|
|
programMemory: previousProgramMemory,
|
|
|
|
fnName,
|
|
|
|
pathToNode,
|
2024-09-13 21:14:14 +10:00
|
|
|
input: segmentInput,
|
2024-04-03 13:22:56 +11:00
|
|
|
spliceBetween = false,
|
2024-06-24 11:45:40 -04:00
|
|
|
}: CreateLineFnCallArgs):
|
|
|
|
| {
|
|
|
|
modifiedAst: Program
|
|
|
|
pathToNode: PathToNode
|
|
|
|
}
|
|
|
|
| Error {
|
2024-07-25 20:11:46 -04:00
|
|
|
const node = structuredClone(_node)
|
2023-03-21 19:02:18 +11:00
|
|
|
const { add, updateArgs } = sketchLineHelperMap?.[fnName] || {}
|
2024-06-24 11:45:40 -04:00
|
|
|
if (!add || !updateArgs) {
|
|
|
|
return new Error('not a sketch line helper')
|
|
|
|
}
|
|
|
|
|
2023-11-01 07:39:31 -04:00
|
|
|
getNodeFromPath<VariableDeclarator>(node, pathToNode, 'VariableDeclarator')
|
|
|
|
getNodeFromPath<PipeExpression | CallExpression>(
|
2023-02-21 10:50:45 +11:00
|
|
|
node,
|
|
|
|
pathToNode,
|
2023-11-01 07:39:31 -04:00
|
|
|
'PipeExpression'
|
2023-02-21 10:50:45 +11:00
|
|
|
)
|
|
|
|
return add({
|
|
|
|
node,
|
|
|
|
previousProgramMemory,
|
|
|
|
pathToNode,
|
2024-09-13 21:14:14 +10:00
|
|
|
segmentInput,
|
2024-04-03 13:22:56 +11:00
|
|
|
spliceBetween,
|
2023-02-21 10:50:45 +11:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-05-23 00:53:15 -04:00
|
|
|
export function addCallExpressionsToPipe({
|
|
|
|
node,
|
|
|
|
pathToNode,
|
|
|
|
expressions,
|
|
|
|
}: {
|
|
|
|
node: Program
|
|
|
|
programMemory: ProgramMemory
|
|
|
|
pathToNode: PathToNode
|
|
|
|
expressions: CallExpression[]
|
|
|
|
}) {
|
|
|
|
const _node = { ...node }
|
|
|
|
const pipeExpression = getNodeFromPath<PipeExpression>(
|
|
|
|
_node,
|
|
|
|
pathToNode,
|
|
|
|
'PipeExpression'
|
2024-06-24 11:45:40 -04:00
|
|
|
)
|
|
|
|
if (err(pipeExpression)) return pipeExpression
|
|
|
|
|
|
|
|
if (pipeExpression.node.type !== 'PipeExpression') {
|
|
|
|
return new Error('not a pipe expression')
|
|
|
|
}
|
|
|
|
pipeExpression.node.body = [...pipeExpression.node.body, ...expressions]
|
2024-05-23 00:53:15 -04:00
|
|
|
return _node
|
|
|
|
}
|
|
|
|
|
2023-09-14 09:34:37 -04:00
|
|
|
export function addCloseToPipe({
|
|
|
|
node,
|
|
|
|
pathToNode,
|
|
|
|
}: {
|
|
|
|
node: Program
|
|
|
|
programMemory: ProgramMemory
|
|
|
|
pathToNode: PathToNode
|
|
|
|
}) {
|
|
|
|
const _node = { ...node }
|
|
|
|
const closeExpression = createCallExpression('close', [
|
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
|
|
|
const pipeExpression = getNodeFromPath<PipeExpression>(
|
|
|
|
_node,
|
|
|
|
pathToNode,
|
|
|
|
'PipeExpression'
|
2024-06-24 11:45:40 -04:00
|
|
|
)
|
|
|
|
if (err(pipeExpression)) return pipeExpression
|
|
|
|
|
|
|
|
if (pipeExpression.node.type !== 'PipeExpression') {
|
|
|
|
return new Error('not a pipe expression')
|
|
|
|
}
|
|
|
|
pipeExpression.node.body = [...pipeExpression.node.body, closeExpression]
|
2023-09-14 09:34:37 -04:00
|
|
|
return _node
|
|
|
|
}
|
|
|
|
|
2023-02-21 10:50:45 +11:00
|
|
|
export function replaceSketchLine({
|
|
|
|
node,
|
|
|
|
programMemory,
|
2024-05-24 20:54:42 +10:00
|
|
|
pathToNode: _pathToNode,
|
2023-02-21 10:50:45 +11:00
|
|
|
fnName,
|
2024-09-13 21:14:14 +10:00
|
|
|
segmentInput,
|
|
|
|
replaceExistingCallback,
|
2023-03-05 07:34:56 +11:00
|
|
|
referencedSegment,
|
2023-02-21 10:50:45 +11:00
|
|
|
}: {
|
|
|
|
node: Program
|
|
|
|
programMemory: ProgramMemory
|
2024-05-24 20:54:42 +10:00
|
|
|
pathToNode: PathToNode
|
2023-09-15 11:48:23 -04:00
|
|
|
fnName: ToolTip
|
2024-09-13 21:14:14 +10:00
|
|
|
segmentInput: SegmentInputs
|
|
|
|
replaceExistingCallback: (rawArgs: RawArgs) => CreatedSketchExprResult | Error
|
2023-03-05 07:34:56 +11:00
|
|
|
referencedSegment?: Path
|
2024-06-24 11:45:40 -04:00
|
|
|
}):
|
|
|
|
| {
|
|
|
|
modifiedAst: Program
|
|
|
|
valueUsedInTransform?: number
|
|
|
|
pathToNode: PathToNode
|
|
|
|
}
|
|
|
|
| Error {
|
2024-09-23 22:42:51 +10:00
|
|
|
if (![...toolTips, 'intersect', 'circle'].includes(fnName)) {
|
2024-09-13 21:14:14 +10:00
|
|
|
return new Error(`The following function name is not tooltip: ${fnName}`)
|
2024-06-24 11:45:40 -04:00
|
|
|
}
|
2023-02-21 10:50:45 +11:00
|
|
|
const _node = { ...node }
|
|
|
|
|
|
|
|
const { add } = sketchLineHelperMap[fnName]
|
2024-06-24 11:45:40 -04:00
|
|
|
const addRetVal = add({
|
2023-02-21 10:50:45 +11:00
|
|
|
node: _node,
|
|
|
|
previousProgramMemory: programMemory,
|
2024-05-24 20:54:42 +10:00
|
|
|
pathToNode: _pathToNode,
|
2023-03-05 07:34:56 +11:00
|
|
|
referencedSegment,
|
2024-09-13 21:14:14 +10:00
|
|
|
segmentInput,
|
|
|
|
replaceExistingCallback,
|
2023-02-21 10:50:45 +11:00
|
|
|
})
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(addRetVal)) return addRetVal
|
|
|
|
|
|
|
|
const { modifiedAst, valueUsedInTransform, pathToNode } = addRetVal
|
2023-04-14 07:49:36 +10:00
|
|
|
return { modifiedAst, valueUsedInTransform, pathToNode }
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
|
|
|
|
2024-09-26 18:25:05 +10:00
|
|
|
/** Ostensibly should be used to add a chamfer tag to a chamfer call expression
|
|
|
|
*
|
|
|
|
* However things get complicated in situations like:
|
|
|
|
* ```ts
|
|
|
|
* |> chamfer({
|
|
|
|
* length: 1,
|
|
|
|
* tags: [tag1, tagOfInterest]
|
|
|
|
* }, %)
|
|
|
|
* ```
|
|
|
|
* Because tag declarator is not allowed on a chamfer with more than one tag,
|
|
|
|
* They must be pulled apart into separate chamfer calls:
|
|
|
|
* ```ts
|
|
|
|
* |> chamfer({
|
|
|
|
* length: 1,
|
|
|
|
* tags: [tag1]
|
|
|
|
* }, %)
|
|
|
|
* |> chamfer({
|
|
|
|
* length: 1,
|
|
|
|
* tags: [tagOfInterest]
|
|
|
|
* }, %, $newTagDeclarator)
|
|
|
|
* ```
|
|
|
|
*/
|
|
|
|
function addTagToChamfer(
|
|
|
|
tagInfo: AddTagInfo,
|
|
|
|
edgeCutMeta: EdgeCutInfo | null
|
|
|
|
):
|
|
|
|
| {
|
|
|
|
modifiedAst: Program
|
|
|
|
tag: string
|
|
|
|
}
|
|
|
|
| Error {
|
|
|
|
const _node = structuredClone(tagInfo.node)
|
|
|
|
let pipeIndex = 0
|
|
|
|
for (let i = 0; i < tagInfo.pathToNode.length; i++) {
|
|
|
|
if (tagInfo.pathToNode[i][1] === 'PipeExpression') {
|
|
|
|
pipeIndex = Number(tagInfo.pathToNode[i + 1][0])
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const pipeExpr = getNodeFromPath<PipeExpression>(
|
|
|
|
_node,
|
|
|
|
tagInfo.pathToNode,
|
|
|
|
'PipeExpression'
|
|
|
|
)
|
|
|
|
const variableDec = getNodeFromPath<VariableDeclarator>(
|
|
|
|
_node,
|
|
|
|
tagInfo.pathToNode,
|
|
|
|
'VariableDeclarator'
|
|
|
|
)
|
|
|
|
if (err(pipeExpr)) return pipeExpr
|
|
|
|
if (err(variableDec)) return variableDec
|
|
|
|
const isPipeExpression = pipeExpr.node.type === 'PipeExpression'
|
|
|
|
|
|
|
|
console.log('pipeExpr', pipeExpr, variableDec)
|
|
|
|
// const callExpr = isPipeExpression ? pipeExpr.node.body[pipeIndex] : variableDec.node.init
|
|
|
|
const callExpr = isPipeExpression
|
|
|
|
? pipeExpr.node.body[pipeIndex]
|
|
|
|
: variableDec.node.init
|
|
|
|
if (callExpr.type !== 'CallExpression')
|
|
|
|
return new Error('no chamfer call Expr')
|
|
|
|
const chamferObjArg = callExpr.arguments[0]
|
|
|
|
if (chamferObjArg.type !== 'ObjectExpression')
|
|
|
|
return new Error('first argument should be an object expression')
|
|
|
|
const inputTags = getObjExprProperty(chamferObjArg, 'tags')
|
|
|
|
if (!inputTags) return new Error('no tags property')
|
|
|
|
if (inputTags.expr.type !== 'ArrayExpression')
|
|
|
|
return new Error('tags should be an array expression')
|
|
|
|
|
|
|
|
const isChamferBreakUpNeeded = inputTags.expr.elements.length > 1
|
|
|
|
if (!isChamferBreakUpNeeded) {
|
|
|
|
return addTag(2)(tagInfo)
|
|
|
|
}
|
|
|
|
|
|
|
|
// There's more than one input tag, we need to break that chamfer call into a separate chamfer call
|
|
|
|
// so that it can have a tag declarator added.
|
|
|
|
const tagIndexToPullOut = inputTags.expr.elements.findIndex((tag) => {
|
|
|
|
// e.g. chamfer({ tags: [tagOfInterest, tag2] }, %)
|
|
|
|
// ^^^^^^^^^^^^^
|
|
|
|
const elementMatchesBaseTagType =
|
|
|
|
edgeCutMeta?.subType === 'base' &&
|
|
|
|
tag.type === 'Identifier' &&
|
|
|
|
tag.name === edgeCutMeta.tagName
|
|
|
|
if (elementMatchesBaseTagType) return true
|
|
|
|
|
|
|
|
// e.g. chamfer({ tags: [getOppositeEdge(tagOfInterest), tag2] }, %)
|
|
|
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
const tagMatchesOppositeTagType =
|
|
|
|
edgeCutMeta?.subType === 'opposite' &&
|
|
|
|
tag.type === 'CallExpression' &&
|
|
|
|
tag.callee.name === 'getOppositeEdge' &&
|
|
|
|
tag.arguments[0].type === 'Identifier' &&
|
|
|
|
tag.arguments[0].name === edgeCutMeta.tagName
|
|
|
|
if (tagMatchesOppositeTagType) return true
|
|
|
|
|
|
|
|
// e.g. chamfer({ tags: [getNextAdjacentEdge(tagOfInterest), tag2] }, %)
|
|
|
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
const tagMatchesAdjacentTagType =
|
|
|
|
edgeCutMeta?.subType === 'adjacent' &&
|
|
|
|
tag.type === 'CallExpression' &&
|
|
|
|
(tag.callee.name === 'getNextAdjacentEdge' ||
|
|
|
|
tag.callee.name === 'getPrevAdjacentEdge') &&
|
|
|
|
tag.arguments[0].type === 'Identifier' &&
|
|
|
|
tag.arguments[0].name === edgeCutMeta.tagName
|
|
|
|
if (tagMatchesAdjacentTagType) return true
|
|
|
|
return false
|
|
|
|
})
|
|
|
|
if (tagIndexToPullOut === -1) return new Error('tag not found')
|
|
|
|
// get the tag we're pulling out
|
|
|
|
const tagToPullOut = inputTags.expr.elements[tagIndexToPullOut]
|
|
|
|
// and remove it from the original chamfer call
|
|
|
|
// [pullOutTag, tag2] to [tag2]
|
|
|
|
inputTags.expr.elements.splice(tagIndexToPullOut, 1)
|
|
|
|
|
|
|
|
// get the length of the chamfer we're breaking up, as the new chamfer will have the same length
|
|
|
|
const chamferLength = getObjExprProperty(chamferObjArg, 'length')
|
|
|
|
if (!chamferLength) return new Error('no chamfer length')
|
|
|
|
const tagDec = createTagDeclarator(findUniqueName(_node, 'seg', 2))
|
|
|
|
const solid3dIdentifierUsedInOriginalChamfer = callExpr.arguments[1]
|
|
|
|
const newExpressionToInsert = createCallExpression('chamfer', [
|
|
|
|
createObjectExpression({
|
|
|
|
length: chamferLength.expr,
|
|
|
|
// single tag to add to the new chamfer call
|
|
|
|
tags: createArrayExpression([tagToPullOut]),
|
|
|
|
}),
|
|
|
|
isPipeExpression
|
|
|
|
? createPipeSubstitution()
|
|
|
|
: solid3dIdentifierUsedInOriginalChamfer,
|
|
|
|
tagDec,
|
|
|
|
])
|
|
|
|
|
|
|
|
// insert the new chamfer call with the tag declarator, add its above the original
|
|
|
|
// alternatively we could use `pipeIndex + 1` to insert it below the original
|
|
|
|
if (isPipeExpression) {
|
|
|
|
pipeExpr.node.body.splice(pipeIndex, 0, newExpressionToInsert)
|
|
|
|
} else {
|
|
|
|
console.log('yo', createPipeExpression([newExpressionToInsert, callExpr]))
|
|
|
|
callExpr.arguments[1] = createPipeSubstitution()
|
|
|
|
variableDec.node.init = createPipeExpression([
|
|
|
|
newExpressionToInsert,
|
|
|
|
callExpr,
|
|
|
|
])
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
|
|
|
tag: tagDec.value,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-13 21:14:14 +10:00
|
|
|
export function addTagForSketchOnFace(
|
|
|
|
tagInfo: AddTagInfo,
|
2024-09-26 18:25:05 +10:00
|
|
|
expressionName: string,
|
|
|
|
edgeCutMeta: EdgeCutInfo | null
|
|
|
|
):
|
|
|
|
| {
|
|
|
|
modifiedAst: Program
|
|
|
|
tag: string
|
|
|
|
}
|
|
|
|
| Error {
|
2024-03-22 10:23:04 +11:00
|
|
|
if (expressionName === 'close') {
|
2024-09-13 21:14:14 +10:00
|
|
|
return addTag(1)(tagInfo)
|
2024-03-22 10:23:04 +11:00
|
|
|
}
|
2024-09-26 18:25:05 +10:00
|
|
|
if (expressionName === 'chamfer') {
|
|
|
|
return addTagToChamfer(tagInfo, edgeCutMeta)
|
|
|
|
}
|
2023-02-12 10:56:45 +11:00
|
|
|
if (expressionName in sketchLineHelperMap) {
|
|
|
|
const { addTag } = sketchLineHelperMap[expressionName]
|
2024-09-13 21:14:14 +10:00
|
|
|
return addTag(tagInfo)
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
2024-06-24 11:45:40 -04:00
|
|
|
return new Error(`"${expressionName}" is not a sketch line helper`)
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
|
|
|
|
2024-07-15 19:20:32 +10:00
|
|
|
export function getTagFromCallExpression(
|
|
|
|
callExp: CallExpression
|
|
|
|
): string | Error {
|
|
|
|
if (callExp.callee.name === 'close') return getTag(1)(callExp)
|
|
|
|
if (callExp.callee.name in sketchLineHelperMap) {
|
|
|
|
const { getTag } = sketchLineHelperMap[callExp.callee.name]
|
|
|
|
return getTag(callExp)
|
|
|
|
}
|
|
|
|
return new Error(`"${callExp.callee.name}" is not a sketch line helper`)
|
|
|
|
}
|
|
|
|
|
2024-08-12 15:38:42 -05:00
|
|
|
function isAngleLiteral(lineArugement: Expr): boolean {
|
2023-02-12 10:56:45 +11:00
|
|
|
return lineArugement?.type === 'ArrayExpression'
|
2023-09-13 07:23:14 -07:00
|
|
|
? isLiteralArrayOrStatic(lineArugement.elements[0])
|
2023-02-12 10:56:45 +11:00
|
|
|
: lineArugement?.type === 'ObjectExpression'
|
2023-09-13 07:23:14 -07:00
|
|
|
? isLiteralArrayOrStatic(
|
|
|
|
lineArugement.properties.find(({ key }) => key.name === 'angle')?.value
|
|
|
|
)
|
2023-02-12 10:56:45 +11:00
|
|
|
: false
|
|
|
|
}
|
|
|
|
|
2024-07-15 19:20:32 +10:00
|
|
|
type addTagFn = (a: AddTagInfo) => { modifiedAst: Program; tag: string } | Error
|
2023-02-12 10:56:45 +11:00
|
|
|
|
2024-03-22 10:23:04 +11:00
|
|
|
function addTag(tagIndex = 2): addTagFn {
|
2023-02-12 10:56:45 +11:00
|
|
|
return ({ node, pathToNode }) => {
|
|
|
|
const _node = { ...node }
|
2024-06-24 11:45:40 -04:00
|
|
|
const callExpr = getNodeFromPath<CallExpression>(
|
2023-02-12 10:56:45 +11:00
|
|
|
_node,
|
2024-03-17 18:24:03 +11:00
|
|
|
pathToNode,
|
|
|
|
'CallExpression'
|
2023-02-12 10:56:45 +11:00
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(callExpr)) return callExpr
|
|
|
|
|
|
|
|
const { node: primaryCallExp } = callExpr
|
|
|
|
|
2024-03-17 18:24:03 +11: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.
|
2024-03-22 10:23:04 +11:00
|
|
|
const thirdArg = primaryCallExp.arguments?.[tagIndex]
|
2024-06-24 22:39:04 -07:00
|
|
|
const tagDeclarator =
|
|
|
|
thirdArg ||
|
|
|
|
(createTagDeclarator(findUniqueName(_node, 'seg', 2)) as TagDeclarator)
|
2024-03-17 18:24:03 +11:00
|
|
|
const isTagExisting = !!thirdArg
|
|
|
|
if (!isTagExisting) {
|
2024-06-24 22:39:04 -07:00
|
|
|
primaryCallExp.arguments[tagIndex] = tagDeclarator
|
2024-03-17 18:24:03 +11:00
|
|
|
}
|
2024-06-24 22:39:04 -07:00
|
|
|
if ('value' in tagDeclarator) {
|
|
|
|
// Now TypeScript knows tagDeclarator has a value property
|
2023-02-12 10:56:45 +11:00
|
|
|
return {
|
|
|
|
modifiedAst: _node,
|
2024-06-24 22:39:04 -07:00
|
|
|
tag: String(tagDeclarator.value),
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
2024-03-15 17:03:42 -04:00
|
|
|
} else {
|
2024-06-24 11:45:40 -04:00
|
|
|
return new Error('Unable to assign tag without value')
|
2023-02-12 10:56:45 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getYComponent(
|
|
|
|
angleDegree: number,
|
|
|
|
xComponent: number
|
|
|
|
): [number, number] {
|
|
|
|
const normalisedAngle = ((angleDegree % 360) + 360) % 360 // between 0 and 360
|
|
|
|
let yComponent = xComponent * Math.tan((normalisedAngle * Math.PI) / 180)
|
|
|
|
const sign = normalisedAngle > 90 && normalisedAngle <= 270 ? -1 : 1
|
|
|
|
return [sign * xComponent, sign * yComponent]
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getXComponent(
|
|
|
|
angleDegree: number,
|
|
|
|
yComponent: number
|
|
|
|
): [number, number] {
|
|
|
|
const normalisedAngle = ((angleDegree % 360) + 360) % 360 // between 0 and 360
|
|
|
|
let xComponent = yComponent / Math.tan((normalisedAngle * Math.PI) / 180)
|
|
|
|
const sign = normalisedAngle > 180 && normalisedAngle <= 360 ? -1 : 1
|
|
|
|
return [sign * xComponent, sign * yComponent]
|
|
|
|
}
|
2023-02-21 10:50:45 +11:00
|
|
|
|
2024-06-24 11:45:40 -04:00
|
|
|
function getFirstArgValuesForXYFns(callExpression: CallExpression):
|
|
|
|
| {
|
2024-08-12 15:38:42 -05:00
|
|
|
val: [Expr, Expr]
|
|
|
|
tag?: Expr
|
2024-06-24 11:45:40 -04:00
|
|
|
}
|
|
|
|
| Error {
|
2023-02-21 10:50:45 +11:00
|
|
|
// used for lineTo, line
|
|
|
|
const firstArg = callExpression.arguments[0]
|
|
|
|
if (firstArg.type === 'ArrayExpression') {
|
|
|
|
return { val: [firstArg.elements[0], firstArg.elements[1]] }
|
|
|
|
}
|
|
|
|
if (firstArg.type === 'ObjectExpression') {
|
|
|
|
const to = firstArg.properties.find((p) => p.key.name === 'to')?.value
|
|
|
|
const tag = firstArg.properties.find((p) => p.key.name === 'tag')?.value
|
|
|
|
if (to?.type === 'ArrayExpression') {
|
|
|
|
const [x, y] = to.elements
|
|
|
|
return { val: [x, y], tag }
|
|
|
|
}
|
|
|
|
}
|
2024-06-24 11:45:40 -04:00
|
|
|
return new Error('expected ArrayExpression or ObjectExpression')
|
2023-02-21 10:50:45 +11:00
|
|
|
}
|
|
|
|
|
2024-06-24 11:45:40 -04:00
|
|
|
function getFirstArgValuesForAngleFns(callExpression: CallExpression):
|
|
|
|
| {
|
2024-08-12 15:38:42 -05:00
|
|
|
val: [Expr, Expr]
|
|
|
|
tag?: Expr
|
2024-06-24 11:45:40 -04:00
|
|
|
}
|
|
|
|
| Error {
|
2023-02-21 10:50:45 +11:00
|
|
|
// used for angledLine, angledLineOfXLength, angledLineToX, angledLineOfYLength, angledLineToY
|
|
|
|
const firstArg = callExpression.arguments[0]
|
|
|
|
if (firstArg.type === 'ArrayExpression') {
|
|
|
|
return { val: [firstArg.elements[0], firstArg.elements[1]] }
|
|
|
|
}
|
|
|
|
if (firstArg.type === 'ObjectExpression') {
|
|
|
|
const tag = firstArg.properties.find((p) => p.key.name === 'tag')?.value
|
|
|
|
const angle = firstArg.properties.find((p) => p.key.name === 'angle')?.value
|
|
|
|
const secondArgName = ['angledLineToX', 'angledLineToY'].includes(
|
2023-09-15 11:48:23 -04:00
|
|
|
callExpression?.callee?.name as ToolTip
|
2023-02-21 10:50:45 +11:00
|
|
|
)
|
|
|
|
? 'to'
|
|
|
|
: 'length'
|
|
|
|
const length = firstArg.properties.find(
|
|
|
|
(p) => p.key.name === secondArgName
|
|
|
|
)?.value
|
|
|
|
if (angle && length) {
|
|
|
|
return { val: [angle, length], tag }
|
|
|
|
}
|
|
|
|
}
|
2024-06-24 11:45:40 -04:00
|
|
|
return new Error('expected ArrayExpression or ObjectExpression')
|
2023-02-21 10:50:45 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
function getFirstArgValuesForXYLineFns(callExpression: CallExpression): {
|
2024-08-12 15:38:42 -05:00
|
|
|
val: Expr
|
|
|
|
tag?: Expr
|
2023-02-21 10:50:45 +11:00
|
|
|
} {
|
|
|
|
// used for xLine, yLine, xLineTo, yLineTo
|
|
|
|
const firstArg = callExpression.arguments[0]
|
|
|
|
if (firstArg.type !== 'ObjectExpression') {
|
|
|
|
return { val: firstArg }
|
|
|
|
}
|
|
|
|
const tag = firstArg.properties.find((p) => p.key.name === 'tag')?.value
|
2023-03-17 08:27:40 +11:00
|
|
|
const secondArgName = ['xLineTo', 'yLineTo', 'startSketchAt'].includes(
|
2023-02-21 10:50:45 +11:00
|
|
|
// const secondArgName = ['xLineTo', 'yLineTo', 'angledLineToX', 'angledLineToY'].includes(
|
|
|
|
callExpression?.callee?.name
|
|
|
|
)
|
|
|
|
? 'to'
|
|
|
|
: 'length'
|
|
|
|
const length = firstArg.properties.find(
|
|
|
|
(p) => p.key.name === secondArgName
|
|
|
|
)?.value
|
|
|
|
if (length) {
|
|
|
|
return { val: length, tag }
|
|
|
|
}
|
2023-03-22 03:02:37 +11:00
|
|
|
console.warn('expected ArrayExpression or ObjectExpression')
|
|
|
|
return {
|
|
|
|
val: createLiteral(1),
|
|
|
|
}
|
2023-02-21 10:50:45 +11:00
|
|
|
}
|
|
|
|
|
2024-09-23 22:42:51 +10:00
|
|
|
const getCircle = (
|
|
|
|
callExp: CallExpression
|
|
|
|
):
|
|
|
|
| {
|
|
|
|
val: [Expr, Expr, Expr]
|
|
|
|
tag?: Expr
|
|
|
|
}
|
|
|
|
| Error => {
|
|
|
|
const firstArg = callExp.arguments[0]
|
|
|
|
if (firstArg.type === 'ObjectExpression') {
|
|
|
|
const centerDetails = getObjExprProperty(firstArg, 'center')
|
|
|
|
const radiusDetails = getObjExprProperty(firstArg, 'radius')
|
|
|
|
const tag = callExp.arguments[2]
|
|
|
|
if (centerDetails?.expr?.type === 'ArrayExpression' && radiusDetails) {
|
|
|
|
return {
|
|
|
|
val: [
|
|
|
|
centerDetails?.expr.elements[0],
|
|
|
|
centerDetails?.expr.elements[1],
|
|
|
|
radiusDetails.expr,
|
|
|
|
],
|
|
|
|
tag,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return new Error('expected ArrayExpression or ObjectExpression')
|
|
|
|
}
|
2023-03-19 18:46:39 +11:00
|
|
|
const getAngledLineThatIntersects = (
|
|
|
|
callExp: CallExpression
|
2024-06-24 11:45:40 -04:00
|
|
|
):
|
|
|
|
| {
|
2024-08-12 15:38:42 -05:00
|
|
|
val: [Expr, Expr, Expr]
|
|
|
|
tag?: Expr
|
2024-06-24 11:45:40 -04:00
|
|
|
}
|
|
|
|
| Error => {
|
2023-03-19 18:46:39 +11:00
|
|
|
const firstArg = callExp.arguments[0]
|
|
|
|
if (firstArg.type === 'ObjectExpression') {
|
|
|
|
const tag = firstArg.properties.find((p) => p.key.name === 'tag')?.value
|
|
|
|
const angle = firstArg.properties.find((p) => p.key.name === 'angle')?.value
|
|
|
|
const offset = firstArg.properties.find(
|
|
|
|
(p) => p.key.name === 'offset'
|
|
|
|
)?.value
|
2023-04-03 16:05:25 +10:00
|
|
|
const intersectTag = firstArg.properties.find(
|
|
|
|
(p) => p.key.name === 'intersectTag'
|
|
|
|
)?.value
|
|
|
|
if (angle && offset && intersectTag) {
|
|
|
|
return { val: [angle, offset, intersectTag], tag }
|
2023-03-19 18:46:39 +11:00
|
|
|
}
|
|
|
|
}
|
2024-06-24 11:45:40 -04:00
|
|
|
return new Error('expected ArrayExpression or ObjectExpression')
|
2023-03-19 18:46:39 +11:00
|
|
|
}
|
|
|
|
|
2024-06-24 11:45:40 -04:00
|
|
|
export function getFirstArg(callExp: CallExpression):
|
|
|
|
| {
|
2024-08-12 15:38:42 -05:00
|
|
|
val: Expr | [Expr, Expr] | [Expr, Expr, Expr]
|
|
|
|
tag?: Expr
|
2024-06-24 11:45:40 -04:00
|
|
|
}
|
|
|
|
| Error {
|
2023-03-02 21:19:11 +11:00
|
|
|
const name = callExp?.callee?.name
|
|
|
|
if (['lineTo', 'line'].includes(name)) {
|
|
|
|
return getFirstArgValuesForXYFns(callExp)
|
|
|
|
}
|
|
|
|
if (
|
|
|
|
[
|
|
|
|
'angledLine',
|
|
|
|
'angledLineOfXLength',
|
|
|
|
'angledLineToX',
|
|
|
|
'angledLineOfYLength',
|
|
|
|
'angledLineToY',
|
|
|
|
].includes(name)
|
|
|
|
) {
|
|
|
|
return getFirstArgValuesForAngleFns(callExp)
|
|
|
|
}
|
|
|
|
if (['xLine', 'yLine', 'xLineTo', 'yLineTo'].includes(name)) {
|
|
|
|
return getFirstArgValuesForXYLineFns(callExp)
|
|
|
|
}
|
2023-03-17 08:27:40 +11:00
|
|
|
if (['startSketchAt'].includes(name)) {
|
|
|
|
return getFirstArgValuesForXYLineFns(callExp)
|
|
|
|
}
|
2023-03-19 18:46:39 +11:00
|
|
|
if (['angledLineThatIntersects'].includes(name)) {
|
|
|
|
return getAngledLineThatIntersects(callExp)
|
|
|
|
}
|
2024-02-11 12:59:00 +11:00
|
|
|
if (['tangentialArcTo'].includes(name)) {
|
|
|
|
// TODO probably needs it's own implementation
|
|
|
|
return getFirstArgValuesForXYFns(callExp)
|
|
|
|
}
|
2024-09-23 22:42:51 +10:00
|
|
|
if (name === 'circle') {
|
|
|
|
return getCircle(callExp)
|
|
|
|
}
|
2024-06-24 11:45:40 -04:00
|
|
|
return new Error('unexpected call expression: ' + name)
|
2023-02-21 10:50:45 +11:00
|
|
|
}
|