Add equal-length constraints & implement UnaryExpressions (#35)
* add segLen help to lang std * adding helpers functions to sketchConstraints * update tokeniser tests because they were annoying me not being 100% * compare async lexer with sync lexer instead * add helper functions * remove unneeded nesting * update add ast modifier function for angledLine * initial equal ast modification It adds a tag to the primary line, and converts any secondary lines to angledLine, but doesn't reference the taged/primary line yet * Update fn call with refernce to previous line using segLen * add test for giveSketchFnCallTag * fix excutor bug, executing call expression in array expression * fix small issue in executor * add CallExpressions to BinaryExpressions * add unary Expressions * tweaks to unaryExpression logic * add recasting for unaryExpressions and CallExpressions in BinaryExpressions * ensure pipe substitution info is passed down to unary expressions and others * allow binary expressions in function argumentns * inital setup, new way of organising sketch fn transforms Starting with equal length * overhaul equalLength button * add equal length support for angledLine * line with one variable supports signed legLength * fix indentation when recasting long arrayExpressions in a pipeExpression * improve modifyAst consision * further modify ast tidy * equalLength transfroms far angledLineOfXLength * add transforms for line-yRelative * add equal constraint for angledLineOfYLength * quick test fix * add equal length constrain transforms for lineTo * add equal length constraints for angledLineToX * add equalLength constraints for angledLineToY * test tidy * setup new vertical-horizontal constraints * Add equal Length constraints for vertical/horizontal lines * migrate old tests, and refactor callback tag * tweaks and refactor horzVert component * fix leg len with small negative leg length
This commit is contained in:
851
src/lang/std/sketchcombos.ts
Normal file
851
src/lang/std/sketchcombos.ts
Normal file
@ -0,0 +1,851 @@
|
||||
import { TransformCallback } from './stdTypes'
|
||||
import { Range, Ranges, toolTips, TooTip } from '../../useStore'
|
||||
import {
|
||||
BinaryPart,
|
||||
CallExpression,
|
||||
getNodeFromPath,
|
||||
getNodeFromPathCurry,
|
||||
getNodePathFromSourceRange,
|
||||
Program,
|
||||
Value,
|
||||
VariableDeclarator,
|
||||
} from '../abstractSyntaxTree'
|
||||
import {
|
||||
createBinaryExpression,
|
||||
createCallExpression,
|
||||
createIdentifier,
|
||||
createLiteral,
|
||||
createPipeSubstitution,
|
||||
createUnaryExpression,
|
||||
giveSketchFnCallTag,
|
||||
} from '../modifyAst'
|
||||
import { createFirstArg, getFirstArg, replaceSketchLine } from './sketch'
|
||||
import { ProgramMemory } from '../executor'
|
||||
import { getSketchSegmentIndexFromSourceRange } from './sketchConstraints'
|
||||
|
||||
type LineInputsType =
|
||||
| 'xAbsolute'
|
||||
| 'yAbsolute'
|
||||
| 'xRelative'
|
||||
| 'yRelative'
|
||||
| 'angle'
|
||||
| 'length'
|
||||
|
||||
export type ConstraintType =
|
||||
| 'equalLength'
|
||||
| 'vertical'
|
||||
| 'horizontal'
|
||||
| 'equalangle'
|
||||
|
||||
function createCallWrapper(
|
||||
a: TooTip,
|
||||
val: [Value, Value] | Value,
|
||||
tag?: Value
|
||||
) {
|
||||
return createCallExpression(a, [
|
||||
createFirstArg(a, val, tag),
|
||||
createPipeSubstitution(),
|
||||
])
|
||||
}
|
||||
|
||||
export function replaceSketchCall(
|
||||
programMemory: ProgramMemory,
|
||||
ast: Program,
|
||||
range: Range,
|
||||
transformTo: TooTip,
|
||||
createCallback: TransformCallback
|
||||
): { modifiedAst: Program } {
|
||||
const path = getNodePathFromSourceRange(ast, range)
|
||||
const getNode = getNodeFromPathCurry(ast, path)
|
||||
const varDec = getNode<VariableDeclarator>('VariableDeclarator').node
|
||||
const callExp = getNode<CallExpression>('CallExpression').node
|
||||
const varName = varDec.id.name
|
||||
const sketchGroup = programMemory.root?.[varName]
|
||||
if (!sketchGroup || sketchGroup.type !== 'sketchGroup')
|
||||
throw new Error('not a sketch group')
|
||||
const seg = getSketchSegmentIndexFromSourceRange(sketchGroup, range)
|
||||
const { to, from } = seg
|
||||
const { modifiedAst } = replaceSketchLine({
|
||||
node: ast,
|
||||
programMemory,
|
||||
sourceRange: range,
|
||||
fnName: transformTo || (callExp.callee.name as TooTip),
|
||||
to,
|
||||
from,
|
||||
createCallback,
|
||||
})
|
||||
return { modifiedAst }
|
||||
}
|
||||
|
||||
export type TransformInfo = {
|
||||
tooltip: TooTip
|
||||
createNode: (a: {
|
||||
varValA: Value // x / angle
|
||||
varValB: Value // y / length or x y for angledLineOfXlength etc
|
||||
referenceSegName: string
|
||||
tag?: Value
|
||||
}) => (args: [Value, Value]) => Value
|
||||
}
|
||||
|
||||
type TransformMap = {
|
||||
[key in TooTip]: {
|
||||
[key in LineInputsType | 'free']?: {
|
||||
[key in ConstraintType]?: TransformInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const basicAngledLineCreateNode: TransformInfo['createNode'] =
|
||||
({ referenceSegName, tag }) =>
|
||||
(args) =>
|
||||
createCallWrapper(
|
||||
'angledLine',
|
||||
[args[0], createSegLen(referenceSegName)],
|
||||
tag
|
||||
)
|
||||
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
|
||||
const binExp = createBinaryExpression([
|
||||
createLiteral(truncatedTo90),
|
||||
'+',
|
||||
legAngleVal,
|
||||
])
|
||||
return truncatedTo90 == 0 ? legAngleVal : binExp
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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),
|
||||
},
|
||||
},
|
||||
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),
|
||||
},
|
||||
},
|
||||
free: {
|
||||
equalLength: {
|
||||
tooltip: 'angledLine',
|
||||
createNode: basicAngledLineCreateNode,
|
||||
},
|
||||
horizontal: {
|
||||
tooltip: 'xLine',
|
||||
createNode:
|
||||
({ tag }) =>
|
||||
(args) =>
|
||||
createCallWrapper('xLine', args[0], tag),
|
||||
},
|
||||
vertical: {
|
||||
tooltip: 'yLine',
|
||||
createNode:
|
||||
({ tag }) =>
|
||||
(args) =>
|
||||
createCallWrapper('yLine', args[1], tag),
|
||||
},
|
||||
},
|
||||
},
|
||||
lineTo: {
|
||||
free: {
|
||||
equalLength: {
|
||||
tooltip: 'angledLine',
|
||||
createNode: basicAngledLineCreateNode,
|
||||
},
|
||||
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),
|
||||
},
|
||||
},
|
||||
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),
|
||||
},
|
||||
},
|
||||
},
|
||||
angledLine: {
|
||||
angle: {
|
||||
equalLength: {
|
||||
tooltip: 'angledLine',
|
||||
createNode:
|
||||
({ referenceSegName, varValA, tag }) =>
|
||||
() =>
|
||||
createCallWrapper(
|
||||
'angledLine',
|
||||
[varValA, createSegLen(referenceSegName)],
|
||||
tag
|
||||
),
|
||||
},
|
||||
},
|
||||
free: {
|
||||
equalLength: {
|
||||
tooltip: 'angledLine',
|
||||
createNode: basicAngledLineCreateNode,
|
||||
},
|
||||
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)
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
angledLineOfXLength: {
|
||||
free: {
|
||||
equalLength: {
|
||||
tooltip: 'angledLine',
|
||||
createNode: basicAngledLineCreateNode,
|
||||
},
|
||||
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',
|
||||
createNode: basicAngledLineCreateNode,
|
||||
},
|
||||
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',
|
||||
createNode: basicAngledLineCreateNode,
|
||||
},
|
||||
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',
|
||||
createNode: basicAngledLineCreateNode,
|
||||
},
|
||||
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 }) =>
|
||||
() =>
|
||||
createCallWrapper('xLine', createSegLen(referenceSegName), tag),
|
||||
},
|
||||
},
|
||||
},
|
||||
yLine: {
|
||||
free: {
|
||||
equalLength: {
|
||||
tooltip: 'yLine',
|
||||
createNode:
|
||||
({ referenceSegName, tag }) =>
|
||||
() =>
|
||||
createCallWrapper('yLine', createSegLen(referenceSegName), tag),
|
||||
},
|
||||
},
|
||||
},
|
||||
xLineTo: {
|
||||
free: {
|
||||
equalLength: {
|
||||
tooltip: 'xLine',
|
||||
createNode:
|
||||
({ referenceSegName, tag }) =>
|
||||
() =>
|
||||
createCallWrapper('xLine', createSegLen(referenceSegName), tag),
|
||||
},
|
||||
},
|
||||
},
|
||||
yLineTo: {
|
||||
free: {
|
||||
equalLength: {
|
||||
tooltip: 'yLine',
|
||||
createNode:
|
||||
({ referenceSegName, tag }) =>
|
||||
() =>
|
||||
createCallWrapper('yLine', createSegLen(referenceSegName), tag),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export function getTransformMapPath(
|
||||
sketchFnExp: CallExpression,
|
||||
constraintType: ConstraintType
|
||||
):
|
||||
| {
|
||||
toolTip: TooTip
|
||||
lineInputType: LineInputsType | 'free'
|
||||
constraintType: ConstraintType
|
||||
}
|
||||
| false {
|
||||
const name = sketchFnExp.callee.name as TooTip
|
||||
if (!toolTips.includes(name)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// check if the function is locked down and so can't be transformed
|
||||
const firstArg = getFirstArg(sketchFnExp)
|
||||
if (Array.isArray(firstArg.val)) {
|
||||
const [a, b] = firstArg.val
|
||||
if (a?.type !== 'Literal' && b?.type !== 'Literal') {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if (firstArg.val?.type !== 'Literal') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// check if the function has no constraints
|
||||
const isTwoValFree =
|
||||
Array.isArray(firstArg.val) &&
|
||||
firstArg.val?.[0]?.type === 'Literal' &&
|
||||
firstArg.val?.[1]?.type === 'Literal'
|
||||
const isOneValFree =
|
||||
!Array.isArray(firstArg.val) && firstArg.val?.type === 'Literal'
|
||||
if (isTwoValFree || isOneValFree) {
|
||||
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(
|
||||
val: Value | [Value, Value],
|
||||
fnName: TooTip
|
||||
): 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 {
|
||||
const isFirstArgLockedDown = val?.[0]?.type !== 'Literal'
|
||||
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(
|
||||
selectionRanges: Ranges,
|
||||
ast: Program,
|
||||
constraintType: ConstraintType
|
||||
): TransformInfo[] {
|
||||
const paths = selectionRanges.map((selectionRange) =>
|
||||
getNodePathFromSourceRange(ast, selectionRange)
|
||||
)
|
||||
const nodes = paths.map(
|
||||
(pathToNode) => getNodeFromPath<Value>(ast, pathToNode).node
|
||||
)
|
||||
|
||||
const theTransforms = nodes.map((node) => {
|
||||
if (node?.type === 'CallExpression')
|
||||
return getTransformInfo(node, constraintType)
|
||||
|
||||
return false
|
||||
}) as TransformInfo[]
|
||||
return theTransforms
|
||||
}
|
||||
|
||||
export function transformAstForSketchLines({
|
||||
ast,
|
||||
selectionRanges,
|
||||
transformInfos,
|
||||
programMemory,
|
||||
}: {
|
||||
ast: Program
|
||||
selectionRanges: Ranges
|
||||
transformInfos: TransformInfo[]
|
||||
programMemory: ProgramMemory
|
||||
}): { modifiedAst: Program } {
|
||||
// deep clone since we are mutating in a loop, of which any could fail
|
||||
let node = JSON.parse(JSON.stringify(ast))
|
||||
const primarySelection = selectionRanges[0]
|
||||
|
||||
const { modifiedAst, tag } = giveSketchFnCallTag(node, primarySelection)
|
||||
node = modifiedAst
|
||||
|
||||
selectionRanges.slice(1).forEach((range, index) => {
|
||||
const callBack = transformInfos?.[index].createNode
|
||||
const transformTo = transformInfos?.[index].tooltip
|
||||
if (!callBack || !transformTo) throw new Error('no callback helper')
|
||||
|
||||
const callExpPath = getNodePathFromSourceRange(node, range)
|
||||
const callExp = getNodeFromPath<CallExpression>(
|
||||
node,
|
||||
callExpPath,
|
||||
'CallExpression'
|
||||
)?.node
|
||||
const { val } = getFirstArg(callExp)
|
||||
const [varValA, varValB] = Array.isArray(val) ? val : [val, val]
|
||||
|
||||
const { modifiedAst } = replaceSketchCall(
|
||||
programMemory,
|
||||
node,
|
||||
range,
|
||||
transformTo,
|
||||
callBack({
|
||||
referenceSegName: tag,
|
||||
varValA,
|
||||
varValB,
|
||||
})
|
||||
)
|
||||
node = modifiedAst
|
||||
})
|
||||
return { modifiedAst: node }
|
||||
}
|
||||
|
||||
export function transformAstForHorzVert({
|
||||
ast,
|
||||
selectionRanges,
|
||||
transformInfos,
|
||||
programMemory,
|
||||
}: {
|
||||
ast: Program
|
||||
selectionRanges: Ranges
|
||||
transformInfos: TransformInfo[]
|
||||
programMemory: ProgramMemory
|
||||
}): { modifiedAst: Program } {
|
||||
// deep clone since we are mutating in a loop, of which any could fail
|
||||
let node = JSON.parse(JSON.stringify(ast))
|
||||
|
||||
selectionRanges.forEach((range, index) => {
|
||||
const callBack = transformInfos?.[index]?.createNode
|
||||
const transformTo = transformInfos?.[index].tooltip
|
||||
if (!callBack || !transformTo) throw new Error('no callback helper')
|
||||
|
||||
const callExpPath = getNodePathFromSourceRange(node, range)
|
||||
const callExp = getNodeFromPath<CallExpression>(
|
||||
node,
|
||||
callExpPath,
|
||||
'CallExpression'
|
||||
)?.node
|
||||
const { val, tag } = getFirstArg(callExp)
|
||||
const [varValA, varValB] = Array.isArray(val) ? val : [val, val]
|
||||
|
||||
const { modifiedAst } = replaceSketchCall(
|
||||
programMemory,
|
||||
node,
|
||||
range,
|
||||
transformTo,
|
||||
callBack({
|
||||
referenceSegName: '',
|
||||
varValA,
|
||||
varValB,
|
||||
tag,
|
||||
})
|
||||
)
|
||||
node = modifiedAst
|
||||
})
|
||||
return { modifiedAst: node }
|
||||
}
|
||||
|
||||
function createSegLen(referenceSegName: string): Value {
|
||||
return createCallExpression('segLen', [
|
||||
createLiteral(referenceSegName),
|
||||
createPipeSubstitution(),
|
||||
])
|
||||
}
|
Reference in New Issue
Block a user