2023-03-02 21:19:11 +11:00
|
|
|
import { TransformCallback } from './stdTypes'
|
2023-10-16 21:20:05 +11:00
|
|
|
import { toolTips, ToolTip } from '../../useStore'
|
|
|
|
import { Selections, Selection } from 'lib/selections'
|
2023-03-02 21:19:11 +11:00
|
|
|
import {
|
|
|
|
CallExpression,
|
|
|
|
Program,
|
|
|
|
Value,
|
2023-04-06 12:45:56 +10:00
|
|
|
BinaryPart,
|
2023-03-02 21:19:11 +11:00
|
|
|
VariableDeclarator,
|
2023-09-29 11:11:01 -07:00
|
|
|
PathToNode,
|
|
|
|
ProgramMemory,
|
|
|
|
} from '../wasm'
|
2023-03-03 20:35:48 +11:00
|
|
|
import {
|
|
|
|
getNodeFromPath,
|
|
|
|
getNodeFromPathCurry,
|
|
|
|
getNodePathFromSourceRange,
|
2023-04-06 12:45:56 +10:00
|
|
|
isValueZero,
|
2023-03-03 20:35:48 +11:00
|
|
|
} from '../queryAst'
|
2023-03-02 21:19:11 +11:00
|
|
|
import {
|
2023-04-05 21:06:20 +10:00
|
|
|
createBinaryExpression,
|
2023-04-02 17:20:11 +10:00
|
|
|
createBinaryExpressionWithUnary,
|
2023-03-02 21:19:11 +11:00
|
|
|
createCallExpression,
|
2023-03-10 08:48:50 +11:00
|
|
|
createIdentifier,
|
2023-03-02 21:19:11 +11:00
|
|
|
createLiteral,
|
2023-03-19 18:46:39 +11:00
|
|
|
createObjectExpression,
|
2023-03-02 21:19:11 +11:00
|
|
|
createPipeSubstitution,
|
|
|
|
createUnaryExpression,
|
|
|
|
giveSketchFnCallTag,
|
|
|
|
} from '../modifyAst'
|
|
|
|
import { createFirstArg, getFirstArg, replaceSketchLine } from './sketch'
|
2023-03-20 07:09:19 +11:00
|
|
|
import { getSketchSegmentFromSourceRange } from './sketchConstraints'
|
2023-04-05 21:06:20 +10:00
|
|
|
import { getAngle, roundOff, normaliseAngle } from '../../lib/utils'
|
2023-03-02 21:19:11 +11:00
|
|
|
|
|
|
|
type LineInputsType =
|
|
|
|
| 'xAbsolute'
|
|
|
|
| 'yAbsolute'
|
|
|
|
| 'xRelative'
|
|
|
|
| 'yRelative'
|
|
|
|
| 'angle'
|
|
|
|
| 'length'
|
|
|
|
|
|
|
|
export type ConstraintType =
|
|
|
|
| 'equalLength'
|
|
|
|
| 'vertical'
|
|
|
|
| 'horizontal'
|
2023-03-17 21:15:46 +11:00
|
|
|
| 'equalAngle'
|
2023-03-05 07:34:56 +11:00
|
|
|
| 'setHorzDistance'
|
|
|
|
| 'setVertDistance'
|
2023-03-10 08:48:50 +11:00
|
|
|
| 'setAngle'
|
|
|
|
| 'setLength'
|
2023-03-19 18:46:39 +11:00
|
|
|
| 'intersect'
|
2023-03-22 03:02:37 +11:00
|
|
|
| 'removeConstrainingValues'
|
2023-04-05 15:08:46 +10:00
|
|
|
| 'xAbs'
|
|
|
|
| 'yAbs'
|
2023-04-05 21:06:20 +10:00
|
|
|
| 'setAngleBetween'
|
2023-03-02 21:19:11 +11:00
|
|
|
|
|
|
|
function createCallWrapper(
|
2023-09-15 11:48:23 -04:00
|
|
|
a: ToolTip,
|
2023-03-02 21:19:11 +11:00
|
|
|
val: [Value, Value] | Value,
|
2023-03-07 15:45:59 +11:00
|
|
|
tag?: Value,
|
|
|
|
valueUsedInTransform?: number
|
|
|
|
): ReturnType<TransformCallback> {
|
2024-03-15 17:03:42 -04:00
|
|
|
const args = [createFirstArg(a, val), createPipeSubstitution()]
|
|
|
|
if (tag) {
|
|
|
|
args.push(tag)
|
|
|
|
}
|
2023-03-07 15:45:59 +11:00
|
|
|
return {
|
2024-03-15 17:03:42 -04:00
|
|
|
callExp: createCallExpression(a, args),
|
2023-03-07 15:45:59 +11:00
|
|
|
valueUsedInTransform,
|
|
|
|
}
|
2023-03-02 21:19:11 +11:00
|
|
|
}
|
|
|
|
|
2023-03-19 18:46:39 +11:00
|
|
|
function intersectCallWrapper({
|
|
|
|
fnName,
|
|
|
|
angleVal,
|
|
|
|
offsetVal,
|
|
|
|
intersectTag,
|
|
|
|
tag,
|
|
|
|
valueUsedInTransform,
|
|
|
|
}: {
|
|
|
|
fnName: string
|
|
|
|
angleVal: Value
|
|
|
|
offsetVal: Value
|
|
|
|
intersectTag: Value
|
|
|
|
tag?: Value
|
|
|
|
valueUsedInTransform?: number
|
|
|
|
}): ReturnType<TransformCallback> {
|
|
|
|
const firstArg: any = {
|
|
|
|
angle: angleVal,
|
|
|
|
offset: offsetVal,
|
|
|
|
intersectTag,
|
|
|
|
}
|
2024-03-15 17:03:42 -04:00
|
|
|
const args: Value[] = [
|
|
|
|
createObjectExpression(firstArg),
|
|
|
|
createPipeSubstitution(),
|
|
|
|
]
|
2023-03-19 18:46:39 +11:00
|
|
|
if (tag) {
|
2024-03-15 17:03:42 -04:00
|
|
|
args.push(tag)
|
2023-03-19 18:46:39 +11:00
|
|
|
}
|
|
|
|
return {
|
2024-03-15 17:03:42 -04:00
|
|
|
callExp: createCallExpression(fnName, args),
|
2023-03-19 18:46:39 +11:00
|
|
|
valueUsedInTransform,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-02 21:19:11 +11:00
|
|
|
export type TransformInfo = {
|
2023-09-15 11:48:23 -04:00
|
|
|
tooltip: ToolTip
|
2023-03-02 21:19:11 +11:00
|
|
|
createNode: (a: {
|
|
|
|
varValA: Value // x / angle
|
|
|
|
varValB: Value // y / length or x y for angledLineOfXlength etc
|
|
|
|
referenceSegName: string
|
|
|
|
tag?: Value
|
2023-03-07 15:45:59 +11:00
|
|
|
forceValueUsedInTransform?: Value
|
|
|
|
}) => TransformCallback
|
2023-03-02 21:19:11 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
type TransformMap = {
|
2023-09-15 11:48:23 -04:00
|
|
|
[key in ToolTip]?: {
|
2023-03-02 21:19:11 +11:00
|
|
|
[key in LineInputsType | 'free']?: {
|
|
|
|
[key in ConstraintType]?: TransformInfo
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-10 14:55:16 +11:00
|
|
|
const xyLineSetLength =
|
|
|
|
(
|
|
|
|
xOrY: 'xLine' | 'yLine',
|
|
|
|
referenceSeg = false
|
|
|
|
): TransformInfo['createNode'] =>
|
|
|
|
({ referenceSegName, tag, forceValueUsedInTransform }) =>
|
|
|
|
(args) => {
|
|
|
|
const segRef = createSegLen(referenceSegName)
|
|
|
|
const lineVal = forceValueUsedInTransform
|
|
|
|
? forceValueUsedInTransform
|
|
|
|
: referenceSeg
|
|
|
|
? segRef
|
|
|
|
: args[0]
|
|
|
|
return createCallWrapper(xOrY, lineVal, tag, getArgLiteralVal(args[0]))
|
|
|
|
}
|
|
|
|
|
2023-03-10 08:48:50 +11:00
|
|
|
const basicAngledLineCreateNode =
|
|
|
|
(
|
|
|
|
referenceSeg: 'ang' | 'len' | 'none' = 'none',
|
|
|
|
valToForce: 'ang' | 'len' | 'none' = 'none',
|
|
|
|
varValToUse: 'ang' | 'len' | 'none' = 'none'
|
|
|
|
): TransformInfo['createNode'] =>
|
|
|
|
({ referenceSegName, tag, forceValueUsedInTransform, varValA, varValB }) =>
|
2023-03-17 21:15:46 +11:00
|
|
|
(args, path) => {
|
|
|
|
const refAng = path ? getAngle(path?.from, path?.to) : 0
|
2023-03-10 08:48:50 +11:00
|
|
|
const nonForcedAng =
|
|
|
|
varValToUse === 'ang'
|
|
|
|
? varValA
|
|
|
|
: referenceSeg === 'ang'
|
2023-03-17 21:15:46 +11:00
|
|
|
? getClosesAngleDirection(
|
|
|
|
args[0],
|
|
|
|
refAng,
|
|
|
|
createSegAngle(referenceSegName) as BinaryPart
|
|
|
|
)
|
2023-03-10 08:48:50 +11:00
|
|
|
: args[0]
|
|
|
|
const nonForcedLen =
|
|
|
|
varValToUse === 'len'
|
|
|
|
? varValB
|
|
|
|
: referenceSeg === 'len'
|
|
|
|
? createSegLen(referenceSegName)
|
|
|
|
: args[1]
|
|
|
|
const shouldForceAng = valToForce === 'ang' && forceValueUsedInTransform
|
|
|
|
const shouldForceLen = valToForce === 'len' && forceValueUsedInTransform
|
|
|
|
return createCallWrapper(
|
2023-03-02 21:19:11 +11:00
|
|
|
'angledLine',
|
2023-03-10 08:48:50 +11:00
|
|
|
[
|
|
|
|
shouldForceAng ? forceValueUsedInTransform : nonForcedAng,
|
|
|
|
shouldForceLen ? forceValueUsedInTransform : nonForcedLen,
|
|
|
|
],
|
|
|
|
tag,
|
|
|
|
getArgLiteralVal(valToForce === 'ang' ? args[0] : args[1])
|
2023-03-02 21:19:11 +11:00
|
|
|
)
|
2023-03-10 08:48:50 +11:00
|
|
|
}
|
2023-03-02 21:19:11 +11:00
|
|
|
const angledLineAngleCreateNode: TransformInfo['createNode'] =
|
|
|
|
({ referenceSegName, varValA, tag }) =>
|
|
|
|
() =>
|
|
|
|
createCallWrapper(
|
|
|
|
'angledLine',
|
|
|
|
[varValA, createSegLen(referenceSegName)],
|
|
|
|
tag
|
|
|
|
)
|
|
|
|
|
|
|
|
const getMinAndSegLenVals = (
|
|
|
|
referenceSegName: string,
|
|
|
|
varVal: Value
|
|
|
|
): [Value, BinaryPart] => {
|
|
|
|
const segLenVal = createSegLen(referenceSegName)
|
|
|
|
return [
|
|
|
|
createCallExpression('min', [segLenVal, varVal]),
|
|
|
|
createCallExpression('legLen', [segLenVal, varVal]),
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
const getMinAndSegAngVals = (
|
|
|
|
referenceSegName: string,
|
|
|
|
varVal: Value,
|
|
|
|
fnName: 'legAngX' | 'legAngY' = 'legAngX'
|
|
|
|
): [Value, BinaryPart] => {
|
|
|
|
const minVal = createCallExpression('min', [
|
|
|
|
createSegLen(referenceSegName),
|
|
|
|
varVal,
|
|
|
|
])
|
|
|
|
const legAngle = createCallExpression(fnName, [
|
|
|
|
createSegLen(referenceSegName),
|
|
|
|
varVal,
|
|
|
|
])
|
|
|
|
return [minVal, legAngle]
|
|
|
|
}
|
|
|
|
|
|
|
|
const getSignedLeg = (arg: Value, legLenVal: BinaryPart) =>
|
|
|
|
arg.type === 'Literal' && Number(arg.value) < 0
|
|
|
|
? createUnaryExpression(legLenVal)
|
|
|
|
: legLenVal
|
|
|
|
|
|
|
|
const getLegAng = (arg: Value, legAngleVal: BinaryPart) => {
|
|
|
|
const ang = (arg.type === 'Literal' && Number(arg.value)) || 0
|
|
|
|
const normalisedAngle = ((ang % 360) + 360) % 360 // between 0 and 360
|
|
|
|
const truncatedTo90 = Math.floor(normalisedAngle / 90) * 90
|
2023-04-02 17:20:11 +10:00
|
|
|
const binExp = createBinaryExpressionWithUnary([
|
2023-03-02 21:19:11 +11:00
|
|
|
createLiteral(truncatedTo90),
|
|
|
|
legAngleVal,
|
|
|
|
])
|
2023-03-10 08:48:50 +11:00
|
|
|
return truncatedTo90 === 0 ? legAngleVal : binExp
|
2023-03-02 21:19:11 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
const getAngleLengthSign = (arg: Value, legAngleVal: BinaryPart) => {
|
|
|
|
const ang = (arg.type === 'Literal' && Number(arg.value)) || 0
|
|
|
|
const normalisedAngle = ((ang % 180) + 180) % 180 // between 0 and 180
|
|
|
|
return normalisedAngle > 90 ? createUnaryExpression(legAngleVal) : legAngleVal
|
|
|
|
}
|
|
|
|
|
2023-03-17 21:15:46 +11:00
|
|
|
function getClosesAngleDirection(
|
|
|
|
arg: Value,
|
|
|
|
refAngle: number,
|
|
|
|
angleVal: BinaryPart
|
|
|
|
) {
|
|
|
|
const currentAng = (arg.type === 'Literal' && Number(arg.value)) || 0
|
|
|
|
const angDiff = Math.abs(currentAng - refAngle)
|
|
|
|
const normalisedAngle = ((angDiff % 360) + 360) % 360 // between 0 and 180
|
|
|
|
return normalisedAngle > 90
|
2023-04-02 17:20:11 +10:00
|
|
|
? createBinaryExpressionWithUnary([angleVal, createLiteral(180)])
|
2023-03-17 21:15:46 +11:00
|
|
|
: angleVal
|
|
|
|
}
|
|
|
|
|
2023-03-05 07:34:56 +11:00
|
|
|
const setHorzVertDistanceCreateNode =
|
2023-03-07 15:45:59 +11:00
|
|
|
(
|
|
|
|
xOrY: 'x' | 'y',
|
|
|
|
index = xOrY === 'x' ? 0 : 1
|
|
|
|
): TransformInfo['createNode'] =>
|
|
|
|
({ referenceSegName, tag, forceValueUsedInTransform }) => {
|
2023-03-05 07:34:56 +11:00
|
|
|
return (args, referencedSegment) => {
|
2023-03-07 15:45:59 +11:00
|
|
|
const valueUsedInTransform = roundOff(
|
|
|
|
getArgLiteralVal(args?.[index]) - (referencedSegment?.to?.[index] || 0),
|
|
|
|
2
|
|
|
|
)
|
2023-04-06 12:45:56 +10:00
|
|
|
let finalValue: Value = createBinaryExpressionWithUnary([
|
2023-03-07 15:45:59 +11:00
|
|
|
createSegEnd(referenceSegName, !index),
|
|
|
|
(forceValueUsedInTransform as BinaryPart) ||
|
|
|
|
createLiteral(valueUsedInTransform),
|
|
|
|
])
|
2023-04-06 12:45:56 +10:00
|
|
|
if (isValueZero(forceValueUsedInTransform)) {
|
|
|
|
finalValue = createSegEnd(referenceSegName, !index)
|
|
|
|
}
|
2023-03-05 07:34:56 +11:00
|
|
|
return createCallWrapper(
|
|
|
|
'lineTo',
|
2023-04-06 12:45:56 +10:00
|
|
|
!index ? [finalValue, args[1]] : [args[0], finalValue],
|
2023-03-07 15:45:59 +11:00
|
|
|
tag,
|
|
|
|
valueUsedInTransform
|
2023-03-05 07:34:56 +11:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2023-03-17 21:15:46 +11:00
|
|
|
const setHorzVertDistanceForAngleLineCreateNode =
|
|
|
|
(
|
|
|
|
xOrY: 'x' | 'y',
|
|
|
|
index = xOrY === 'x' ? 0 : 1
|
|
|
|
): TransformInfo['createNode'] =>
|
|
|
|
({ referenceSegName, tag, forceValueUsedInTransform, varValA }) => {
|
|
|
|
return (args, referencedSegment) => {
|
|
|
|
const valueUsedInTransform = roundOff(
|
|
|
|
getArgLiteralVal(args?.[1]) - (referencedSegment?.to?.[index] || 0),
|
|
|
|
2
|
|
|
|
)
|
2023-04-02 17:20:11 +10:00
|
|
|
const binExp = createBinaryExpressionWithUnary([
|
2023-03-17 21:15:46 +11:00
|
|
|
createSegEnd(referenceSegName, !index),
|
|
|
|
(forceValueUsedInTransform as BinaryPart) ||
|
|
|
|
createLiteral(valueUsedInTransform),
|
|
|
|
])
|
|
|
|
return createCallWrapper(
|
|
|
|
xOrY === 'x' ? 'angledLineToX' : 'angledLineToY',
|
2023-04-02 17:20:11 +10:00
|
|
|
[varValA, binExp],
|
2023-03-17 21:15:46 +11:00
|
|
|
tag,
|
|
|
|
valueUsedInTransform
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-05 15:08:46 +10:00
|
|
|
const setAbsDistanceCreateNode =
|
|
|
|
(
|
|
|
|
xOrY: 'x' | 'y',
|
|
|
|
isXOrYLine = false,
|
|
|
|
index = xOrY === 'x' ? 0 : 1
|
|
|
|
): TransformInfo['createNode'] =>
|
2023-04-05 21:06:20 +10:00
|
|
|
({ tag, forceValueUsedInTransform }) => {
|
|
|
|
return (args, referencedSegment) => {
|
2023-04-05 15:08:46 +10:00
|
|
|
const valueUsedInTransform = roundOff(
|
|
|
|
getArgLiteralVal(args?.[index]) - (referencedSegment?.to?.[index] || 0),
|
|
|
|
2
|
|
|
|
)
|
|
|
|
const val =
|
|
|
|
(forceValueUsedInTransform as BinaryPart) ||
|
|
|
|
createLiteral(valueUsedInTransform)
|
|
|
|
if (isXOrYLine) {
|
|
|
|
return createCallWrapper(
|
|
|
|
xOrY === 'x' ? 'xLineTo' : 'yLineTo',
|
|
|
|
val,
|
|
|
|
tag,
|
|
|
|
valueUsedInTransform
|
|
|
|
)
|
|
|
|
}
|
|
|
|
return createCallWrapper(
|
|
|
|
'lineTo',
|
|
|
|
!index ? [val, args[1]] : [args[0], val],
|
|
|
|
tag,
|
|
|
|
valueUsedInTransform
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const setAbsDistanceForAngleLineCreateNode =
|
|
|
|
(
|
|
|
|
xOrY: 'x' | 'y',
|
|
|
|
index = xOrY === 'x' ? 0 : 1
|
|
|
|
): TransformInfo['createNode'] =>
|
|
|
|
({ tag, forceValueUsedInTransform, varValA }) => {
|
|
|
|
return (args, referencedSegment) => {
|
2023-12-01 20:18:51 +11:00
|
|
|
const valueUsedInTransform = roundOff(getArgLiteralVal(args?.[1]), 2)
|
2023-04-05 15:08:46 +10:00
|
|
|
const val =
|
|
|
|
(forceValueUsedInTransform as BinaryPart) ||
|
|
|
|
createLiteral(valueUsedInTransform)
|
|
|
|
return createCallWrapper(
|
|
|
|
xOrY === 'x' ? 'angledLineToX' : 'angledLineToY',
|
|
|
|
[varValA, val],
|
|
|
|
tag,
|
|
|
|
valueUsedInTransform
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-17 21:15:46 +11:00
|
|
|
const setHorVertDistanceForXYLines =
|
|
|
|
(xOrY: 'x' | 'y'): TransformInfo['createNode'] =>
|
|
|
|
({ referenceSegName, tag, forceValueUsedInTransform }) => {
|
|
|
|
return (args, referencedSegment) => {
|
|
|
|
const index = xOrY === 'x' ? 0 : 1
|
|
|
|
const valueUsedInTransform = roundOff(
|
|
|
|
getArgLiteralVal(args?.[index]) - (referencedSegment?.to?.[index] || 0),
|
|
|
|
2
|
|
|
|
)
|
2023-04-02 17:20:11 +10:00
|
|
|
const makeBinExp = createBinaryExpressionWithUnary([
|
2023-03-17 21:15:46 +11:00
|
|
|
createSegEnd(referenceSegName, xOrY === 'x'),
|
|
|
|
(forceValueUsedInTransform as BinaryPart) ||
|
|
|
|
createLiteral(valueUsedInTransform),
|
|
|
|
])
|
|
|
|
return createCallWrapper(
|
|
|
|
xOrY === 'x' ? 'xLineTo' : 'yLineTo',
|
|
|
|
makeBinExp,
|
|
|
|
tag,
|
|
|
|
valueUsedInTransform
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2023-03-05 07:34:56 +11:00
|
|
|
|
|
|
|
const setHorzVertDistanceConstraintLineCreateNode =
|
|
|
|
(isX: boolean): TransformInfo['createNode'] =>
|
|
|
|
({ referenceSegName, tag, varValA, varValB }) => {
|
|
|
|
const varVal = (isX ? varValB : varValA) as BinaryPart
|
2023-04-02 17:20:11 +10:00
|
|
|
const varValBinExp = createBinaryExpressionWithUnary([
|
2023-03-05 07:34:56 +11:00
|
|
|
createLastSeg(!isX),
|
|
|
|
varVal,
|
|
|
|
])
|
|
|
|
|
|
|
|
return (args, referencedSegment) => {
|
|
|
|
const makeBinExp = (index: 0 | 1) => {
|
|
|
|
const arg = getArgLiteralVal(args?.[index])
|
2023-04-02 17:20:11 +10:00
|
|
|
return createBinaryExpressionWithUnary([
|
2023-03-05 07:34:56 +11:00
|
|
|
createSegEnd(referenceSegName, isX),
|
|
|
|
createLiteral(
|
|
|
|
roundOff(arg - (referencedSegment?.to?.[index] || 0), 2)
|
|
|
|
),
|
|
|
|
])
|
|
|
|
}
|
|
|
|
return createCallWrapper(
|
|
|
|
'lineTo',
|
|
|
|
isX ? [makeBinExp(0), varValBinExp] : [varValBinExp, makeBinExp(1)],
|
|
|
|
tag
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-19 18:46:39 +11:00
|
|
|
const setAngledIntersectLineForLines: TransformInfo['createNode'] =
|
|
|
|
({ referenceSegName, tag, forceValueUsedInTransform }) =>
|
|
|
|
(args) => {
|
|
|
|
const valueUsedInTransform = roundOff(
|
|
|
|
args[1].type === 'Literal' ? Number(args[1].value) : 0,
|
|
|
|
2
|
|
|
|
)
|
|
|
|
const angle = args[0].type === 'Literal' ? Number(args[0].value) : 0
|
2024-02-11 18:26:09 -08:00
|
|
|
const varNamMap: { [key: number]: string } = {
|
|
|
|
0: 'ZERO',
|
|
|
|
90: 'QUARTER_TURN',
|
|
|
|
180: 'HALF_TURN',
|
|
|
|
270: 'THREE_QUARTER_TURN',
|
|
|
|
}
|
2023-04-01 21:25:00 +11:00
|
|
|
const angleVal = [0, 90, 180, 270].includes(angle)
|
2024-02-11 18:26:09 -08:00
|
|
|
? createIdentifier(varNamMap[angle])
|
2023-04-01 21:25:00 +11:00
|
|
|
: createLiteral(angle)
|
2023-03-19 18:46:39 +11:00
|
|
|
return intersectCallWrapper({
|
|
|
|
fnName: 'angledLineThatIntersects',
|
2023-04-01 21:25:00 +11:00
|
|
|
angleVal,
|
2023-03-19 18:46:39 +11:00
|
|
|
offsetVal:
|
|
|
|
forceValueUsedInTransform || createLiteral(valueUsedInTransform),
|
|
|
|
intersectTag: createLiteral(referenceSegName),
|
|
|
|
tag,
|
|
|
|
valueUsedInTransform,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
const setAngledIntersectForAngledLines: TransformInfo['createNode'] =
|
|
|
|
({ referenceSegName, tag, forceValueUsedInTransform, varValA }) =>
|
|
|
|
(args) => {
|
|
|
|
const valueUsedInTransform = roundOff(
|
|
|
|
args[1].type === 'Literal' ? Number(args[1].value) : 0,
|
|
|
|
2
|
|
|
|
)
|
|
|
|
// const angle = args[0].type === 'Literal' ? Number(args[0].value) : 0
|
|
|
|
return intersectCallWrapper({
|
|
|
|
fnName: 'angledLineThatIntersects',
|
|
|
|
angleVal: varValA,
|
|
|
|
offsetVal:
|
|
|
|
forceValueUsedInTransform || createLiteral(valueUsedInTransform),
|
|
|
|
intersectTag: createLiteral(referenceSegName),
|
|
|
|
tag,
|
|
|
|
valueUsedInTransform,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-04-05 21:06:20 +10:00
|
|
|
const setAngleBetweenCreateNode =
|
|
|
|
(tranformToType: 'none' | 'xAbs' | 'yAbs'): TransformInfo['createNode'] =>
|
|
|
|
({ referenceSegName, tag, forceValueUsedInTransform, varValA, varValB }) => {
|
|
|
|
return (args, referencedSegment) => {
|
|
|
|
const refAngle = referencedSegment
|
|
|
|
? getAngle(referencedSegment?.from, referencedSegment?.to)
|
|
|
|
: 0
|
|
|
|
let valueUsedInTransform = roundOff(
|
|
|
|
normaliseAngle(
|
|
|
|
(args[0].type === 'Literal' ? Number(args[0].value) : 0) - refAngle
|
|
|
|
)
|
|
|
|
)
|
|
|
|
let firstHalfValue = createSegAngle(referenceSegName) as BinaryPart
|
|
|
|
if (Math.abs(valueUsedInTransform) > 90) {
|
|
|
|
firstHalfValue = createBinaryExpression([
|
|
|
|
firstHalfValue,
|
|
|
|
'+',
|
2024-02-11 18:26:09 -08:00
|
|
|
createIdentifier('HALF_TURN'),
|
2023-04-05 21:06:20 +10:00
|
|
|
])
|
|
|
|
valueUsedInTransform = normaliseAngle(valueUsedInTransform - 180)
|
|
|
|
}
|
|
|
|
const binExp = createBinaryExpressionWithUnary([
|
|
|
|
firstHalfValue,
|
|
|
|
(forceValueUsedInTransform as BinaryPart) ||
|
|
|
|
createLiteral(valueUsedInTransform),
|
|
|
|
])
|
|
|
|
return createCallWrapper(
|
|
|
|
tranformToType === 'none'
|
|
|
|
? 'angledLine'
|
|
|
|
: tranformToType === 'xAbs'
|
|
|
|
? 'angledLineToX'
|
|
|
|
: 'angledLineToY',
|
|
|
|
tranformToType === 'none'
|
|
|
|
? [binExp, args[1]]
|
|
|
|
: tranformToType === 'xAbs'
|
|
|
|
? [binExp, varValA]
|
|
|
|
: [binExp, varValB],
|
|
|
|
tag,
|
|
|
|
valueUsedInTransform
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-02 21:19:11 +11:00
|
|
|
const transformMap: TransformMap = {
|
|
|
|
line: {
|
|
|
|
xRelative: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'line',
|
|
|
|
createNode: ({ referenceSegName, varValA, tag }) => {
|
|
|
|
const [minVal, legLenVal] = getMinAndSegLenVals(
|
|
|
|
referenceSegName,
|
|
|
|
varValA
|
|
|
|
)
|
|
|
|
return (args) =>
|
|
|
|
createCallWrapper(
|
|
|
|
'line',
|
|
|
|
[minVal, getSignedLeg(args[1], legLenVal)],
|
|
|
|
tag
|
|
|
|
)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
horizontal: {
|
|
|
|
tooltip: 'xLine',
|
|
|
|
createNode:
|
|
|
|
({ varValA, tag }) =>
|
|
|
|
() =>
|
|
|
|
createCallWrapper('xLine', varValA, tag),
|
|
|
|
},
|
2023-03-05 07:34:56 +11:00
|
|
|
setVertDistance: {
|
|
|
|
tooltip: 'lineTo',
|
|
|
|
createNode: setHorzVertDistanceConstraintLineCreateNode(false),
|
|
|
|
},
|
2023-03-02 21:19:11 +11:00
|
|
|
},
|
|
|
|
yRelative: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'line',
|
|
|
|
createNode: ({ referenceSegName, varValB, tag }) => {
|
|
|
|
const [minVal, legLenVal] = getMinAndSegLenVals(
|
|
|
|
referenceSegName,
|
|
|
|
varValB
|
|
|
|
)
|
|
|
|
return (args) =>
|
|
|
|
createCallWrapper(
|
|
|
|
'line',
|
|
|
|
[getSignedLeg(args[0], legLenVal), minVal],
|
|
|
|
tag
|
|
|
|
)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
vertical: {
|
|
|
|
tooltip: 'yLine',
|
|
|
|
createNode:
|
|
|
|
({ varValB, tag }) =>
|
|
|
|
() =>
|
|
|
|
createCallWrapper('yLine', varValB, tag),
|
|
|
|
},
|
2023-03-05 07:34:56 +11:00
|
|
|
setHorzDistance: {
|
|
|
|
tooltip: 'lineTo',
|
|
|
|
createNode: setHorzVertDistanceConstraintLineCreateNode(true),
|
|
|
|
},
|
2023-03-02 21:19:11 +11:00
|
|
|
},
|
|
|
|
free: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'angledLine',
|
2023-03-10 08:48:50 +11:00
|
|
|
createNode: basicAngledLineCreateNode('len'),
|
2023-03-02 21:19:11 +11:00
|
|
|
},
|
|
|
|
horizontal: {
|
|
|
|
tooltip: 'xLine',
|
|
|
|
createNode:
|
|
|
|
({ tag }) =>
|
|
|
|
(args) =>
|
|
|
|
createCallWrapper('xLine', args[0], tag),
|
|
|
|
},
|
|
|
|
vertical: {
|
|
|
|
tooltip: 'yLine',
|
|
|
|
createNode:
|
|
|
|
({ tag }) =>
|
|
|
|
(args) =>
|
|
|
|
createCallWrapper('yLine', args[1], tag),
|
|
|
|
},
|
2023-03-05 07:34:56 +11:00
|
|
|
setHorzDistance: {
|
|
|
|
tooltip: 'lineTo',
|
2023-03-07 15:45:59 +11:00
|
|
|
createNode: setHorzVertDistanceCreateNode('x'),
|
2023-03-05 07:34:56 +11:00
|
|
|
},
|
2023-04-05 15:08:46 +10:00
|
|
|
xAbs: {
|
|
|
|
tooltip: 'lineTo',
|
|
|
|
createNode: setAbsDistanceCreateNode('x'),
|
|
|
|
},
|
2023-03-05 07:34:56 +11:00
|
|
|
setVertDistance: {
|
|
|
|
tooltip: 'lineTo',
|
2023-03-07 15:45:59 +11:00
|
|
|
createNode: setHorzVertDistanceCreateNode('y'),
|
2023-03-05 07:34:56 +11:00
|
|
|
},
|
2023-04-05 15:08:46 +10:00
|
|
|
yAbs: {
|
|
|
|
tooltip: 'lineTo',
|
|
|
|
createNode: setAbsDistanceCreateNode('y'),
|
|
|
|
},
|
2023-03-10 08:48:50 +11:00
|
|
|
setAngle: {
|
|
|
|
tooltip: 'angledLine',
|
|
|
|
createNode: basicAngledLineCreateNode('none', 'ang'),
|
|
|
|
},
|
|
|
|
setLength: {
|
|
|
|
tooltip: 'angledLine',
|
|
|
|
createNode: basicAngledLineCreateNode('none', 'len'),
|
|
|
|
},
|
2023-03-17 21:15:46 +11:00
|
|
|
equalAngle: {
|
|
|
|
tooltip: 'angledLine',
|
|
|
|
createNode: basicAngledLineCreateNode('ang'),
|
|
|
|
},
|
2023-03-19 18:46:39 +11:00
|
|
|
intersect: {
|
|
|
|
tooltip: 'angledLineThatIntersects',
|
|
|
|
createNode: setAngledIntersectLineForLines,
|
|
|
|
},
|
2023-04-05 21:06:20 +10:00
|
|
|
setAngleBetween: {
|
|
|
|
tooltip: 'angledLine',
|
|
|
|
createNode: setAngleBetweenCreateNode('none'),
|
|
|
|
},
|
2023-03-02 21:19:11 +11:00
|
|
|
},
|
|
|
|
},
|
|
|
|
lineTo: {
|
|
|
|
free: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'angledLine',
|
2023-03-10 08:48:50 +11:00
|
|
|
createNode: basicAngledLineCreateNode('len'),
|
2023-03-02 21:19:11 +11:00
|
|
|
},
|
|
|
|
horizontal: {
|
|
|
|
tooltip: 'xLineTo',
|
|
|
|
createNode:
|
|
|
|
({ tag }) =>
|
|
|
|
(args) =>
|
|
|
|
createCallWrapper('xLineTo', args[0], tag),
|
|
|
|
},
|
|
|
|
vertical: {
|
|
|
|
tooltip: 'yLineTo',
|
|
|
|
createNode:
|
|
|
|
({ tag }) =>
|
|
|
|
(args) =>
|
|
|
|
createCallWrapper('yLineTo', args[1], tag),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
xAbsolute: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'angledLineToX',
|
|
|
|
createNode:
|
|
|
|
({ referenceSegName, varValA, tag }) =>
|
|
|
|
(args) => {
|
|
|
|
const angleToMatchLengthXCall = createCallExpression(
|
|
|
|
'angleToMatchLengthX',
|
|
|
|
[
|
|
|
|
createLiteral(referenceSegName),
|
|
|
|
varValA,
|
|
|
|
createPipeSubstitution(),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
return createCallWrapper(
|
|
|
|
'angledLineToX',
|
|
|
|
[getAngleLengthSign(args[0], angleToMatchLengthXCall), varValA],
|
|
|
|
tag
|
|
|
|
)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
horizontal: {
|
|
|
|
tooltip: 'xLineTo',
|
|
|
|
createNode:
|
|
|
|
({ varValA, tag }) =>
|
|
|
|
() =>
|
|
|
|
createCallWrapper('xLineTo', varValA, tag),
|
|
|
|
},
|
2023-04-05 21:06:20 +10:00
|
|
|
setAngleBetween: {
|
|
|
|
tooltip: 'angledLineToX',
|
|
|
|
createNode: setAngleBetweenCreateNode('xAbs'),
|
|
|
|
},
|
2023-03-02 21:19:11 +11:00
|
|
|
},
|
|
|
|
yAbsolute: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'angledLineToY',
|
|
|
|
createNode:
|
|
|
|
({ referenceSegName, varValB, tag }) =>
|
|
|
|
(args) => {
|
|
|
|
const angleToMatchLengthYCall = createCallExpression(
|
|
|
|
'angleToMatchLengthY',
|
|
|
|
[
|
|
|
|
createLiteral(referenceSegName),
|
|
|
|
varValB,
|
|
|
|
createPipeSubstitution(),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
return createCallWrapper(
|
|
|
|
'angledLineToY',
|
|
|
|
[getAngleLengthSign(args[0], angleToMatchLengthYCall), varValB],
|
|
|
|
tag
|
|
|
|
)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
vertical: {
|
|
|
|
tooltip: 'yLineTo',
|
|
|
|
createNode:
|
|
|
|
({ varValB, tag }) =>
|
|
|
|
() =>
|
|
|
|
createCallWrapper('yLineTo', varValB, tag),
|
|
|
|
},
|
2023-03-10 14:55:16 +11:00
|
|
|
setAngle: {
|
|
|
|
tooltip: 'angledLineToY',
|
|
|
|
createNode:
|
|
|
|
({ varValB, tag, forceValueUsedInTransform }) =>
|
|
|
|
(args) => {
|
|
|
|
return createCallWrapper(
|
|
|
|
'angledLineToY',
|
|
|
|
[forceValueUsedInTransform || args[0], varValB],
|
|
|
|
tag,
|
|
|
|
getArgLiteralVal(args[0])
|
|
|
|
)
|
|
|
|
},
|
|
|
|
},
|
2023-04-05 21:06:20 +10:00
|
|
|
setAngleBetween: {
|
|
|
|
tooltip: 'angledLineToY',
|
|
|
|
createNode: setAngleBetweenCreateNode('yAbs'),
|
|
|
|
},
|
2023-03-02 21:19:11 +11:00
|
|
|
},
|
|
|
|
},
|
|
|
|
angledLine: {
|
|
|
|
angle: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'angledLine',
|
|
|
|
createNode:
|
|
|
|
({ referenceSegName, varValA, tag }) =>
|
|
|
|
() =>
|
|
|
|
createCallWrapper(
|
|
|
|
'angledLine',
|
|
|
|
[varValA, createSegLen(referenceSegName)],
|
|
|
|
tag
|
|
|
|
),
|
|
|
|
},
|
2023-03-10 08:48:50 +11:00
|
|
|
setLength: {
|
|
|
|
tooltip: 'angledLine',
|
|
|
|
createNode: basicAngledLineCreateNode('none', 'len', 'ang'),
|
|
|
|
},
|
2023-03-17 21:15:46 +11:00
|
|
|
setVertDistance: {
|
|
|
|
tooltip: 'angledLineToY',
|
|
|
|
createNode: setHorzVertDistanceForAngleLineCreateNode('y'),
|
|
|
|
},
|
2023-04-05 15:08:46 +10:00
|
|
|
yAbs: {
|
|
|
|
tooltip: 'angledLineToY',
|
|
|
|
createNode: setAbsDistanceForAngleLineCreateNode('y'),
|
|
|
|
},
|
2023-03-17 21:15:46 +11:00
|
|
|
setHorzDistance: {
|
|
|
|
tooltip: 'angledLineToX',
|
|
|
|
createNode: setHorzVertDistanceForAngleLineCreateNode('x'),
|
|
|
|
},
|
2023-04-05 15:08:46 +10:00
|
|
|
xAbs: {
|
|
|
|
tooltip: 'angledLineToX',
|
|
|
|
createNode: setAbsDistanceForAngleLineCreateNode('x'),
|
|
|
|
},
|
2023-03-19 18:46:39 +11:00
|
|
|
intersect: {
|
|
|
|
tooltip: 'angledLineThatIntersects',
|
|
|
|
createNode: setAngledIntersectForAngledLines,
|
|
|
|
},
|
2023-03-02 21:19:11 +11:00
|
|
|
},
|
|
|
|
free: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'angledLine',
|
2023-03-10 08:48:50 +11:00
|
|
|
createNode: basicAngledLineCreateNode('len'),
|
2023-04-21 12:18:20 +10:00
|
|
|
},
|
|
|
|
setLength: {
|
|
|
|
tooltip: 'angledLine',
|
|
|
|
createNode: basicAngledLineCreateNode('none', 'len'),
|
2023-03-02 21:19:11 +11:00
|
|
|
},
|
|
|
|
vertical: {
|
|
|
|
tooltip: 'yLine',
|
|
|
|
createNode:
|
|
|
|
({ tag }) =>
|
|
|
|
(args) =>
|
|
|
|
createCallWrapper('yLine', args[1], tag),
|
|
|
|
},
|
|
|
|
horizontal: {
|
|
|
|
tooltip: 'xLine',
|
|
|
|
createNode:
|
|
|
|
({ tag }) =>
|
|
|
|
(args) =>
|
|
|
|
createCallWrapper('xLine', args[0], tag),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
length: {
|
|
|
|
vertical: {
|
|
|
|
tooltip: 'yLine',
|
|
|
|
createNode:
|
|
|
|
({ varValB, tag }) =>
|
|
|
|
([arg0]) => {
|
|
|
|
const val =
|
|
|
|
arg0.type === 'Literal' && Number(arg0.value) < 0
|
|
|
|
? createUnaryExpression(varValB as BinaryPart)
|
|
|
|
: varValB
|
|
|
|
return createCallWrapper('yLine', val, tag)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
horizontal: {
|
|
|
|
tooltip: 'xLine',
|
|
|
|
createNode:
|
|
|
|
({ varValB, tag }) =>
|
|
|
|
([arg0]) => {
|
|
|
|
const val =
|
|
|
|
arg0.type === 'Literal' && Number(arg0.value) < 0
|
|
|
|
? createUnaryExpression(varValB as BinaryPart)
|
|
|
|
: varValB
|
|
|
|
return createCallWrapper('xLine', val, tag)
|
|
|
|
},
|
|
|
|
},
|
2023-03-10 08:48:50 +11:00
|
|
|
setAngle: {
|
|
|
|
tooltip: 'angledLine',
|
|
|
|
createNode: basicAngledLineCreateNode('len', 'ang', 'len'),
|
|
|
|
},
|
2023-04-14 07:49:36 +10:00
|
|
|
equalAngle: {
|
|
|
|
tooltip: 'angledLine',
|
|
|
|
createNode: basicAngledLineCreateNode('ang', 'len', 'len'),
|
|
|
|
},
|
2023-03-02 21:19:11 +11:00
|
|
|
},
|
|
|
|
},
|
|
|
|
angledLineOfXLength: {
|
|
|
|
free: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'angledLine',
|
2023-03-10 08:48:50 +11:00
|
|
|
createNode: basicAngledLineCreateNode('len'),
|
2023-03-02 21:19:11 +11:00
|
|
|
},
|
|
|
|
horizontal: {
|
|
|
|
tooltip: 'xLine',
|
|
|
|
createNode:
|
|
|
|
({ tag }) =>
|
|
|
|
(args) =>
|
|
|
|
createCallWrapper('xLine', args[0], tag),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
angle: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'angledLine',
|
|
|
|
createNode: angledLineAngleCreateNode,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
xRelative: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'angledLineOfXLength',
|
|
|
|
createNode: ({ referenceSegName, varValB, tag }) => {
|
|
|
|
const [minVal, legAngle] = getMinAndSegAngVals(
|
|
|
|
referenceSegName,
|
|
|
|
varValB
|
|
|
|
)
|
|
|
|
return (args) =>
|
|
|
|
createCallWrapper(
|
|
|
|
'angledLineOfXLength',
|
|
|
|
[getLegAng(args[0], legAngle), minVal],
|
|
|
|
tag
|
|
|
|
)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
horizontal: {
|
|
|
|
tooltip: 'xLine',
|
|
|
|
createNode:
|
|
|
|
({ varValB, tag }) =>
|
|
|
|
([arg0]) => {
|
|
|
|
const val =
|
|
|
|
arg0.type === 'Literal' && Number(arg0.value) < 0
|
|
|
|
? createUnaryExpression(varValB as BinaryPart)
|
|
|
|
: varValB
|
|
|
|
return createCallWrapper('xLine', val, tag)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
angledLineOfYLength: {
|
|
|
|
free: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'angledLine',
|
2023-03-10 08:48:50 +11:00
|
|
|
createNode: basicAngledLineCreateNode('len'),
|
2023-03-02 21:19:11 +11:00
|
|
|
},
|
|
|
|
vertical: {
|
|
|
|
tooltip: 'yLine',
|
|
|
|
createNode:
|
|
|
|
({ tag }) =>
|
|
|
|
(args) =>
|
|
|
|
createCallWrapper('yLine', args[1], tag),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
angle: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'angledLine',
|
|
|
|
createNode: angledLineAngleCreateNode,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
yRelative: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'angledLineOfYLength',
|
|
|
|
createNode: ({ referenceSegName, varValB, tag }) => {
|
|
|
|
const [minVal, legAngle] = getMinAndSegAngVals(
|
|
|
|
referenceSegName,
|
|
|
|
varValB,
|
|
|
|
'legAngY'
|
|
|
|
)
|
|
|
|
return (args) =>
|
|
|
|
createCallWrapper(
|
|
|
|
'angledLineOfXLength',
|
|
|
|
[getLegAng(args[0], legAngle), minVal],
|
|
|
|
tag
|
|
|
|
)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
vertical: {
|
|
|
|
tooltip: 'yLine',
|
|
|
|
createNode:
|
|
|
|
({ varValB, tag }) =>
|
|
|
|
([arg0]) => {
|
|
|
|
const val =
|
|
|
|
arg0.type === 'Literal' && Number(arg0.value) < 0
|
|
|
|
? createUnaryExpression(varValB as BinaryPart)
|
|
|
|
: varValB
|
|
|
|
return createCallWrapper('yLine', val, tag)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
angledLineToX: {
|
|
|
|
free: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'angledLine',
|
2023-03-10 08:48:50 +11:00
|
|
|
createNode: basicAngledLineCreateNode('len'),
|
2023-03-02 21:19:11 +11:00
|
|
|
},
|
|
|
|
horizontal: {
|
|
|
|
tooltip: 'xLineTo',
|
|
|
|
createNode:
|
|
|
|
({ tag }) =>
|
|
|
|
(args) =>
|
|
|
|
createCallWrapper('xLineTo', args[0], tag),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
angle: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'angledLine',
|
|
|
|
createNode: angledLineAngleCreateNode,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
xAbsolute: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'angledLineToX',
|
|
|
|
createNode:
|
|
|
|
({ referenceSegName, varValB, tag }) =>
|
|
|
|
(args) => {
|
|
|
|
const angleToMatchLengthXCall = createCallExpression(
|
|
|
|
'angleToMatchLengthX',
|
|
|
|
[
|
|
|
|
createLiteral(referenceSegName),
|
|
|
|
varValB,
|
|
|
|
createPipeSubstitution(),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
return createCallWrapper(
|
|
|
|
'angledLineToX',
|
|
|
|
[getAngleLengthSign(args[0], angleToMatchLengthXCall), varValB],
|
|
|
|
tag
|
|
|
|
)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
horizontal: {
|
|
|
|
tooltip: 'xLineTo',
|
|
|
|
createNode:
|
|
|
|
({ varValB, tag }) =>
|
|
|
|
([arg0]) =>
|
|
|
|
createCallWrapper('xLineTo', varValB, tag),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
angledLineToY: {
|
|
|
|
free: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'angledLine',
|
2023-03-10 08:48:50 +11:00
|
|
|
createNode: basicAngledLineCreateNode('len'),
|
2023-03-02 21:19:11 +11:00
|
|
|
},
|
|
|
|
vertical: {
|
|
|
|
tooltip: 'yLineTo',
|
|
|
|
createNode:
|
|
|
|
({ tag }) =>
|
|
|
|
(args) =>
|
|
|
|
createCallWrapper('yLineTo', args[1], tag),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
angle: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'angledLine',
|
|
|
|
createNode: angledLineAngleCreateNode,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
yAbsolute: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'angledLineToY',
|
|
|
|
createNode:
|
|
|
|
({ referenceSegName, varValB, tag }) =>
|
|
|
|
(args) => {
|
|
|
|
const angleToMatchLengthXCall = createCallExpression(
|
|
|
|
'angleToMatchLengthY',
|
|
|
|
[
|
|
|
|
createLiteral(referenceSegName),
|
|
|
|
varValB,
|
|
|
|
createPipeSubstitution(),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
return createCallWrapper(
|
|
|
|
'angledLineToY',
|
|
|
|
[getAngleLengthSign(args[0], angleToMatchLengthXCall), varValB],
|
|
|
|
tag
|
|
|
|
)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
vertical: {
|
|
|
|
tooltip: 'yLineTo',
|
|
|
|
createNode:
|
|
|
|
({ varValB, tag }) =>
|
|
|
|
() =>
|
|
|
|
createCallWrapper('yLineTo', varValB, tag),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
xLine: {
|
|
|
|
free: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'xLine',
|
|
|
|
createNode:
|
|
|
|
({ referenceSegName, tag }) =>
|
2023-03-10 14:55:16 +11:00
|
|
|
(arg) => {
|
|
|
|
const argVal = getArgLiteralVal(arg[0])
|
|
|
|
const segLen = createSegLen(referenceSegName) as BinaryPart
|
|
|
|
const val = argVal > 0 ? segLen : createUnaryExpression(segLen)
|
|
|
|
return createCallWrapper('xLine', val, tag, argVal)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
setHorzDistance: {
|
|
|
|
tooltip: 'xLineTo',
|
2023-03-17 21:15:46 +11:00
|
|
|
createNode: setHorVertDistanceForXYLines('x'),
|
2023-03-10 14:55:16 +11:00
|
|
|
},
|
|
|
|
setLength: {
|
|
|
|
tooltip: 'xLine',
|
|
|
|
createNode: xyLineSetLength('xLine'),
|
2023-03-02 21:19:11 +11:00
|
|
|
},
|
2023-03-19 18:46:39 +11:00
|
|
|
intersect: {
|
|
|
|
tooltip: 'angledLineThatIntersects',
|
|
|
|
createNode: setAngledIntersectLineForLines,
|
|
|
|
},
|
2023-04-05 15:08:46 +10:00
|
|
|
xAbs: {
|
|
|
|
tooltip: 'xLineTo',
|
|
|
|
createNode: setAbsDistanceCreateNode('x', true),
|
|
|
|
},
|
2023-03-02 21:19:11 +11:00
|
|
|
},
|
|
|
|
},
|
|
|
|
yLine: {
|
|
|
|
free: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'yLine',
|
|
|
|
createNode:
|
|
|
|
({ referenceSegName, tag }) =>
|
2023-04-02 09:34:56 +10:00
|
|
|
(arg) => {
|
|
|
|
const argVal = getArgLiteralVal(arg[0])
|
|
|
|
let segLen = createSegLen(referenceSegName) as BinaryPart
|
|
|
|
if (argVal < 0) segLen = createUnaryExpression(segLen)
|
|
|
|
return createCallWrapper('yLine', segLen, tag, argVal)
|
|
|
|
},
|
2023-03-02 21:19:11 +11:00
|
|
|
},
|
2023-03-10 14:55:16 +11:00
|
|
|
setLength: {
|
|
|
|
tooltip: 'yLine',
|
|
|
|
createNode: xyLineSetLength('yLine'),
|
|
|
|
},
|
2023-03-17 21:15:46 +11:00
|
|
|
setVertDistance: {
|
|
|
|
tooltip: 'yLineTo',
|
|
|
|
createNode: setHorVertDistanceForXYLines('y'),
|
|
|
|
},
|
2023-03-19 18:46:39 +11:00
|
|
|
intersect: {
|
|
|
|
tooltip: 'angledLineThatIntersects',
|
|
|
|
createNode: setAngledIntersectLineForLines,
|
|
|
|
},
|
2023-04-05 15:08:46 +10:00
|
|
|
yAbs: {
|
|
|
|
tooltip: 'yLineTo',
|
|
|
|
createNode: setAbsDistanceCreateNode('y', true),
|
|
|
|
},
|
2023-03-02 21:19:11 +11:00
|
|
|
},
|
|
|
|
},
|
|
|
|
xLineTo: {
|
|
|
|
free: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'xLine',
|
|
|
|
createNode:
|
|
|
|
({ referenceSegName, tag }) =>
|
|
|
|
() =>
|
|
|
|
createCallWrapper('xLine', createSegLen(referenceSegName), tag),
|
|
|
|
},
|
2023-03-17 21:15:46 +11:00
|
|
|
setLength: {
|
|
|
|
tooltip: 'xLine',
|
|
|
|
createNode: xyLineSetLength('xLine'),
|
|
|
|
},
|
2023-03-02 21:19:11 +11:00
|
|
|
},
|
|
|
|
},
|
|
|
|
yLineTo: {
|
|
|
|
free: {
|
|
|
|
equalLength: {
|
|
|
|
tooltip: 'yLine',
|
|
|
|
createNode:
|
|
|
|
({ referenceSegName, tag }) =>
|
|
|
|
() =>
|
|
|
|
createCallWrapper('yLine', createSegLen(referenceSegName), tag),
|
|
|
|
},
|
2023-03-17 21:15:46 +11:00
|
|
|
setLength: {
|
|
|
|
tooltip: 'yLine',
|
|
|
|
createNode: xyLineSetLength('yLine'),
|
|
|
|
},
|
2023-03-02 21:19:11 +11:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2023-03-22 03:02:37 +11:00
|
|
|
export function getRemoveConstraintsTransform(
|
|
|
|
sketchFnExp: CallExpression,
|
|
|
|
constraintType: ConstraintType
|
|
|
|
): TransformInfo | false {
|
2023-09-15 11:48:23 -04:00
|
|
|
let name = sketchFnExp.callee.name as ToolTip
|
2023-03-22 03:02:37 +11:00
|
|
|
if (!toolTips.includes(name)) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
const xyLineMap: {
|
2023-09-15 11:48:23 -04:00
|
|
|
[key in ToolTip]?: ToolTip
|
2023-03-22 03:02:37 +11:00
|
|
|
} = {
|
|
|
|
xLine: 'line',
|
|
|
|
yLine: 'line',
|
|
|
|
xLineTo: 'lineTo',
|
|
|
|
yLineTo: 'lineTo',
|
|
|
|
}
|
|
|
|
|
|
|
|
const _name = xyLineMap[name]
|
|
|
|
if (_name) {
|
|
|
|
name = _name
|
|
|
|
}
|
|
|
|
|
|
|
|
const transformInfo: TransformInfo = {
|
|
|
|
tooltip: 'line',
|
|
|
|
// tooltip: name,
|
|
|
|
createNode:
|
|
|
|
({ tag, referenceSegName }) =>
|
|
|
|
(args) => {
|
|
|
|
return createCallWrapper('line', args, tag)
|
|
|
|
// The following commented changes values to hardcode, but keeps the line type the same, maybe that's useful?
|
|
|
|
|
|
|
|
// if (name === 'angledLineThatIntersects') {
|
|
|
|
// return intersectCallWrapper({
|
|
|
|
// fnName: name,
|
|
|
|
// angleVal: args[0],
|
|
|
|
// offsetVal: args[1],
|
|
|
|
// intersectTag: createLiteral(referenceSegName),
|
|
|
|
// tag,
|
|
|
|
// })
|
|
|
|
// }
|
|
|
|
// return createCallWrapper(name, args, tag)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if the function is locked down and so can't be transformed
|
|
|
|
const firstArg = getFirstArg(sketchFnExp)
|
2023-09-13 07:23:14 -07:00
|
|
|
if (isNotLiteralArrayOrStatic(firstArg.val)) {
|
|
|
|
return transformInfo
|
2023-03-22 03:02:37 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
// check if the function has no constraints
|
|
|
|
const isTwoValFree =
|
2023-09-13 07:23:14 -07:00
|
|
|
Array.isArray(firstArg.val) && isLiteralArrayOrStatic(firstArg.val)
|
2023-03-22 03:02:37 +11:00
|
|
|
if (isTwoValFree) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
const isOneValFree =
|
2023-09-13 07:23:14 -07:00
|
|
|
!Array.isArray(firstArg.val) && isLiteralArrayOrStatic(firstArg.val)
|
2023-03-22 03:02:37 +11:00
|
|
|
if (isOneValFree) {
|
|
|
|
return transformInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
// check what constraints the function has
|
|
|
|
const lineInputType = getConstraintType(firstArg.val, name)
|
|
|
|
if (lineInputType) {
|
|
|
|
return transformInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-04-01 16:47:00 +11:00
|
|
|
function getTransformMapPath(
|
2023-03-02 21:19:11 +11:00
|
|
|
sketchFnExp: CallExpression,
|
|
|
|
constraintType: ConstraintType
|
|
|
|
):
|
|
|
|
| {
|
2023-09-15 11:48:23 -04:00
|
|
|
toolTip: ToolTip
|
2023-03-02 21:19:11 +11:00
|
|
|
lineInputType: LineInputsType | 'free'
|
|
|
|
constraintType: ConstraintType
|
|
|
|
}
|
|
|
|
| false {
|
2023-09-15 11:48:23 -04:00
|
|
|
const name = sketchFnExp.callee.name as ToolTip
|
2023-03-02 21:19:11 +11:00
|
|
|
if (!toolTips.includes(name)) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if the function is locked down and so can't be transformed
|
|
|
|
const firstArg = getFirstArg(sketchFnExp)
|
2023-09-13 07:23:14 -07:00
|
|
|
if (isNotLiteralArrayOrStatic(firstArg.val)) {
|
|
|
|
return false
|
2023-03-02 21:19:11 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
// check if the function has no constraints
|
2023-09-13 07:23:14 -07:00
|
|
|
if (isLiteralArrayOrStatic(firstArg.val)) {
|
2023-03-02 21:19:11 +11:00
|
|
|
const info = transformMap?.[name]?.free?.[constraintType]
|
|
|
|
if (info)
|
|
|
|
return {
|
|
|
|
toolTip: name,
|
|
|
|
lineInputType: 'free',
|
|
|
|
constraintType,
|
|
|
|
}
|
|
|
|
// if (info) return info
|
|
|
|
}
|
|
|
|
|
|
|
|
// check what constraints the function has
|
|
|
|
const lineInputType = getConstraintType(firstArg.val, name)
|
|
|
|
if (lineInputType) {
|
|
|
|
const info = transformMap?.[name]?.[lineInputType]?.[constraintType]
|
|
|
|
if (info)
|
|
|
|
return {
|
|
|
|
toolTip: name,
|
|
|
|
lineInputType,
|
|
|
|
constraintType,
|
|
|
|
}
|
|
|
|
// if (info) return info
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getTransformInfo(
|
|
|
|
sketchFnExp: CallExpression,
|
|
|
|
constraintType: ConstraintType
|
|
|
|
): TransformInfo | false {
|
|
|
|
const path = getTransformMapPath(sketchFnExp, constraintType)
|
|
|
|
if (!path) return false
|
|
|
|
const { toolTip, lineInputType, constraintType: _constraintType } = path
|
|
|
|
const info = transformMap?.[toolTip]?.[lineInputType]?.[_constraintType]
|
|
|
|
if (!info) return false
|
|
|
|
return info
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getConstraintType(
|
2023-04-03 16:05:25 +10:00
|
|
|
val: Value | [Value, Value] | [Value, Value, Value],
|
2023-09-15 11:48:23 -04:00
|
|
|
fnName: ToolTip
|
2023-03-02 21:19:11 +11:00
|
|
|
): LineInputsType | null {
|
|
|
|
// this function assumes that for two val sketch functions that one arg is locked down not both
|
|
|
|
// and for one val sketch functions that the arg is NOT locked down
|
|
|
|
// these conditions should have been checked previously.
|
|
|
|
// completely locked down or not locked down at all does not depend on the fnName so we can check that first
|
|
|
|
const isArr = Array.isArray(val)
|
|
|
|
if (!isArr) {
|
|
|
|
if (fnName === 'xLine') return 'yRelative'
|
|
|
|
if (fnName === 'yLine') return 'xRelative'
|
|
|
|
if (fnName === 'xLineTo') return 'yAbsolute'
|
|
|
|
if (fnName === 'yLineTo') return 'xAbsolute'
|
|
|
|
} else {
|
2023-09-13 07:23:14 -07:00
|
|
|
const isFirstArgLockedDown = isNotLiteralArrayOrStatic(val[0])
|
2023-03-02 21:19:11 +11:00
|
|
|
if (fnName === 'line')
|
|
|
|
return isFirstArgLockedDown ? 'xRelative' : 'yRelative'
|
|
|
|
if (fnName === 'lineTo')
|
|
|
|
return isFirstArgLockedDown ? 'xAbsolute' : 'yAbsolute'
|
|
|
|
if (fnName === 'angledLine')
|
|
|
|
return isFirstArgLockedDown ? 'angle' : 'length'
|
|
|
|
if (fnName === 'angledLineOfXLength')
|
|
|
|
return isFirstArgLockedDown ? 'angle' : 'xRelative'
|
|
|
|
if (fnName === 'angledLineToX')
|
|
|
|
return isFirstArgLockedDown ? 'angle' : 'xAbsolute'
|
|
|
|
if (fnName === 'angledLineOfYLength')
|
|
|
|
return isFirstArgLockedDown ? 'angle' : 'yRelative'
|
|
|
|
if (fnName === 'angledLineToY')
|
|
|
|
return isFirstArgLockedDown ? 'angle' : 'yAbsolute'
|
|
|
|
}
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getTransformInfos(
|
2023-04-03 16:05:25 +10:00
|
|
|
selectionRanges: Selections,
|
2023-03-02 21:19:11 +11:00
|
|
|
ast: Program,
|
|
|
|
constraintType: ConstraintType
|
|
|
|
): TransformInfo[] {
|
2023-04-03 16:05:25 +10:00
|
|
|
const paths = selectionRanges.codeBasedSelections.map(({ range }) =>
|
|
|
|
getNodePathFromSourceRange(ast, range)
|
2023-03-02 21:19:11 +11:00
|
|
|
)
|
|
|
|
const nodes = paths.map(
|
2023-04-01 16:47:00 +11:00
|
|
|
(pathToNode) =>
|
|
|
|
getNodeFromPath<Value>(ast, pathToNode, 'CallExpression').node
|
2023-03-02 21:19:11 +11:00
|
|
|
)
|
|
|
|
|
2023-04-02 17:20:11 +10:00
|
|
|
try {
|
|
|
|
const theTransforms = nodes.map((node) => {
|
|
|
|
if (node?.type === 'CallExpression')
|
|
|
|
return getTransformInfo(node, constraintType)
|
2023-03-02 21:19:11 +11:00
|
|
|
|
2023-04-02 17:20:11 +10:00
|
|
|
return false
|
|
|
|
}) as TransformInfo[]
|
|
|
|
return theTransforms
|
|
|
|
} catch (error) {
|
2023-09-25 17:28:03 +10:00
|
|
|
console.log('error', error)
|
2023-04-02 17:20:11 +10:00
|
|
|
return []
|
|
|
|
}
|
2023-03-02 21:19:11 +11:00
|
|
|
}
|
|
|
|
|
2023-03-22 03:02:37 +11:00
|
|
|
export function getRemoveConstraintsTransforms(
|
2023-04-03 16:05:25 +10:00
|
|
|
selectionRanges: Selections,
|
2023-03-22 03:02:37 +11:00
|
|
|
ast: Program,
|
|
|
|
constraintType: ConstraintType
|
|
|
|
): TransformInfo[] {
|
|
|
|
// return ()
|
2023-04-03 16:05:25 +10:00
|
|
|
const paths = selectionRanges.codeBasedSelections.map((selectionRange) =>
|
|
|
|
getNodePathFromSourceRange(ast, selectionRange.range)
|
2023-03-22 03:02:37 +11:00
|
|
|
)
|
|
|
|
const nodes = paths.map(
|
|
|
|
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
|
|
|
|
)
|
|
|
|
|
|
|
|
const theTransforms = nodes.map((node) => {
|
|
|
|
if (node?.type === 'CallExpression')
|
|
|
|
return getRemoveConstraintsTransform(node, constraintType)
|
|
|
|
|
|
|
|
return false
|
|
|
|
}) as TransformInfo[]
|
|
|
|
return theTransforms
|
|
|
|
}
|
|
|
|
|
2023-10-11 13:36:54 +11:00
|
|
|
export type PathToNodeMap = { [key: number]: PathToNode }
|
2023-04-14 07:49:36 +10:00
|
|
|
|
2023-03-07 15:45:59 +11:00
|
|
|
export function transformSecondarySketchLinesTagFirst({
|
2023-03-02 21:19:11 +11:00
|
|
|
ast,
|
|
|
|
selectionRanges,
|
|
|
|
transformInfos,
|
|
|
|
programMemory,
|
2023-03-07 15:45:59 +11:00
|
|
|
forceSegName,
|
|
|
|
forceValueUsedInTransform,
|
2023-03-02 21:19:11 +11:00
|
|
|
}: {
|
|
|
|
ast: Program
|
2023-04-03 16:05:25 +10:00
|
|
|
selectionRanges: Selections
|
2023-03-02 21:19:11 +11:00
|
|
|
transformInfos: TransformInfo[]
|
|
|
|
programMemory: ProgramMemory
|
2023-03-07 15:45:59 +11:00
|
|
|
forceSegName?: string
|
|
|
|
forceValueUsedInTransform?: Value
|
|
|
|
}): {
|
|
|
|
modifiedAst: Program
|
|
|
|
valueUsedInTransform?: number
|
2023-04-14 07:49:36 +10:00
|
|
|
pathToNodeMap: PathToNodeMap
|
2023-03-07 15:45:59 +11:00
|
|
|
tagInfo: {
|
|
|
|
tag: string
|
|
|
|
isTagExisting: boolean
|
|
|
|
}
|
|
|
|
} {
|
|
|
|
// let node = JSON.parse(JSON.stringify(ast))
|
2023-04-03 16:05:25 +10:00
|
|
|
const primarySelection = selectionRanges.codeBasedSelections[0].range
|
2023-03-02 21:19:11 +11:00
|
|
|
|
2023-04-14 07:49:36 +10:00
|
|
|
const { modifiedAst, tag, isTagExisting, pathToNode } = giveSketchFnCallTag(
|
2023-03-07 15:45:59 +11:00
|
|
|
ast,
|
|
|
|
primarySelection,
|
|
|
|
forceSegName
|
|
|
|
)
|
2023-03-02 21:19:11 +11:00
|
|
|
|
2023-04-14 07:49:36 +10:00
|
|
|
const result = transformAstSketchLines({
|
|
|
|
ast: modifiedAst,
|
|
|
|
selectionRanges: {
|
|
|
|
...selectionRanges,
|
|
|
|
codeBasedSelections: selectionRanges.codeBasedSelections.slice(1),
|
|
|
|
},
|
|
|
|
referencedSegmentRange: primarySelection,
|
|
|
|
transformInfos,
|
|
|
|
programMemory,
|
|
|
|
referenceSegName: tag,
|
|
|
|
forceValueUsedInTransform,
|
|
|
|
})
|
|
|
|
const updatedPathToNodeMap = incrementPathToNodeMap(result.pathToNodeMap)
|
|
|
|
updatedPathToNodeMap[0] = pathToNode
|
|
|
|
|
2023-03-07 15:45:59 +11:00
|
|
|
return {
|
2023-04-14 07:49:36 +10:00
|
|
|
...result,
|
|
|
|
pathToNodeMap: updatedPathToNodeMap,
|
2023-03-07 15:45:59 +11:00
|
|
|
tagInfo: {
|
|
|
|
tag,
|
|
|
|
isTagExisting,
|
|
|
|
},
|
|
|
|
}
|
2023-03-02 21:19:11 +11:00
|
|
|
}
|
|
|
|
|
2023-04-14 07:49:36 +10:00
|
|
|
function incrementPathToNodeMap(
|
|
|
|
pathToNodeMap: PathToNodeMap,
|
|
|
|
increment = 1
|
|
|
|
): PathToNodeMap {
|
|
|
|
const newMap: PathToNodeMap = {}
|
|
|
|
Object.entries(pathToNodeMap).forEach(([key, path]) => {
|
|
|
|
newMap[Number(key) + increment] = path
|
|
|
|
})
|
|
|
|
return newMap
|
|
|
|
}
|
|
|
|
|
2023-03-07 15:45:59 +11:00
|
|
|
export function transformAstSketchLines({
|
2023-03-02 21:19:11 +11:00
|
|
|
ast,
|
|
|
|
selectionRanges,
|
|
|
|
transformInfos,
|
|
|
|
programMemory,
|
2023-03-07 15:45:59 +11:00
|
|
|
referenceSegName,
|
|
|
|
forceValueUsedInTransform,
|
2023-03-17 21:15:46 +11:00
|
|
|
referencedSegmentRange,
|
2023-03-02 21:19:11 +11:00
|
|
|
}: {
|
|
|
|
ast: Program
|
2023-04-03 16:05:25 +10:00
|
|
|
selectionRanges: Selections
|
2023-03-02 21:19:11 +11:00
|
|
|
transformInfos: TransformInfo[]
|
|
|
|
programMemory: ProgramMemory
|
2023-03-07 15:45:59 +11:00
|
|
|
referenceSegName: string
|
|
|
|
forceValueUsedInTransform?: Value
|
2023-04-03 16:05:25 +10:00
|
|
|
referencedSegmentRange?: Selection['range']
|
2023-04-14 07:49:36 +10:00
|
|
|
}): {
|
|
|
|
modifiedAst: Program
|
|
|
|
valueUsedInTransform?: number
|
|
|
|
pathToNodeMap: PathToNodeMap
|
|
|
|
} {
|
2023-03-02 21:19:11 +11:00
|
|
|
// deep clone since we are mutating in a loop, of which any could fail
|
|
|
|
let node = JSON.parse(JSON.stringify(ast))
|
2023-03-07 15:45:59 +11:00
|
|
|
let _valueUsedInTransform // TODO should this be an array?
|
2023-04-14 07:49:36 +10:00
|
|
|
const pathToNodeMap: PathToNodeMap = {}
|
2023-03-02 21:19:11 +11:00
|
|
|
|
2023-04-03 16:05:25 +10:00
|
|
|
selectionRanges.codeBasedSelections.forEach(({ range }, index) => {
|
2023-03-07 15:45:59 +11:00
|
|
|
const callBack = transformInfos?.[index].createNode
|
2023-03-02 21:19:11 +11:00
|
|
|
const transformTo = transformInfos?.[index].tooltip
|
|
|
|
if (!callBack || !transformTo) throw new Error('no callback helper')
|
|
|
|
|
2023-03-07 15:45:59 +11:00
|
|
|
const getNode = getNodeFromPathCurry(
|
2023-03-02 21:19:11 +11:00
|
|
|
node,
|
2023-03-07 15:45:59 +11:00
|
|
|
getNodePathFromSourceRange(node, range)
|
|
|
|
)
|
|
|
|
|
|
|
|
const callExp = getNode<CallExpression>('CallExpression')?.node
|
|
|
|
const varDec = getNode<VariableDeclarator>('VariableDeclarator').node
|
|
|
|
|
2024-03-15 17:03:42 -04:00
|
|
|
const { val } = getFirstArg(callExp)
|
|
|
|
const callBackTag = callExp.arguments[2]
|
2023-03-22 03:02:37 +11:00
|
|
|
const _referencedSegmentNameVal =
|
|
|
|
callExp.arguments[0]?.type === 'ObjectExpression' &&
|
|
|
|
callExp.arguments[0].properties?.find(
|
|
|
|
(prop) => prop.key.name === 'intersectTag'
|
|
|
|
)?.value
|
|
|
|
const _referencedSegmentName =
|
|
|
|
referenceSegName ||
|
|
|
|
(_referencedSegmentNameVal &&
|
|
|
|
_referencedSegmentNameVal.type === 'Literal' &&
|
|
|
|
String(_referencedSegmentNameVal.value)) ||
|
|
|
|
''
|
2023-03-02 21:19:11 +11:00
|
|
|
const [varValA, varValB] = Array.isArray(val) ? val : [val, val]
|
|
|
|
|
2023-03-07 15:45:59 +11:00
|
|
|
const varName = varDec.id.name
|
|
|
|
const sketchGroup = programMemory.root?.[varName]
|
2023-09-12 18:10:27 -07:00
|
|
|
if (!sketchGroup || sketchGroup.type !== 'SketchGroup')
|
2023-03-07 15:45:59 +11:00
|
|
|
throw new Error('not a sketch group')
|
2023-04-08 14:16:49 +10:00
|
|
|
const seg = getSketchSegmentFromSourceRange(sketchGroup, range).segment
|
2023-03-17 21:15:46 +11:00
|
|
|
const referencedSegment = referencedSegmentRange
|
2023-03-20 07:09:19 +11:00
|
|
|
? getSketchSegmentFromSourceRange(sketchGroup, referencedSegmentRange)
|
2023-04-08 14:16:49 +10:00
|
|
|
.segment
|
2023-03-22 03:02:37 +11:00
|
|
|
: sketchGroup.value.find((path) => path.name === _referencedSegmentName)
|
2023-03-07 15:45:59 +11:00
|
|
|
const { to, from } = seg
|
2023-04-14 07:49:36 +10:00
|
|
|
const { modifiedAst, valueUsedInTransform, pathToNode } = replaceSketchLine(
|
|
|
|
{
|
|
|
|
node: node,
|
|
|
|
programMemory,
|
|
|
|
sourceRange: range,
|
|
|
|
referencedSegment,
|
2023-09-15 11:48:23 -04:00
|
|
|
fnName: transformTo || (callExp.callee.name as ToolTip),
|
2023-04-14 07:49:36 +10:00
|
|
|
to,
|
|
|
|
from,
|
|
|
|
createCallback: callBack({
|
|
|
|
referenceSegName: _referencedSegmentName,
|
|
|
|
varValA,
|
|
|
|
varValB,
|
|
|
|
tag: callBackTag,
|
|
|
|
forceValueUsedInTransform,
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
)
|
2023-03-07 15:45:59 +11:00
|
|
|
|
2023-03-02 21:19:11 +11:00
|
|
|
node = modifiedAst
|
2023-04-14 07:49:36 +10:00
|
|
|
pathToNodeMap[index] = pathToNode
|
2023-03-07 15:45:59 +11:00
|
|
|
if (typeof valueUsedInTransform === 'number') {
|
|
|
|
_valueUsedInTransform = valueUsedInTransform
|
|
|
|
}
|
2023-03-02 21:19:11 +11:00
|
|
|
})
|
2023-04-14 07:49:36 +10:00
|
|
|
return {
|
|
|
|
modifiedAst: node,
|
|
|
|
valueUsedInTransform: _valueUsedInTransform,
|
|
|
|
pathToNodeMap,
|
|
|
|
}
|
2023-03-02 21:19:11 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
function createSegLen(referenceSegName: string): Value {
|
|
|
|
return createCallExpression('segLen', [
|
|
|
|
createLiteral(referenceSegName),
|
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
|
|
|
}
|
2023-03-05 07:34:56 +11:00
|
|
|
|
2023-03-10 08:48:50 +11:00
|
|
|
function createSegAngle(referenceSegName: string): Value {
|
2023-03-17 21:15:46 +11:00
|
|
|
return createCallExpression('segAng', [
|
2023-03-10 08:48:50 +11:00
|
|
|
createLiteral(referenceSegName),
|
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
|
|
|
}
|
|
|
|
|
2023-03-05 07:34:56 +11:00
|
|
|
function createSegEnd(referenceSegName: string, isX: boolean): CallExpression {
|
|
|
|
return createCallExpression(isX ? 'segEndX' : 'segEndY', [
|
|
|
|
createLiteral(referenceSegName),
|
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
|
|
|
}
|
|
|
|
|
|
|
|
function createLastSeg(isX: boolean): CallExpression {
|
|
|
|
return createCallExpression(isX ? 'lastSegX' : 'lastSegY', [
|
|
|
|
createPipeSubstitution(),
|
|
|
|
])
|
|
|
|
}
|
|
|
|
|
|
|
|
function getArgLiteralVal(arg: Value): number {
|
|
|
|
return arg?.type === 'Literal' ? Number(arg.value) : 0
|
|
|
|
}
|
2023-03-19 21:25:22 +11:00
|
|
|
|
|
|
|
export function getConstraintLevelFromSourceRange(
|
2023-04-03 16:05:25 +10:00
|
|
|
cursorRange: Selection['range'],
|
2023-03-19 21:25:22 +11:00
|
|
|
ast: Program
|
2024-02-11 12:59:00 +11:00
|
|
|
): { range: [number, number]; level: 'free' | 'partial' | 'full' } {
|
2023-03-19 21:25:22 +11:00
|
|
|
const { node: sketchFnExp } = getNodeFromPath<CallExpression>(
|
|
|
|
ast,
|
2023-04-01 16:47:00 +11:00
|
|
|
getNodePathFromSourceRange(ast, cursorRange),
|
|
|
|
'CallExpression'
|
2023-03-19 21:25:22 +11:00
|
|
|
)
|
2023-09-15 11:48:23 -04:00
|
|
|
const name = sketchFnExp?.callee?.name as ToolTip
|
2024-02-11 12:59:00 +11:00
|
|
|
const range: [number, number] = [sketchFnExp.start, sketchFnExp.end]
|
|
|
|
if (!toolTips.includes(name)) return { level: 'free', range: range }
|
2023-03-19 21:25:22 +11:00
|
|
|
|
|
|
|
const firstArg = getFirstArg(sketchFnExp)
|
|
|
|
|
|
|
|
// check if the function is fully constrained
|
2023-09-13 07:23:14 -07:00
|
|
|
if (isNotLiteralArrayOrStatic(firstArg.val)) {
|
2024-02-11 12:59:00 +11:00
|
|
|
return { level: 'full', range: range }
|
2023-03-19 21:25:22 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
// check if the function has no constraints
|
|
|
|
const isTwoValFree =
|
2023-09-13 07:23:14 -07:00
|
|
|
Array.isArray(firstArg.val) && isLiteralArrayOrStatic(firstArg.val)
|
2023-03-19 21:25:22 +11:00
|
|
|
const isOneValFree =
|
2023-09-13 07:23:14 -07:00
|
|
|
!Array.isArray(firstArg.val) && isLiteralArrayOrStatic(firstArg.val)
|
2023-03-19 21:25:22 +11:00
|
|
|
|
2024-02-11 12:59:00 +11:00
|
|
|
if (isTwoValFree) return { level: 'free', range: range }
|
|
|
|
if (isOneValFree) return { level: 'partial', range: range }
|
2023-03-19 21:25:22 +11:00
|
|
|
|
2024-02-11 12:59:00 +11:00
|
|
|
return { level: 'partial', range: range }
|
2023-03-19 21:25:22 +11:00
|
|
|
}
|
2023-09-13 07:23:14 -07:00
|
|
|
|
|
|
|
export function isLiteralArrayOrStatic(
|
|
|
|
val: Value | [Value, Value] | [Value, Value, Value] | undefined
|
|
|
|
): boolean {
|
|
|
|
if (!val) return false
|
|
|
|
|
|
|
|
if (Array.isArray(val)) {
|
Remove just one enum (#1096)
# Problem
This is my proposal for fixing #1107 . I've only done it for one stdlib function, `tangentialArcTo` -- if y'all like it, I'll apply this idea to the rest of the stdlib.
Previously, if users want to put a tag on the arc, the function's parameters change type.
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is object
tangentialArcTo({to: [x, y], tag: "myTag"}, %)
```
# Solution
My proposal in #1006 is that KCL should have optional values. This means we can change the stdlib `tangentialArcTo` function to use them. In this PR, the calls are now like
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is array still, but we now pass a tag at the end.
tangentialArcTo([x, y], %, "myTag")
```
This adds an "option" type to KCL typesystem, but it's not really revealed to users (no KCL types are revealed to users right now, they write untyped code and only interact with types when they get type errors upon executing programs). Also adds a None type, which is the default case of the Optional enum.
2023-12-18 23:49:32 -06:00
|
|
|
const a = val[0]
|
|
|
|
const b = val[1]
|
2023-09-13 07:23:14 -07:00
|
|
|
return isLiteralArrayOrStatic(a) && isLiteralArrayOrStatic(b)
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
val.type === 'Literal' ||
|
|
|
|
(val.type === 'UnaryExpression' && val.argument.type === 'Literal')
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export function isNotLiteralArrayOrStatic(
|
|
|
|
val: Value | [Value, Value] | [Value, Value, Value]
|
|
|
|
): boolean {
|
|
|
|
if (Array.isArray(val)) {
|
Remove just one enum (#1096)
# Problem
This is my proposal for fixing #1107 . I've only done it for one stdlib function, `tangentialArcTo` -- if y'all like it, I'll apply this idea to the rest of the stdlib.
Previously, if users want to put a tag on the arc, the function's parameters change type.
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is object
tangentialArcTo({to: [x, y], tag: "myTag"}, %)
```
# Solution
My proposal in #1006 is that KCL should have optional values. This means we can change the stdlib `tangentialArcTo` function to use them. In this PR, the calls are now like
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is array still, but we now pass a tag at the end.
tangentialArcTo([x, y], %, "myTag")
```
This adds an "option" type to KCL typesystem, but it's not really revealed to users (no KCL types are revealed to users right now, they write untyped code and only interact with types when they get type errors upon executing programs). Also adds a None type, which is the default case of the Optional enum.
2023-12-18 23:49:32 -06:00
|
|
|
const a = val[0]
|
|
|
|
const b = val[1]
|
2023-09-13 07:23:14 -07:00
|
|
|
return isNotLiteralArrayOrStatic(a) && isNotLiteralArrayOrStatic(b)
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
(val.type !== 'Literal' && val.type !== 'UnaryExpression') ||
|
|
|
|
(val.type === 'UnaryExpression' && val.argument.type !== 'Literal')
|
|
|
|
)
|
|
|
|
}
|