Symbols overlay (#2033)

* start of overlay work

* add new icons

* add constraint symbols

* add three dots

* add primary colours

* refactor how we get constraint info for overlays

* refactor how we get constraint info for overlays

* get symbols working for tangential arc too

* extra data on constraint info

* add initial delete

* fix types and circular dep issue after rebase

* fix quirk with horz vert line overlays

* fix setup and tear down of overlays

* remove overlays that are too small

* throttle overlay updates and prove tests selecting html instead of hardcoded px coords

* initial show overaly on segment hover

* remove overlays when tool is equipped

* dounce overlay updates

* tsc

* make higlighting robust to small changes in source ranges

* replace with variable for unconstrained values, and improve styles for popover

* background tweak

* make overlays unconstrain inputs

* fix small regression

* write query for finding related tag references

* make delete segment safe

* typo

* un used imports

* test deleteSegmentFromPipeExpression

* add getConstraintInfo test

* test removeSingleConstraintInfo

* more tests

* tsc

* add tests for overlay buttons

* rename tests

* fmt

* better naming structure

* more reliablity

* more test tweaks

* fix selection test

* add delete segments with overlays tests

* dependant tag tests for segment delet

* typo

* test clean up

* fix some perf issus

* clean up

* clean up

* make things a little more dry

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* trigger ci

* Make constraint hover popovers readable on light mode

* Touch up the new variable dialog

* Little touch-up to three-dot menu style

* fix highlight issue

* fmt

* use optional chain

* Revert "A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)"

This reverts commit be3d61e4a3.

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* disable var panel in sketch mode

* fix overlay tests after mergi in main

* test tweak

* try fix ubuntu

* fmt

* more test tweaks

* tweak

* tweaks

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
This commit is contained in:
Kurt Hutten
2024-05-24 20:54:42 +10:00
committed by GitHub
parent 87c551b869
commit cf52e151fb
28 changed files with 4359 additions and 216 deletions

View File

@ -5,9 +5,16 @@ import {
getYComponent,
getXComponent,
addCloseToPipe,
getConstraintInfo,
} from './sketch'
import { parse, recast, initPromise } from '../wasm'
import { getNodePathFromSourceRange } from '../queryAst'
import {
parse,
recast,
initPromise,
SourceRange,
CallExpression,
} from '../wasm'
import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst'
import { enginelessExecutor } from '../../lib/testHelpers'
const eachQuad: [number, [number, number]][] = [
@ -212,3 +219,884 @@ describe('testing addTagForSketchOnFace', () => {
expect(recast(modifiedAst)).toBe(expectedCode)
})
})
describe('testing getConstraintInfo', () => {
describe('object notation', () => {
const code = `const part001 = startSketchOn('-XZ')
|> startProfileAt([0,0], %)
|> line([3, 4], %)
|> angledLine({
angle: 3.14,
length: 3.14,
}, %)
|> lineTo([6.14, 3.14], %)
|> xLineTo(8, %)
|> yLineTo(5, %)
|> yLine(3.14, %, 'a')
|> xLine(3.14, %)
|> angledLineOfXLength({
angle: 3.14,
length: 3.14,
}, %)
|> angledLineOfYLength({
angle: 30,
length: 3,
}, %)
|> angledLineToX({
angle: 12.14,
to: 12,
}, %)
|> angledLineToY({
angle: 30,
to: 10.14,
}, %)
|> angledLineThatIntersects({
angle: 3.14,
intersectTag: 'a',
offset: 0
}, %)
|> tangentialArcTo([3.14, 13.14], %)`
const ast = parse(code)
test.each([
[
'line',
[
{
type: 'xRelative',
isConstrained: false,
value: '3',
sourceRange: [78, 79],
argPosition: { type: 'arrayItem', index: 0 },
pathToNode: expect.any(Array),
stdLibFnName: 'line',
},
{
type: 'yRelative',
isConstrained: false,
value: '4',
sourceRange: [81, 82],
argPosition: { type: 'arrayItem', index: 1 },
pathToNode: expect.any(Array),
stdLibFnName: 'line',
},
],
],
[
`angledLine(`,
[
{
type: 'angle',
isConstrained: false,
value: '3.14',
sourceRange: [117, 121],
argPosition: { type: 'objectProperty', key: 'angle' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLine',
},
{
type: 'length',
isConstrained: false,
value: '3.14',
sourceRange: [135, 139],
argPosition: { type: 'objectProperty', key: 'length' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLine',
},
],
],
[
'lineTo',
[
{
type: 'xAbsolute',
isConstrained: false,
value: '6.14',
sourceRange: [162, 166],
argPosition: { type: 'arrayItem', index: 0 },
pathToNode: expect.any(Array),
stdLibFnName: 'lineTo',
},
{
type: 'yAbsolute',
isConstrained: false,
value: '3.14',
sourceRange: [168, 172],
argPosition: { type: 'arrayItem', index: 1 },
pathToNode: expect.any(Array),
stdLibFnName: 'lineTo',
},
],
],
[
'xLineTo',
[
{
type: 'horizontal',
isConstrained: true,
value: 'xLineTo',
sourceRange: [183, 190],
argPosition: undefined,
pathToNode: expect.any(Array),
stdLibFnName: 'xLineTo',
},
{
type: 'xAbsolute',
isConstrained: false,
value: '8',
sourceRange: [191, 192],
argPosition: { type: 'singleValue' },
pathToNode: expect.any(Array),
stdLibFnName: 'xLineTo',
},
],
],
[
'yLineTo',
[
{
type: 'vertical',
isConstrained: true,
value: 'yLineTo',
sourceRange: [202, 209],
argPosition: undefined,
pathToNode: expect.any(Array),
stdLibFnName: 'yLineTo',
},
{
type: 'yAbsolute',
isConstrained: false,
value: '5',
sourceRange: [210, 211],
argPosition: { type: 'singleValue' },
pathToNode: expect.any(Array),
stdLibFnName: 'yLineTo',
},
],
],
[
'yLine(',
[
{
type: 'vertical',
isConstrained: true,
value: 'yLine',
sourceRange: [221, 226],
argPosition: undefined,
pathToNode: expect.any(Array),
stdLibFnName: 'yLine',
},
{
type: 'yRelative',
isConstrained: false,
value: '3.14',
sourceRange: [227, 231],
argPosition: { type: 'singleValue' },
pathToNode: expect.any(Array),
stdLibFnName: 'yLine',
},
],
],
[
'xLine(',
[
{
type: 'horizontal',
isConstrained: true,
value: 'xLine',
sourceRange: [246, 251],
argPosition: undefined,
pathToNode: expect.any(Array),
stdLibFnName: 'xLine',
},
{
type: 'xRelative',
isConstrained: false,
value: '3.14',
sourceRange: [252, 256],
argPosition: { type: 'singleValue' },
pathToNode: expect.any(Array),
stdLibFnName: 'xLine',
},
],
],
[
'angledLineOfXLength',
[
{
type: 'angle',
isConstrained: false,
value: '3.14',
sourceRange: [299, 303],
argPosition: { type: 'objectProperty', key: 'angle' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineOfXLength',
},
{
type: 'xRelative',
isConstrained: false,
value: '3.14',
sourceRange: [317, 321],
argPosition: { type: 'objectProperty', key: 'length' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineOfXLength',
},
],
],
[
'angledLineOfYLength',
[
{
type: 'angle',
isConstrained: false,
value: '30',
sourceRange: [369, 371],
argPosition: { type: 'objectProperty', key: 'angle' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineOfYLength',
},
{
type: 'yRelative',
isConstrained: false,
value: '3',
sourceRange: [385, 386],
argPosition: { type: 'objectProperty', key: 'length' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineOfYLength',
},
],
],
[
'angledLineToX',
[
{
type: 'angle',
isConstrained: false,
value: '12.14',
sourceRange: [428, 433],
argPosition: { type: 'objectProperty', key: 'angle' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineToX',
},
{
type: 'xAbsolute',
isConstrained: false,
value: '12',
sourceRange: [443, 445],
argPosition: { type: 'objectProperty', key: 'to' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineToX',
},
],
],
[
'angledLineToY',
[
{
type: 'angle',
isConstrained: false,
value: '30',
sourceRange: [487, 489],
argPosition: { type: 'objectProperty', key: 'angle' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineToY',
},
{
type: 'yAbsolute',
isConstrained: false,
value: '10.14',
sourceRange: [499, 504],
argPosition: { type: 'objectProperty', key: 'to' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineToY',
},
],
],
[
'angledLineThatIntersects',
[
{
type: 'angle',
isConstrained: false,
value: '3.14',
sourceRange: [557, 561],
argPosition: { type: 'objectProperty', key: 'angle' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineThatIntersects',
},
{
type: 'intersectionOffset',
isConstrained: false,
value: '0',
sourceRange: [598, 599],
argPosition: { type: 'objectProperty', key: 'offset' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineThatIntersects',
},
{
type: 'intersectionTag',
isConstrained: false,
value: "'a'",
sourceRange: [581, 584],
argPosition: {
key: 'intersectTag',
type: 'objectProperty',
},
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineThatIntersects',
},
],
],
[
'tangentialArcTo',
[
{
type: 'tangentialWithPrevious',
isConstrained: true,
value: 'tangentialArcTo',
sourceRange: [613, 628],
argPosition: undefined,
pathToNode: expect.any(Array),
stdLibFnName: 'tangentialArcTo',
},
{
type: 'xAbsolute',
isConstrained: false,
value: '3.14',
sourceRange: [630, 634],
argPosition: { type: 'arrayItem', index: 0 },
pathToNode: expect.any(Array),
stdLibFnName: 'tangentialArcTo',
},
{
type: 'yAbsolute',
isConstrained: false,
value: '13.14',
sourceRange: [636, 641],
argPosition: { type: 'arrayItem', index: 1 },
pathToNode: expect.any(Array),
stdLibFnName: 'tangentialArcTo',
},
],
],
])('testing %s when inputs are unconstrained', (functionName, expected) => {
const sourceRange: SourceRange = [
code.indexOf(functionName),
code.indexOf(functionName) + functionName.length,
]
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
const callExp = getNodeFromPath<CallExpression>(
ast,
pathToNode,
'CallExpression'
).node
const result = getConstraintInfo(callExp, code, pathToNode)
expect(result).toEqual(expected)
})
})
describe('array notation', () => {
const code = `const part001 = startSketchOn('-XZ')
|> startProfileAt([0, 0], %)
|> line([3, 4], %)
|> angledLine([3.14, 3.14], %)
|> lineTo([6.14, 3.14], %)
|> xLineTo(8, %)
|> yLineTo(5, %)
|> yLine(3.14, %, 'a')
|> xLine(3.14, %)
|> angledLineOfXLength([3.14, 3.14], %)
|> angledLineOfYLength([30, 3], %)
|> angledLineToX([12, 12], %)
|> angledLineToY([30, 10], %)
|> angledLineThatIntersects({
angle: 3.14,
intersectTag: 'a',
offset: 0
}, %)
|> tangentialArcTo([3.14, 13.14], %)`
const ast = parse(code)
test.each([
[
`angledLine(`,
[
{
type: 'angle',
isConstrained: false,
value: '3.14',
sourceRange: [112, 116],
argPosition: { type: 'arrayItem', index: 0 },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLine',
},
{
type: 'length',
isConstrained: false,
value: '3.14',
sourceRange: [118, 122],
argPosition: { type: 'arrayItem', index: 1 },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLine',
},
],
],
[
'angledLineOfXLength',
[
{
type: 'angle',
isConstrained: false,
value: '3.14',
sourceRange: [278, 282],
argPosition: { type: 'arrayItem', index: 0 },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineOfXLength',
},
{
type: 'xRelative',
isConstrained: false,
value: '3.14',
sourceRange: [284, 288],
argPosition: { type: 'arrayItem', index: 1 },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineOfXLength',
},
],
],
[
'angledLineOfYLength',
[
{
type: 'angle',
isConstrained: false,
value: '30',
sourceRange: [322, 324],
argPosition: { type: 'arrayItem', index: 0 },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineOfYLength',
},
{
type: 'yRelative',
isConstrained: false,
value: '3',
sourceRange: [326, 327],
argPosition: { type: 'arrayItem', index: 1 },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineOfYLength',
},
],
],
[
'angledLineToX',
[
{
type: 'angle',
isConstrained: false,
value: '12',
sourceRange: [355, 357],
argPosition: { type: 'arrayItem', index: 0 },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineToX',
},
{
type: 'xAbsolute',
isConstrained: false,
value: '12',
sourceRange: [359, 361],
argPosition: { type: 'arrayItem', index: 1 },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineToX',
},
],
],
[
'angledLineToY',
[
{
type: 'angle',
isConstrained: false,
value: '30',
sourceRange: [389, 391],
argPosition: { type: 'arrayItem', index: 0 },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineToY',
},
{
type: 'yAbsolute',
isConstrained: false,
value: '10',
sourceRange: [393, 395],
argPosition: { type: 'arrayItem', index: 1 },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineToY',
},
],
],
])('testing %s when inputs are unconstrained', (functionName, expected) => {
const sourceRange: SourceRange = [
code.indexOf(functionName),
code.indexOf(functionName) + functionName.length,
]
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
const callExp = getNodeFromPath<CallExpression>(
ast,
pathToNode,
'CallExpression'
).node
const result = getConstraintInfo(callExp, code, pathToNode)
expect(result).toEqual(expected)
})
})
describe('constrained', () => {
const code = `const part001 = startSketchOn('-XZ')
|> startProfileAt([0, 0], %)
|> line([3 + 0, 4 + 0], %)
|> angledLine({ angle: 3.14 + 0, length: 3.14 + 0 }, %)
|> lineTo([6.14 + 0, 3.14 + 0], %)
|> xLineTo(8 + 0, %)
|> yLineTo(5 + 0, %)
|> yLine(3.14 + 0, %, 'a')
|> xLine(3.14 + 0, %)
|> angledLineOfXLength({ angle: 3.14 + 0, length: 3.14 + 0 }, %)
|> angledLineOfYLength({ angle: 30 + 0, length: 3 + 0 }, %)
|> angledLineToX({ angle: 12.14 + 0, to: 12 + 0 }, %)
|> angledLineToY({ angle: 30 + 0, to: 10.14 + 0 }, %)
|> angledLineThatIntersects({
angle: 3.14 + 0,
intersectTag: 'a',
offset: 0 + 0
}, %)
|> tangentialArcTo([3.14 + 0, 13.14 + 0], %)`
const ast = parse(code)
test.each([
[
'line',
[
{
type: 'xRelative',
isConstrained: true,
value: '3 + 0',
sourceRange: [83, 88],
argPosition: { type: 'arrayItem', index: 0 },
pathToNode: expect.any(Array),
stdLibFnName: 'line',
},
{
type: 'yRelative',
isConstrained: true,
value: '4 + 0',
sourceRange: [90, 95],
argPosition: { type: 'arrayItem', index: 1 },
pathToNode: expect.any(Array),
stdLibFnName: 'line',
},
],
],
[
`angledLine(`,
[
{
type: 'angle',
isConstrained: true,
value: '3.14 + 0',
sourceRange: [128, 136],
argPosition: { type: 'objectProperty', key: 'angle' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLine',
},
{
type: 'length',
isConstrained: true,
value: '3.14 + 0',
sourceRange: [146, 154],
argPosition: { type: 'objectProperty', key: 'length' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLine',
},
],
],
[
'lineTo',
[
{
type: 'xAbsolute',
isConstrained: true,
value: '6.14 + 0',
sourceRange: [176, 184],
argPosition: { type: 'arrayItem', index: 0 },
pathToNode: expect.any(Array),
stdLibFnName: 'lineTo',
},
{
type: 'yAbsolute',
isConstrained: true,
value: '3.14 + 0',
sourceRange: [186, 194],
argPosition: { type: 'arrayItem', index: 1 },
pathToNode: expect.any(Array),
stdLibFnName: 'lineTo',
},
],
],
[
'xLineTo',
[
{
type: 'horizontal',
isConstrained: true,
value: 'xLineTo',
sourceRange: [207, 214],
argPosition: undefined,
pathToNode: expect.any(Array),
stdLibFnName: 'xLineTo',
},
{
type: 'xAbsolute',
isConstrained: true,
value: '8 + 0',
sourceRange: [215, 220],
argPosition: { type: 'singleValue' },
pathToNode: expect.any(Array),
stdLibFnName: 'xLineTo',
},
],
],
[
'yLineTo',
[
{
type: 'vertical',
isConstrained: true,
value: 'yLineTo',
sourceRange: [232, 239],
argPosition: undefined,
pathToNode: expect.any(Array),
stdLibFnName: 'yLineTo',
},
{
type: 'yAbsolute',
isConstrained: true,
value: '5 + 0',
sourceRange: [240, 245],
argPosition: { type: 'singleValue' },
pathToNode: expect.any(Array),
stdLibFnName: 'yLineTo',
},
],
],
[
'yLine(',
[
{
type: 'vertical',
isConstrained: true,
value: 'yLine',
sourceRange: [257, 262],
argPosition: undefined,
pathToNode: expect.any(Array),
stdLibFnName: 'yLine',
},
{
type: 'yRelative',
isConstrained: true,
value: '3.14 + 0',
sourceRange: [263, 271],
argPosition: { type: 'singleValue' },
pathToNode: expect.any(Array),
stdLibFnName: 'yLine',
},
],
],
[
'xLine(',
[
{
type: 'horizontal',
isConstrained: true,
value: 'xLine',
sourceRange: [288, 293],
argPosition: undefined,
pathToNode: expect.any(Array),
stdLibFnName: 'xLine',
},
{
type: 'xRelative',
isConstrained: true,
value: '3.14 + 0',
sourceRange: [294, 302],
argPosition: { type: 'singleValue' },
pathToNode: expect.any(Array),
stdLibFnName: 'xLine',
},
],
],
[
'angledLineOfXLength',
[
{
type: 'angle',
isConstrained: true,
value: '3.14 + 0',
sourceRange: [343, 351],
argPosition: { type: 'objectProperty', key: 'angle' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineOfXLength',
},
{
type: 'xRelative',
isConstrained: true,
value: '3.14 + 0',
sourceRange: [361, 369],
argPosition: { type: 'objectProperty', key: 'length' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineOfXLength',
},
],
],
[
'angledLineOfYLength',
[
{
type: 'angle',
isConstrained: true,
value: '30 + 0',
sourceRange: [412, 418],
argPosition: { type: 'objectProperty', key: 'angle' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineOfYLength',
},
{
type: 'yRelative',
isConstrained: true,
value: '3 + 0',
sourceRange: [428, 433],
argPosition: { type: 'objectProperty', key: 'length' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineOfYLength',
},
],
],
[
'angledLineToX',
[
{
type: 'angle',
isConstrained: true,
value: '12.14 + 0',
sourceRange: [470, 479],
argPosition: { type: 'objectProperty', key: 'angle' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineToX',
},
{
type: 'xAbsolute',
isConstrained: true,
value: '12 + 0',
sourceRange: [485, 491],
argPosition: { type: 'objectProperty', key: 'to' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineToX',
},
],
],
[
'angledLineToY',
[
{
type: 'angle',
isConstrained: true,
value: '30 + 0',
sourceRange: [528, 534],
argPosition: { type: 'objectProperty', key: 'angle' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineToY',
},
{
type: 'yAbsolute',
isConstrained: true,
value: '10.14 + 0',
sourceRange: [540, 549],
argPosition: { type: 'objectProperty', key: 'to' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineToY',
},
],
],
[
'angledLineThatIntersects',
[
{
type: 'angle',
isConstrained: true,
value: '3.14 + 0',
sourceRange: [606, 614],
argPosition: { type: 'objectProperty', key: 'angle' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineThatIntersects',
},
{
type: 'intersectionOffset',
isConstrained: true,
value: '0 + 0',
sourceRange: [661, 666],
argPosition: { type: 'objectProperty', key: 'offset' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineThatIntersects',
},
{
type: 'intersectionTag',
isConstrained: false,
value: "'a'",
sourceRange: [639, 642],
argPosition: { key: 'intersectTag', type: 'objectProperty' },
pathToNode: expect.any(Array),
stdLibFnName: 'angledLineThatIntersects',
},
],
],
[
'tangentialArcTo',
[
{
type: 'tangentialWithPrevious',
isConstrained: true,
value: 'tangentialArcTo',
sourceRange: [687, 702],
argPosition: undefined,
pathToNode: expect.any(Array),
stdLibFnName: 'tangentialArcTo',
},
{
type: 'xAbsolute',
isConstrained: true,
value: '3.14 + 0',
sourceRange: [704, 712],
argPosition: { type: 'arrayItem', index: 0 },
pathToNode: expect.any(Array),
stdLibFnName: 'tangentialArcTo',
},
{
type: 'yAbsolute',
isConstrained: true,
value: '13.14 + 0',
sourceRange: [714, 723],
argPosition: { type: 'arrayItem', index: 1 },
pathToNode: expect.any(Array),
stdLibFnName: 'tangentialArcTo',
},
],
],
])('testing %s when inputs are unconstrained', (functionName, expected) => {
const sourceRange: SourceRange = [
code.indexOf(functionName),
code.indexOf(functionName) + functionName.length,
]
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
const callExp = getNodeFromPath<CallExpression>(
ast,
pathToNode,
'CallExpression'
).node
const result = getConstraintInfo(callExp, code, pathToNode)
expect(result).toEqual(expected)
})
})
})

View File

@ -11,17 +11,32 @@ import {
Value,
Literal,
VariableDeclaration,
} from '../wasm'
} from 'lang/wasm'
import {
getNodeFromPath,
getNodeFromPathCurry,
getNodePathFromSourceRange,
} from '../queryAst'
import { isLiteralArrayOrStatic } from './sketchcombos'
} from 'lang/queryAst'
import {
LineInputsType,
isLiteralArrayOrStatic,
isNotLiteralArrayOrStatic,
} from 'lang/std/sketchcombos'
import { toolTips, ToolTip } from '../../useStore'
import { createPipeExpression, splitPathAtPipeExpression } from '../modifyAst'
import { SketchLineHelper, ModifyAstBase, TransformCallback } from './stdTypes'
import {
SketchLineHelper,
ModifyAstBase,
TransformCallback,
ConstrainInfo,
RawValues,
ArrayItemInput,
ObjectPropertyInput,
SingleValueInput,
VarValueKeys,
ArrayOrObjItemInput,
} from 'lang/std/stdTypes'
import {
createLiteral,
@ -32,8 +47,8 @@ import {
mutateArrExp,
mutateObjExpProp,
findUniqueName,
} from '../modifyAst'
import { roundOff, getLength, getAngle } from '../../lib/utils'
} from 'lang/modifyAst'
import { roundOff, getLength, getAngle } from 'lib/utils'
import { perpendicularDistance } from 'sketch-helpers'
export type Coords2d = [number, number]
@ -85,6 +100,210 @@ export function createFirstArg(
throw new Error('all sketch line types should have been covered')
}
type AbbreviatedInput =
| ArrayItemInput<any>['index']
| ObjectPropertyInput<any>['key']
| SingleValueInput<any>['type']
| 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 []
const pathToArrayExpression: PathToNode = [
...pathToNode,
['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
),
]
}
function arrayRawValuesHelper(a: Array<[Literal, LineInputsType]>): RawValues {
return a.map(
([literal, argType], index): ArrayItemInput<Literal> => ({
type: 'arrayItem',
index: index === 0 ? 0 : 1,
argType,
value: literal,
})
)
}
function arrOrObjectRawValuesHelper(
a: Array<[Literal, LineInputsType, VarValueKeys]>
): RawValues {
return a.map(
([literal, argType, key], index): ArrayOrObjItemInput<Literal> => ({
type: 'arrayOrObjItem',
// key: argType,w
index: index === 0 ? 0 : 1,
key,
argType,
value: literal,
})
)
}
function singleRawValueHelper(
literal: Literal,
argType: LineInputsType
): RawValues {
return [
{
type: 'singleValue',
argType,
value: literal,
},
]
}
export const lineTo: SketchLineHelper = {
add: ({
node,
@ -114,6 +333,10 @@ export const lineTo: SketchLineHelper = {
if (replaceExisting && createCallback) {
const { callExp, valueUsedInTransform } = createCallback(
newVals,
arrayRawValuesHelper([
[createLiteral(roundOff(to[0], 2)), 'xAbsolute'],
[createLiteral(roundOff(to[1], 2)), 'yAbsolute'],
]),
referencedSegment
)
pipe.body[callIndex] = callExp
@ -150,6 +373,14 @@ export const lineTo: SketchLineHelper = {
}
},
addTag: addTag(),
getConstraintInfo: (callExp, ...args) =>
commonConstraintInfoHelper(
callExp,
['xAbsolute', 'yAbsolute'],
'lineTo',
[{ arrayInput: 0 }, { arrayInput: 1 }],
...args
),
}
export const line: SketchLineHelper = {
@ -207,6 +438,10 @@ export const line: SketchLineHelper = {
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
const { callExp, valueUsedInTransform } = createCallback(
[newXVal, newYVal],
arrayRawValuesHelper([
[createLiteral(roundOff(to[0] - from[0], 2)), 'xRelative'],
[createLiteral(roundOff(to[1] - from[1], 2)), 'yRelative'],
]),
referencedSegment
)
pipe.body[callIndex] = callExp
@ -266,6 +501,14 @@ export const line: SketchLineHelper = {
}
},
addTag: addTag(),
getConstraintInfo: (callExp, ...args) =>
commonConstraintInfoHelper(
callExp,
['xRelative', 'yRelative'],
'line',
[{ arrayInput: 0 }, { arrayInput: 1 }],
...args
),
}
export const xLineTo: SketchLineHelper = {
@ -278,7 +521,10 @@ export const xLineTo: SketchLineHelper = {
if (replaceExisting && createCallback) {
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
const { callExp, valueUsedInTransform } = createCallback([newVal, newVal])
const { callExp, valueUsedInTransform } = createCallback(
[newVal, newVal],
singleRawValueHelper(newVal, 'xAbsolute')
)
pipe.body[callIndex] = callExp
return {
modifiedAst: _node,
@ -314,6 +560,14 @@ export const xLineTo: SketchLineHelper = {
}
},
addTag: addTag(),
getConstraintInfo: (callExp, ...args) =>
horzVertConstraintInfoHelper(
callExp,
['horizontal', 'xAbsolute'],
'xLineTo',
'singleValue',
...args
),
}
export const yLineTo: SketchLineHelper = {
@ -326,7 +580,10 @@ export const yLineTo: SketchLineHelper = {
if (replaceExisting && createCallback) {
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
const { callExp, valueUsedInTransform } = createCallback([newVal, newVal])
const { callExp, valueUsedInTransform } = createCallback(
[newVal, newVal],
singleRawValueHelper(newVal, 'yAbsolute')
)
pipe.body[callIndex] = callExp
return {
modifiedAst: _node,
@ -362,6 +619,14 @@ export const yLineTo: SketchLineHelper = {
}
},
addTag: addTag(),
getConstraintInfo: (callExp, ...args) =>
horzVertConstraintInfoHelper(
callExp,
['vertical', 'yAbsolute'],
'yLineTo',
'singleValue',
...args
),
}
export const xLine: SketchLineHelper = {
@ -375,10 +640,10 @@ export const xLine: SketchLineHelper = {
if (replaceExisting && createCallback) {
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
const { callExp, valueUsedInTransform } = createCallback([
firstArg,
firstArg,
])
const { callExp, valueUsedInTransform } = createCallback(
[firstArg, firstArg],
singleRawValueHelper(firstArg, 'xRelative')
)
pipe.body[callIndex] = callExp
return {
modifiedAst: _node,
@ -412,6 +677,14 @@ export const xLine: SketchLineHelper = {
}
},
addTag: addTag(),
getConstraintInfo: (callExp, ...args) =>
horzVertConstraintInfoHelper(
callExp,
['horizontal', 'xRelative'],
'xLine',
'singleValue',
...args
),
}
export const yLine: SketchLineHelper = {
@ -422,7 +695,10 @@ export const yLine: SketchLineHelper = {
const newVal = createLiteral(roundOff(to[1] - from[1], 2))
if (replaceExisting && createCallback) {
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
const { callExp, valueUsedInTransform } = createCallback([newVal, newVal])
const { callExp, valueUsedInTransform } = createCallback(
[newVal, newVal],
singleRawValueHelper(newVal, 'yRelative')
)
pipe.body[callIndex] = callExp
return {
modifiedAst: _node,
@ -456,6 +732,14 @@ export const yLine: SketchLineHelper = {
}
},
addTag: addTag(),
getConstraintInfo: (callExp, ...args) =>
horzVertConstraintInfoHelper(
callExp,
['vertical', 'yRelative'],
'yLine',
'singleValue',
...args
),
}
export const tangentialArcTo: SketchLineHelper = {
@ -485,6 +769,10 @@ export const tangentialArcTo: SketchLineHelper = {
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
const { callExp, valueUsedInTransform } = createCallback(
[toX, toY],
arrayRawValuesHelper([
[createLiteral(roundOff(to[0], 2)), 'xAbsolute'],
[createLiteral(roundOff(to[1], 2)), 'yAbsolute'],
]),
referencedSegment
)
pipe.body[callIndex] = callExp
@ -534,8 +822,54 @@ export const tangentialArcTo: SketchLineHelper = {
pathToNode,
}
},
// TODO copy-paste from angledLine
addTag: addTag(),
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
),
]
},
}
export const angledLine: SketchLineHelper = {
add: ({
@ -562,6 +896,10 @@ export const angledLine: SketchLineHelper = {
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
const { callExp, valueUsedInTransform } = createCallback(
[newAngleVal, newLengthVal],
arrOrObjectRawValuesHelper([
[newAngleVal, 'angle', 'angle'],
[newLengthVal, 'length', 'length'],
]),
referencedSegment
)
pipe.body[callIndex] = callExp
@ -602,6 +940,17 @@ export const angledLine: SketchLineHelper = {
}
},
addTag: addTag(),
getConstraintInfo: (callExp, ...args) =>
commonConstraintInfoHelper(
callExp,
['angle', 'length'],
'angledLine',
[
{ arrayInput: 0, objInput: 'angle' },
{ arrayInput: 1, objInput: 'length' },
],
...args
),
}
export const angledLineOfXLength: SketchLineHelper = {
@ -631,7 +980,13 @@ export const angledLineOfXLength: SketchLineHelper = {
const angle = createLiteral(roundOff(getAngle(from, to), 0))
const xLength = createLiteral(roundOff(Math.abs(from[0] - to[0]), 2) || 0.1)
const newLine = createCallback
? createCallback([angle, xLength]).callExp
? createCallback(
[angle, xLength],
arrOrObjectRawValuesHelper([
[angle, 'angle', 'angle'],
[xLength, 'xRelative', 'length'],
])
).callExp
: createCallExpression('angledLineOfXLength', [
createArrayExpression([angle, xLength]),
createPipeSubstitution(),
@ -675,6 +1030,17 @@ export const angledLineOfXLength: SketchLineHelper = {
}
},
addTag: addTag(),
getConstraintInfo: (callExp, ...args) =>
commonConstraintInfoHelper(
callExp,
['angle', 'xRelative'],
'angledLineOfXLength',
[
{ arrayInput: 0, objInput: 'angle' },
{ arrayInput: 1, objInput: 'length' },
],
...args
),
}
export const angledLineOfYLength: SketchLineHelper = {
@ -705,7 +1071,13 @@ export const angledLineOfYLength: SketchLineHelper = {
const angle = createLiteral(roundOff(getAngle(from, to), 0))
const yLength = createLiteral(roundOff(Math.abs(from[1] - to[1]), 2) || 0.1)
const newLine = createCallback
? createCallback([angle, yLength]).callExp
? createCallback(
[angle, yLength],
arrOrObjectRawValuesHelper([
[angle, 'angle', 'angle'],
[yLength, 'yRelative', 'length'],
])
).callExp
: createCallExpression('angledLineOfYLength', [
createArrayExpression([angle, yLength]),
createPipeSubstitution(),
@ -749,6 +1121,17 @@ export const angledLineOfYLength: SketchLineHelper = {
}
},
addTag: addTag(),
getConstraintInfo: (callExp, ...args) =>
commonConstraintInfoHelper(
callExp,
['angle', 'yRelative'],
'angledLineOfYLength',
[
{ arrayInput: 0, objInput: 'angle' },
{ arrayInput: 1, objInput: 'length' },
],
...args
),
}
export const angledLineToX: SketchLineHelper = {
@ -772,6 +1155,10 @@ export const angledLineToX: SketchLineHelper = {
if (replaceExisting && createCallback) {
const { callExp, valueUsedInTransform } = createCallback(
[angle, xArg],
arrOrObjectRawValuesHelper([
[angle, 'angle', 'angle'],
[xArg, 'xAbsolute', 'to'],
]),
referencedSegment
)
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
@ -818,6 +1205,17 @@ export const angledLineToX: SketchLineHelper = {
}
},
addTag: addTag(),
getConstraintInfo: (callExp, ...args) =>
commonConstraintInfoHelper(
callExp,
['angle', 'xAbsolute'],
'angledLineToX',
[
{ arrayInput: 0, objInput: 'angle' },
{ arrayInput: 1, objInput: 'to' },
],
...args
),
}
export const angledLineToY: SketchLineHelper = {
@ -842,6 +1240,10 @@ export const angledLineToY: SketchLineHelper = {
if (replaceExisting && createCallback) {
const { callExp, valueUsedInTransform } = createCallback(
[angle, yArg],
arrOrObjectRawValuesHelper([
[angle, 'angle', 'angle'],
[yArg, 'yAbsolute', 'to'],
]),
referencedSegment
)
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
@ -888,6 +1290,17 @@ export const angledLineToY: SketchLineHelper = {
}
},
addTag: addTag(),
getConstraintInfo: (callExp, ...args) =>
commonConstraintInfoHelper(
callExp,
['angle', 'yAbsolute'],
'angledLineToY',
[
{ arrayInput: 0, objInput: 'angle' },
{ arrayInput: 1, objInput: 'to' },
],
...args
),
}
export const angledLineThatIntersects: SketchLineHelper = {
@ -921,7 +1334,23 @@ export const angledLineThatIntersects: SketchLineHelper = {
)
if (replaceExisting && createCallback) {
const { callExp, valueUsedInTransform } = createCallback([angle, offset])
const { callExp, valueUsedInTransform } = createCallback(
[angle, offset],
[
{
type: 'objectProperty',
key: 'angle',
value: angle,
argType: 'angle',
},
{
type: 'objectProperty',
key: 'offset',
value: offset,
argType: 'intersectionOffset',
},
]
)
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
pipe.body[callIndex] = callExp
return {
@ -976,7 +1405,86 @@ export const angledLineThatIntersects: SketchLineHelper = {
pathToNode,
}
},
addTag: addTag(), // TODO might be wrong
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) {
const tag = firstArg.properties[intersectTag]?.value
const pathToTagProp: PathToNode = [
...pathToObjectExp,
[intersectTag, 'index'],
['value', 'Property'],
]
returnVal.push(
constrainInfo(
'intersectionTag',
isNotLiteralArrayOrStatic(tag),
code.slice(tag.start, tag.end),
'angledLineThatIntersects',
'intersectTag',
[tag.start, tag.end],
pathToTagProp
)
)
}
return returnVal
},
}
export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({
@ -1048,6 +1556,20 @@ export function changeSketchArguments(
throw new Error(`not a sketch line helper: ${callExpression?.callee?.name}`)
}
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
)
}
export function compareVec2Epsilon(
vec1: [number, number],
vec2: [number, number],
@ -1162,7 +1684,7 @@ export function addCloseToPipe({
export function replaceSketchLine({
node,
programMemory,
sourceRange,
pathToNode: _pathToNode,
fnName,
to,
from,
@ -1171,7 +1693,7 @@ export function replaceSketchLine({
}: {
node: Program
programMemory: ProgramMemory
sourceRange: SourceRange
pathToNode: PathToNode
fnName: ToolTip
to: [number, number]
from: [number, number]
@ -1185,13 +1707,12 @@ export function replaceSketchLine({
if (![...toolTips, 'intersect'].includes(fnName))
throw new Error('not a tooltip')
const _node = { ...node }
const thePath = getNodePathFromSourceRange(_node, sourceRange)
const { add } = sketchLineHelperMap[fnName]
const { modifiedAst, valueUsedInTransform, pathToNode } = add({
node: _node,
previousProgramMemory: programMemory,
pathToNode: thePath,
pathToNode: _pathToNode,
referencedSegment,
to,
from,
@ -1406,5 +1927,5 @@ export function getFirstArg(callExp: CallExpression): {
// TODO probably needs it's own implementation
return getFirstArgValuesForXYFns(callExp)
}
throw new Error('unexpected call expression')
throw new Error('unexpected call expression: ' + name)
}

View File

@ -1,3 +1,4 @@
import { getNodeFromPath } from 'lang/queryAst'
import { ToolTip, toolTips } from '../../useStore'
import {
Program,
@ -6,8 +7,27 @@ import {
SketchGroup,
SourceRange,
Path,
PathToNode,
Value,
} from '../wasm'
export function getSketchSegmentFromPathToNode(
sketchGroup: SketchGroup,
ast: Program,
pathToNode: PathToNode
): {
segment: SketchGroup['value'][number]
index: number
} {
// TODO: once pathTodNode is stored on program memory as part of execution,
// we can check if the pathToNode matches the pathToNode of the sketchGroup.
// For now we fall back to the sourceRange
const node = getNodeFromPath<Value>(ast, pathToNode).node
if (!node || typeof node.start !== 'number' || !node.end)
throw new Error('no node found')
const sourceRange: SourceRange = [node.start, node.end]
return getSketchSegmentFromSourceRange(sketchGroup, sourceRange)
}
export function getSketchSegmentFromSourceRange(
sketchGroup: SketchGroup,
[rangeStart, rangeEnd]: SourceRange

View File

@ -1,4 +1,4 @@
import { TransformCallback } from './stdTypes'
import { TransformCallback, VarValues } from './stdTypes'
import { toolTips, ToolTip } from '../../useStore'
import { Selections, Selection } from 'lib/selections'
import {
@ -17,6 +17,7 @@ import {
isValueZero,
} from '../queryAst'
import {
createArrayExpression,
createBinaryExpression,
createBinaryExpressionWithUnary,
createCallExpression,
@ -27,17 +28,27 @@ import {
createUnaryExpression,
giveSketchFnCallTag,
} from '../modifyAst'
import { createFirstArg, getFirstArg, replaceSketchLine } from './sketch'
import { getSketchSegmentFromSourceRange } from './sketchConstraints'
import {
createFirstArg,
getConstraintInfo,
getFirstArg,
replaceSketchLine,
} from './sketch'
import {
getSketchSegmentFromPathToNode,
getSketchSegmentFromSourceRange,
} from './sketchConstraints'
import { getAngle, roundOff, normaliseAngle } from '../../lib/utils'
type LineInputsType =
export type LineInputsType =
| 'xAbsolute'
| 'yAbsolute'
| 'xRelative'
| 'yRelative'
| 'angle'
| 'length'
| 'intersectionOffset'
| 'intersectionTag'
export type ConstraintType =
| 'equalLength'
@ -70,6 +81,31 @@ function createCallWrapper(
}
}
/**
* Abstracts creation of a callExpression ready for use for a sketchCombo transform
* Assume it exists within a pipe and adds the pipe substitution
* @param tool line, lineTo, angledLine, etc
* @param val The first argument to the function
* @param tag
* @param valueUsedInTransform
* @returns
*/
function createStdlibCallExpression(
tool: ToolTip,
val: Value,
tag?: Value,
valueUsedInTransform?: number
): ReturnType<TransformCallback> {
const args = [val, createPipeSubstitution()]
if (tag) {
args.push(tag)
}
return {
callExp: createCallExpression(tool, args),
valueUsedInTransform,
}
}
function intersectCallWrapper({
fnName,
angleVal,
@ -106,6 +142,7 @@ function intersectCallWrapper({
export type TransformInfo = {
tooltip: ToolTip
createNode: (a: {
varValues: VarValues
varValA: Value // x / angle
varValB: Value // y / length or x y for angledLineOfXlength etc
referenceSegName: string
@ -145,7 +182,7 @@ const basicAngledLineCreateNode =
varValToUse: 'ang' | 'len' | 'none' = 'none'
): TransformInfo['createNode'] =>
({ referenceSegName, tag, forceValueUsedInTransform, varValA, varValB }) =>
(args, path) => {
(args, _, path) => {
const refAng = path ? getAngle(path?.from, path?.to) : 0
const nonForcedAng =
varValToUse === 'ang'
@ -252,7 +289,7 @@ const setHorzVertDistanceCreateNode =
index = xOrY === 'x' ? 0 : 1
): TransformInfo['createNode'] =>
({ referenceSegName, tag, forceValueUsedInTransform }) => {
return (args, referencedSegment) => {
return (args, _, referencedSegment) => {
const valueUsedInTransform = roundOff(
getArgLiteralVal(args?.[index]) - (referencedSegment?.to?.[index] || 0),
2
@ -279,7 +316,7 @@ const setHorzVertDistanceForAngleLineCreateNode =
index = xOrY === 'x' ? 0 : 1
): TransformInfo['createNode'] =>
({ referenceSegName, tag, forceValueUsedInTransform, varValA }) => {
return (args, referencedSegment) => {
return (args, _, referencedSegment) => {
const valueUsedInTransform = roundOff(
getArgLiteralVal(args?.[1]) - (referencedSegment?.to?.[index] || 0),
2
@ -305,7 +342,7 @@ const setAbsDistanceCreateNode =
index = xOrY === 'x' ? 0 : 1
): TransformInfo['createNode'] =>
({ tag, forceValueUsedInTransform }) => {
return (args, referencedSegment) => {
return (args, _, referencedSegment) => {
const valueUsedInTransform = roundOff(
getArgLiteralVal(args?.[index]) - (referencedSegment?.to?.[index] || 0),
2
@ -335,7 +372,7 @@ const setAbsDistanceForAngleLineCreateNode =
index = xOrY === 'x' ? 0 : 1
): TransformInfo['createNode'] =>
({ tag, forceValueUsedInTransform, varValA }) => {
return (args, referencedSegment) => {
return (args) => {
const valueUsedInTransform = roundOff(getArgLiteralVal(args?.[1]), 2)
const val =
(forceValueUsedInTransform as BinaryPart) ||
@ -352,7 +389,7 @@ const setAbsDistanceForAngleLineCreateNode =
const setHorVertDistanceForXYLines =
(xOrY: 'x' | 'y'): TransformInfo['createNode'] =>
({ referenceSegName, tag, forceValueUsedInTransform }) => {
return (args, referencedSegment) => {
return (args, _, referencedSegment) => {
const index = xOrY === 'x' ? 0 : 1
const valueUsedInTransform = roundOff(
getArgLiteralVal(args?.[index]) - (referencedSegment?.to?.[index] || 0),
@ -381,7 +418,7 @@ const setHorzVertDistanceConstraintLineCreateNode =
varVal,
])
return (args, referencedSegment) => {
return (args, _, referencedSegment) => {
const makeBinExp = (index: 0 | 1) => {
const arg = getArgLiteralVal(args?.[index])
return createBinaryExpressionWithUnary([
@ -449,7 +486,7 @@ const setAngledIntersectForAngledLines: TransformInfo['createNode'] =
const setAngleBetweenCreateNode =
(tranformToType: 'none' | 'xAbs' | 'yAbs'): TransformInfo['createNode'] =>
({ referenceSegName, tag, forceValueUsedInTransform, varValA, varValB }) => {
return (args, referencedSegment) => {
return (args, _, referencedSegment) => {
const refAngle = referencedSegment
? getAngle(referencedSegment?.from, referencedSegment?.to)
: 0
@ -1168,6 +1205,92 @@ export function getRemoveConstraintsTransform(
return false
}
export function removeSingleConstraint({
pathToCallExp,
arrayIndex,
objectProperty,
ast,
}: {
pathToCallExp: PathToNode
arrayIndex?: number
objectProperty?: string
ast: Program
}): TransformInfo | false {
const callExp = getNodeFromPath<CallExpression>(
ast,
pathToCallExp,
'CallExpression'
).node
if (callExp.type !== 'CallExpression') throw new Error('Invalid node type')
const transform: TransformInfo = {
tooltip: callExp.callee.name as any,
createNode:
({ tag, referenceSegName, varValues }) =>
(_, rawValues) => {
if (objectProperty) {
const expression: Parameters<typeof createObjectExpression>[0] = {}
varValues.forEach((varValue) => {
if (
varValue.type !== 'objectProperty' &&
varValue.type !== 'arrayOrObjItem'
)
return
const literal = rawValues.find(
(rawValue) =>
(rawValue.type === 'objectProperty' ||
rawValue.type === 'arrayOrObjItem') &&
rawValue.key === objectProperty
)?.value
const value =
(varValue.key === objectProperty && literal) || varValue.value
expression[varValue.key] = value
})
const objExp = createObjectExpression(expression)
return createStdlibCallExpression(
callExp.callee.name as any,
objExp,
tag
)
}
if (typeof arrayIndex === 'number') {
const values = varValues.map((varValue) => {
if (
(varValue.type === 'arrayItem' ||
varValue.type === 'arrayOrObjItem') &&
varValue.index === arrayIndex
) {
const literal = rawValues.find(
(rawValue) =>
(rawValue.type === 'arrayItem' ||
rawValue.type === 'arrayOrObjItem') &&
rawValue.index === arrayIndex
)?.value
return (
(varValue.index === arrayIndex && literal) || varValue.value
)
}
return varValue.value
})
return createStdlibCallExpression(
callExp.callee.name as any,
createArrayExpression(values),
tag
)
}
// if (typeof arrayIndex !== 'number' || !objectProperty) must be single value input xLine, yLineTo etc
return createCallWrapper(
callExp.callee.name as any,
rawValues[0].value,
tag
)
},
}
return transform
}
function getTransformMapPath(
sketchFnExp: CallExpression,
constraintType: ConstraintType
@ -1392,7 +1515,7 @@ export function transformAstSketchLines({
referencedSegmentRange,
}: {
ast: Program
selectionRanges: Selections
selectionRanges: Selections | PathToNode[]
transformInfos: TransformInfo[]
programMemory: ProgramMemory
referenceSegName: string
@ -1408,15 +1531,13 @@ export function transformAstSketchLines({
let _valueUsedInTransform // TODO should this be an array?
const pathToNodeMap: PathToNodeMap = {}
selectionRanges.codeBasedSelections.forEach(({ range }, index) => {
const processSelection = (_pathToNode: PathToNode, index: number) => {
const callBack = transformInfos?.[index].createNode
const transformTo = transformInfos?.[index].tooltip
if (!callBack || !transformTo) throw new Error('no callback helper')
const getNode = getNodeFromPathCurry(
node,
getNodePathFromSourceRange(node, range)
)
const getNode = getNodeFromPathCurry(node, _pathToNode)
const callExp = getNode<CallExpression>('CallExpression')?.node
const varDec = getNode<VariableDeclarator>('VariableDeclarator').node
@ -1436,11 +1557,47 @@ export function transformAstSketchLines({
''
const [varValA, varValB] = Array.isArray(val) ? val : [val, val]
const varValues: VarValues = []
getConstraintInfo(callExp, '', _pathToNode).forEach((a) => {
if (
a.type === 'tangentialWithPrevious' ||
a.type === 'horizontal' ||
a.type === 'vertical'
)
return
if (a?.argPosition?.type === 'arrayItem') {
varValues.push({
type: 'arrayItem',
index: a.argPosition.index,
value: getNodeFromPath<Value>(ast, a.pathToNode).node,
argType: a.type,
})
} else if (a?.argPosition?.type === 'objectProperty') {
varValues.push({
type: 'objectProperty',
key: a.argPosition.key,
value: getNodeFromPath<Value>(ast, a.pathToNode).node,
argType: a.type,
})
} else if (a?.argPosition?.type === 'singleValue') {
varValues.push({
type: 'singleValue',
argType: a.type,
value: getNodeFromPath<Value>(ast, a.pathToNode).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 = getSketchSegmentFromSourceRange(sketchGroup, range).segment
const seg = getSketchSegmentFromPathToNode(
sketchGroup,
ast,
_pathToNode
).segment
const referencedSegment = referencedSegmentRange
? getSketchSegmentFromSourceRange(sketchGroup, referencedSegmentRange)
.segment
@ -1450,13 +1607,14 @@ export function transformAstSketchLines({
{
node: node,
programMemory,
sourceRange: range,
pathToNode: _pathToNode,
referencedSegment,
fnName: transformTo || (callExp.callee.name as ToolTip),
to,
from,
createCallback: callBack({
referenceSegName: _referencedSegmentName,
varValues,
varValA,
varValB,
tag: callBackTag,
@ -1470,7 +1628,16 @@ export function transformAstSketchLines({
if (typeof valueUsedInTransform === 'number') {
_valueUsedInTransform = valueUsedInTransform
}
})
}
if ('codeBasedSelections' in selectionRanges) {
selectionRanges.codeBasedSelections.forEach(({ range }, index) =>
processSelection(getNodePathFromSourceRange(node, range), index)
)
} else {
selectionRanges.forEach(processSelection)
}
return {
modifiedAst: node,
valueUsedInTransform: _valueUsedInTransform,

View File

@ -1,3 +1,4 @@
import { ToolTip } from 'useStore'
import {
ProgramMemory,
Path,
@ -5,9 +6,11 @@ import {
Program,
Value,
PathToNode,
CallExpression,
Literal,
} from '../wasm'
import { ToolTip } from '../../useStore'
import { EngineCommandManager } from './engineConnection'
import { LineInputsType } from './sketchcombos'
export interface InternalFirstArg {
programMemory: ProgramMemory
@ -44,16 +47,70 @@ interface updateArgs extends ModifyAstBase {
to: [number, number]
}
export type VarValueKeys = 'angle' | 'offset' | 'length' | 'to' | 'intersectTag'
export interface SingleValueInput<T> {
type: 'singleValue'
argType: LineInputsType
value: T
}
export interface ArrayItemInput<T> {
type: 'arrayItem'
index: 0 | 1
argType: LineInputsType
value: T
}
export interface ObjectPropertyInput<T> {
type: 'objectProperty'
key: VarValueKeys
argType: LineInputsType
value: T
}
export interface ArrayOrObjItemInput<T> {
type: 'arrayOrObjItem'
key: VarValueKeys
index: 0 | 1
argType: LineInputsType
value: T
}
export type _VarValue<T> =
| SingleValueInput<T>
| ArrayItemInput<T>
| ObjectPropertyInput<T>
| ArrayOrObjItemInput<T>
export type VarValue = _VarValue<Value>
export type RawValue = _VarValue<Literal>
export type VarValues = Array<VarValue>
export type RawValues = Array<RawValue>
type SimplifiedVarValue =
| {
type: 'singleValue'
}
| { type: 'arrayItem'; index: 0 | 1 }
| { type: 'objectProperty'; key: VarValueKeys }
export type TransformCallback = (
args: [Value, Value],
literalValues: RawValues,
referencedSegment?: Path
) => {
callExp: Value
valueUsedInTransform?: number
}
export type SketchCallTransfromMap = {
[key in ToolTip]: TransformCallback
export interface ConstrainInfo {
stdLibFnName: ToolTip
type: LineInputsType | 'vertical' | 'horizontal' | 'tangentialWithPrevious'
isConstrained: boolean
sourceRange: SourceRange
pathToNode: PathToNode
value: string
calculatedValue?: any
argPosition?: SimplifiedVarValue
}
export interface SketchLineHelper {
@ -70,4 +127,9 @@ export interface SketchLineHelper {
modifiedAst: Program
tag: string
}
getConstraintInfo: (
callExp: CallExpression,
code: string,
pathToNode: PathToNode
) => ConstrainInfo[]
}