Files
modeling-app/src/lang/std/sketchcombos.ts
Kurt Hutten d0930477ad xstate migration (#713)
* Add basic Popover functionality

* Fix up light mode of basic bar

* Add support for 2D and 3D mode styling

* Turn toolbar buttons back on

* Remove ActionButton until after tool logic refactor

* Add transitions

* Add initial modeling machine
This is not a full description of how the modelingMachine should work,
but begins to replicate all of the features of our useStore in XState
instead of zustand.

* Add fillet tool flow

* Refactor: break out engine manager setup into hook
Preparing for making a wrapper component around the App
that will manage the engine manager at the same level as
the modelingMachine.

* Create modeling provider, move engine management to it

* Refactor: move other engine-related useEffect into hook

* Add TS schema, selection actions to modelingMachine

* Add barebones modeling machine to app
Only implementing adding to code-based selections in the text editor so far

* Update moved useEffect hook after merge

* give myself reminder TODO

* fix engineCommandManager waitForReady Promise

* enable devtools

* make utility class for handling default planes

* progresson startNewSketch and EditSketch

* add provider to tests

* too large of a commit
put all of the lang state into another singleton, but did lots of work on xstate too

* fix edit sketch ast issue

* re-execute on sketch exit

* prettierignore xstate typegen file

* add move tool button back in

* handle mouse commands with xState states

* fix move

* remove old imports

* big useStore delete

* fix some destructuring bugs

* start of constraint actions

* add horizontal/vertical distance constraints

* fix more destructuring errors

* fix

* add angle constaints

* add align vertically/horizontally constraints

* add length and equal length constraints

* rename modal states to be more cmd bar friendly

* add doesPipeHave query

* add another query

* add extrude states

* state machine clean up

* xstate layout tweak

* make xstate types happy

* Revamp cursor logic and place curors after ast mod

* Xstate merge (#796)

* turning back on all planes (#720)

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix more tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix stdlib

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

compile

Signed-off-by: Jess Frazelle <github@jessfraz.com>

update sample code

Signed-off-by: Jess Frazelle <github@jessfraz.com>

re-enable the planes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fix all tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

boilerplate

Signed-off-by: Jess Frazelle <github@jessfraz.com>

Cut release v0.9.2 (#714)

rust make default planes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

use the planes from engine

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fixups

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

negative args

Signed-off-by: Jess Frazelle <github@jessfraz.com>

diable camera

Signed-off-by: Jess Frazelle <github@jessfraz.com>

hide planes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updatress

Signed-off-by: Jess Frazelle <github@jessfraz.com>

fmt

Signed-off-by: Jess Frazelle <github@jessfraz.com>

Update src/hooks/useAppMode.ts

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>

Update src/hooks/useAppMode.ts

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>

cleanups

Signed-off-by: Jess Frazelle <github@jessfraz.com>

Bump kittycad from 0.2.26 to 0.2.27 in /src-tauri (#726)

Bumps [kittycad](https://github.com/KittyCAD/kittycad.rs) from 0.2.26 to 0.2.27.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](https://github.com/KittyCAD/kittycad.rs/compare/v0.2.26...v0.2.27)

---
updated-dependencies:
- dependency-name: kittycad
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Bump tauri-plugin-fs-extra from `b04bde3` to `6c7a4c0` in /src-tauri (#725)

Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `b04bde3` to `6c7a4c0`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](b04bde3461...6c7a4c0984)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Bump toml from 0.8.0 to 0.8.1 in /src-tauri (#724)

Bumps [toml](https://github.com/toml-rs/toml) from 0.8.0 to 0.8.1.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.0...toml-v0.8.1)

---
updated-dependencies:
- dependency-name: toml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Bump openapitor from `61a1605` to `d3e98c4` in /src/wasm-lib (#723)

Bumps [openapitor](https://github.com/KittyCAD/kittycad.rs) from `61a1605` to `d3e98c4`.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](61a16059b3...d3e98c4ec0)

---
updated-dependencies:
- dependency-name: openapitor
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Bump kittycad from 0.2.26 to 0.2.27 in /src/wasm-lib (#722)

Bumps [kittycad](https://github.com/KittyCAD/kittycad.rs) from 0.2.26 to 0.2.27.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](https://github.com/KittyCAD/kittycad.rs/compare/v0.2.26...v0.2.27)

---
updated-dependencies:
- dependency-name: kittycad
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Bump thiserror from 1.0.48 to 1.0.49 in /src/wasm-lib (#721)

Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.48 to 1.0.49.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.48...1.0.49)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Bump expectorate from 1.0.7 to 1.1.0 in /src/wasm-lib (#712)

Bumps [expectorate](https://github.com/oxidecomputer/expectorate) from 1.0.7 to 1.1.0.
- [Release notes](https://github.com/oxidecomputer/expectorate/releases)
- [Commits](https://github.com/oxidecomputer/expectorate/compare/v1.0.7...v1.1.0)

---
updated-dependencies:
- dependency-name: expectorate
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Bump clap from 4.4.4 to 4.4.5 in /src/wasm-lib (#711)

Bumps [clap](https://github.com/clap-rs/clap) from 4.4.4 to 4.4.5.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.4...v4.4.5)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

refactor cleanup

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

type improvements

* use new sketchmode no camera

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* js working better

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* start of negative planes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* tests and neg

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* images

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* norma;s

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* better initial load of planes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* ts

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix tsc

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix edit sketch

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* add regression test for 2d solid issue

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* show planes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix clippy

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* canecel in progress

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix ci as well

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* stopping point

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* refactor

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* it works

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* Hide planes (#797)

* hide planes in one go

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* update hide;

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* make tsc happy

* Make "Replay Onboarding" button available on home settings page (#804)

* Fix unrelated bug, settings button in the home sidebar
doesn't go to the home settings after my previous fixes to routes

* Turn on "Replay Onboarding" button in home settings

* Use ONBOARDING_PROJECT_NAME in both places

* Fix formatting

* Cut release v0.10.0 (#803)

Co-authored-by: Frank Noirot <frank@kittycad.io>

* Bump kittycad from 0.2.28 to 0.2.31 in /src-tauri (#798)

Bumps [kittycad](https://github.com/KittyCAD/kittycad.rs) from 0.2.28 to 0.2.31.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](https://github.com/KittyCAD/kittycad.rs/compare/v0.2.28...v0.2.31)

---
updated-dependencies:
- dependency-name: kittycad
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump openapitor from `fa0345c` to `c122a9b` in /src/wasm-lib (#800)

Bumps [openapitor](https://github.com/KittyCAD/kittycad.rs) from `fa0345c` to `c122a9b`.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](fa0345c514...c122a9b1d6)

---
updated-dependencies:
- dependency-name: openapitor
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump syn from 2.0.37 to 2.0.38 in /src/wasm-lib (#801)

Bumps [syn](https://github.com/dtolnay/syn) from 2.0.37 to 2.0.38.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.37...2.0.38)

---
updated-dependencies:
- dependency-name: syn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump winnow from 0.5.15 to 0.5.16 in /src/wasm-lib (#799)

Bumps [winnow](https://github.com/winnow-rs/winnow) from 0.5.15 to 0.5.16.
- [Changelog](https://github.com/winnow-rs/winnow/blob/main/CHANGELOG.md)
- [Commits](https://github.com/winnow-rs/winnow/compare/v0.5.15...v0.5.16)

---
updated-dependencies:
- dependency-name: winnow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump tauri-plugin-fs-extra from `fa32d1a` to `9f27e6e` in /src-tauri (#802)

Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `fa32d1a` to `9f27e6e`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](fa32d1afa9...9f27e6e441)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* better plane selection

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* use the sketch plane id

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* add todo w bug

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* Improve Prop Typings for Modals. Remove instances of `any`. (#792)

* Update typings for modals. Remove instances of `any`

* Fix generic type for creating modals

* cleanup other stuffs

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* make plane id available when selecting default plane

* few clean up things

* change enter sketch action order to make sure plane id is available to 'enter edit mode'

* Revert "Improve Prop Typings for Modals. Remove instances of `any`. (… (#813)

Revert "Improve Prop Typings for Modals. Remove instances of `any`. (#792)"

This reverts commit 629f326f4c.

* ffmpeg instructions (#814)

* fix some tsc stuff

* small tweak

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jason Rametta <rametta@outlook.com>

* clean up

* fix test and tsc

* remove one more thing from useStore

* tweak state digrame layout

* fmt

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Frank Johnson <frankjohnson1993@gmail.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jason Rametta <rametta@outlook.com>
2023-10-11 13:36:54 +11:00

1563 lines
43 KiB
TypeScript

import { TransformCallback } from './stdTypes'
import { Selections, toolTips, ToolTip, Selection } from '../../useStore'
import {
CallExpression,
Program,
Value,
BinaryPart,
VariableDeclarator,
PathToNode,
ProgramMemory,
} from '../wasm'
import {
getNodeFromPath,
getNodeFromPathCurry,
getNodePathFromSourceRange,
isValueZero,
} from '../queryAst'
import {
createBinaryExpression,
createBinaryExpressionWithUnary,
createCallExpression,
createIdentifier,
createLiteral,
createObjectExpression,
createPipeSubstitution,
createUnaryExpression,
giveSketchFnCallTag,
} from '../modifyAst'
import { createFirstArg, getFirstArg, replaceSketchLine } from './sketch'
import { getSketchSegmentFromSourceRange } from './sketchConstraints'
import { getAngle, roundOff, normaliseAngle } from '../../lib/utils'
type LineInputsType =
| 'xAbsolute'
| 'yAbsolute'
| 'xRelative'
| 'yRelative'
| 'angle'
| 'length'
export type ConstraintType =
| 'equalLength'
| 'vertical'
| 'horizontal'
| 'equalAngle'
| 'setHorzDistance'
| 'setVertDistance'
| 'setAngle'
| 'setLength'
| 'intersect'
| 'removeConstrainingValues'
| 'xAbs'
| 'yAbs'
| 'setAngleBetween'
function createCallWrapper(
a: ToolTip,
val: [Value, Value] | Value,
tag?: Value,
valueUsedInTransform?: number
): ReturnType<TransformCallback> {
return {
callExp: createCallExpression(a, [
createFirstArg(a, val, tag),
createPipeSubstitution(),
]),
valueUsedInTransform,
}
}
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,
}
if (tag) {
firstArg['tag'] = tag
}
return {
callExp: createCallExpression(fnName, [
createObjectExpression(firstArg),
createPipeSubstitution(),
]),
valueUsedInTransform,
}
}
export type TransformInfo = {
tooltip: ToolTip
createNode: (a: {
varValA: Value // x / angle
varValB: Value // y / length or x y for angledLineOfXlength etc
referenceSegName: string
tag?: Value
forceValueUsedInTransform?: Value
}) => TransformCallback
}
type TransformMap = {
[key in ToolTip]?: {
[key in LineInputsType | 'free']?: {
[key in ConstraintType]?: TransformInfo
}
}
}
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]))
}
const basicAngledLineCreateNode =
(
referenceSeg: 'ang' | 'len' | 'none' = 'none',
valToForce: 'ang' | 'len' | 'none' = 'none',
varValToUse: 'ang' | 'len' | 'none' = 'none'
): TransformInfo['createNode'] =>
({ referenceSegName, tag, forceValueUsedInTransform, varValA, varValB }) =>
(args, path) => {
const refAng = path ? getAngle(path?.from, path?.to) : 0
const nonForcedAng =
varValToUse === 'ang'
? varValA
: referenceSeg === 'ang'
? getClosesAngleDirection(
args[0],
refAng,
createSegAngle(referenceSegName) as BinaryPart
)
: 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(
'angledLine',
[
shouldForceAng ? forceValueUsedInTransform : nonForcedAng,
shouldForceLen ? forceValueUsedInTransform : nonForcedLen,
],
tag,
getArgLiteralVal(valToForce === 'ang' ? args[0] : args[1])
)
}
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 = createBinaryExpressionWithUnary([
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
}
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
? createBinaryExpressionWithUnary([angleVal, createLiteral(180)])
: angleVal
}
const setHorzVertDistanceCreateNode =
(
xOrY: 'x' | 'y',
index = xOrY === 'x' ? 0 : 1
): TransformInfo['createNode'] =>
({ referenceSegName, tag, forceValueUsedInTransform }) => {
return (args, referencedSegment) => {
const valueUsedInTransform = roundOff(
getArgLiteralVal(args?.[index]) - (referencedSegment?.to?.[index] || 0),
2
)
let finalValue: Value = createBinaryExpressionWithUnary([
createSegEnd(referenceSegName, !index),
(forceValueUsedInTransform as BinaryPart) ||
createLiteral(valueUsedInTransform),
])
if (isValueZero(forceValueUsedInTransform)) {
finalValue = createSegEnd(referenceSegName, !index)
}
return createCallWrapper(
'lineTo',
!index ? [finalValue, args[1]] : [args[0], finalValue],
tag,
valueUsedInTransform
)
}
}
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
)
const binExp = createBinaryExpressionWithUnary([
createSegEnd(referenceSegName, !index),
(forceValueUsedInTransform as BinaryPart) ||
createLiteral(valueUsedInTransform),
])
return createCallWrapper(
xOrY === 'x' ? 'angledLineToX' : 'angledLineToY',
[varValA, binExp],
tag,
valueUsedInTransform
)
}
}
const setAbsDistanceCreateNode =
(
xOrY: 'x' | 'y',
isXOrYLine = false,
index = xOrY === 'x' ? 0 : 1
): TransformInfo['createNode'] =>
({ tag, forceValueUsedInTransform }) => {
return (args, referencedSegment) => {
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) => {
const valueUsedInTransform = roundOff(
getArgLiteralVal(args?.[1]) - (referencedSegment?.to?.[index] || 0),
2
)
const val =
(forceValueUsedInTransform as BinaryPart) ||
createLiteral(valueUsedInTransform)
return createCallWrapper(
xOrY === 'x' ? 'angledLineToX' : 'angledLineToY',
[varValA, val],
tag,
valueUsedInTransform
)
}
}
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
)
const makeBinExp = createBinaryExpressionWithUnary([
createSegEnd(referenceSegName, xOrY === 'x'),
(forceValueUsedInTransform as BinaryPart) ||
createLiteral(valueUsedInTransform),
])
return createCallWrapper(
xOrY === 'x' ? 'xLineTo' : 'yLineTo',
makeBinExp,
tag,
valueUsedInTransform
)
}
}
const setHorzVertDistanceConstraintLineCreateNode =
(isX: boolean): TransformInfo['createNode'] =>
({ referenceSegName, tag, varValA, varValB }) => {
const varVal = (isX ? varValB : varValA) as BinaryPart
const varValBinExp = createBinaryExpressionWithUnary([
createLastSeg(!isX),
varVal,
])
return (args, referencedSegment) => {
const makeBinExp = (index: 0 | 1) => {
const arg = getArgLiteralVal(args?.[index])
return createBinaryExpressionWithUnary([
createSegEnd(referenceSegName, isX),
createLiteral(
roundOff(arg - (referencedSegment?.to?.[index] || 0), 2)
),
])
}
return createCallWrapper(
'lineTo',
isX ? [makeBinExp(0), varValBinExp] : [varValBinExp, makeBinExp(1)],
tag
)
}
}
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
const angleVal = [0, 90, 180, 270].includes(angle)
? createIdentifier(`_${angle}`)
: createLiteral(angle)
return intersectCallWrapper({
fnName: 'angledLineThatIntersects',
angleVal,
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,
})
}
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,
'+',
createIdentifier('_180'),
])
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
)
}
}
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),
},
setVertDistance: {
tooltip: 'lineTo',
createNode: setHorzVertDistanceConstraintLineCreateNode(false),
},
},
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),
},
setHorzDistance: {
tooltip: 'lineTo',
createNode: setHorzVertDistanceConstraintLineCreateNode(true),
},
},
free: {
equalLength: {
tooltip: 'angledLine',
createNode: basicAngledLineCreateNode('len'),
},
horizontal: {
tooltip: 'xLine',
createNode:
({ tag }) =>
(args) =>
createCallWrapper('xLine', args[0], tag),
},
vertical: {
tooltip: 'yLine',
createNode:
({ tag }) =>
(args) =>
createCallWrapper('yLine', args[1], tag),
},
setHorzDistance: {
tooltip: 'lineTo',
createNode: setHorzVertDistanceCreateNode('x'),
},
xAbs: {
tooltip: 'lineTo',
createNode: setAbsDistanceCreateNode('x'),
},
setVertDistance: {
tooltip: 'lineTo',
createNode: setHorzVertDistanceCreateNode('y'),
},
yAbs: {
tooltip: 'lineTo',
createNode: setAbsDistanceCreateNode('y'),
},
setAngle: {
tooltip: 'angledLine',
createNode: basicAngledLineCreateNode('none', 'ang'),
},
setLength: {
tooltip: 'angledLine',
createNode: basicAngledLineCreateNode('none', 'len'),
},
equalAngle: {
tooltip: 'angledLine',
createNode: basicAngledLineCreateNode('ang'),
},
intersect: {
tooltip: 'angledLineThatIntersects',
createNode: setAngledIntersectLineForLines,
},
setAngleBetween: {
tooltip: 'angledLine',
createNode: setAngleBetweenCreateNode('none'),
},
},
},
lineTo: {
free: {
equalLength: {
tooltip: 'angledLine',
createNode: basicAngledLineCreateNode('len'),
},
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),
},
setAngleBetween: {
tooltip: 'angledLineToX',
createNode: setAngleBetweenCreateNode('xAbs'),
},
},
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),
},
setAngle: {
tooltip: 'angledLineToY',
createNode:
({ varValB, tag, forceValueUsedInTransform }) =>
(args) => {
return createCallWrapper(
'angledLineToY',
[forceValueUsedInTransform || args[0], varValB],
tag,
getArgLiteralVal(args[0])
)
},
},
setAngleBetween: {
tooltip: 'angledLineToY',
createNode: setAngleBetweenCreateNode('yAbs'),
},
},
},
angledLine: {
angle: {
equalLength: {
tooltip: 'angledLine',
createNode:
({ referenceSegName, varValA, tag }) =>
() =>
createCallWrapper(
'angledLine',
[varValA, createSegLen(referenceSegName)],
tag
),
},
setLength: {
tooltip: 'angledLine',
createNode: basicAngledLineCreateNode('none', 'len', 'ang'),
},
setVertDistance: {
tooltip: 'angledLineToY',
createNode: setHorzVertDistanceForAngleLineCreateNode('y'),
},
yAbs: {
tooltip: 'angledLineToY',
createNode: setAbsDistanceForAngleLineCreateNode('y'),
},
setHorzDistance: {
tooltip: 'angledLineToX',
createNode: setHorzVertDistanceForAngleLineCreateNode('x'),
},
xAbs: {
tooltip: 'angledLineToX',
createNode: setAbsDistanceForAngleLineCreateNode('x'),
},
intersect: {
tooltip: 'angledLineThatIntersects',
createNode: setAngledIntersectForAngledLines,
},
},
free: {
equalLength: {
tooltip: 'angledLine',
createNode: basicAngledLineCreateNode('len'),
},
setLength: {
tooltip: 'angledLine',
createNode: basicAngledLineCreateNode('none', 'len'),
},
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)
},
},
setAngle: {
tooltip: 'angledLine',
createNode: basicAngledLineCreateNode('len', 'ang', 'len'),
},
equalAngle: {
tooltip: 'angledLine',
createNode: basicAngledLineCreateNode('ang', 'len', 'len'),
},
},
},
angledLineOfXLength: {
free: {
equalLength: {
tooltip: 'angledLine',
createNode: basicAngledLineCreateNode('len'),
},
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('len'),
},
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('len'),
},
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('len'),
},
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 }) =>
(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',
createNode: setHorVertDistanceForXYLines('x'),
},
setLength: {
tooltip: 'xLine',
createNode: xyLineSetLength('xLine'),
},
intersect: {
tooltip: 'angledLineThatIntersects',
createNode: setAngledIntersectLineForLines,
},
xAbs: {
tooltip: 'xLineTo',
createNode: setAbsDistanceCreateNode('x', true),
},
},
},
yLine: {
free: {
equalLength: {
tooltip: 'yLine',
createNode:
({ referenceSegName, tag }) =>
(arg) => {
const argVal = getArgLiteralVal(arg[0])
let segLen = createSegLen(referenceSegName) as BinaryPart
if (argVal < 0) segLen = createUnaryExpression(segLen)
return createCallWrapper('yLine', segLen, tag, argVal)
},
},
setLength: {
tooltip: 'yLine',
createNode: xyLineSetLength('yLine'),
},
setVertDistance: {
tooltip: 'yLineTo',
createNode: setHorVertDistanceForXYLines('y'),
},
intersect: {
tooltip: 'angledLineThatIntersects',
createNode: setAngledIntersectLineForLines,
},
yAbs: {
tooltip: 'yLineTo',
createNode: setAbsDistanceCreateNode('y', true),
},
},
},
xLineTo: {
free: {
equalLength: {
tooltip: 'xLine',
createNode:
({ referenceSegName, tag }) =>
() =>
createCallWrapper('xLine', createSegLen(referenceSegName), tag),
},
setLength: {
tooltip: 'xLine',
createNode: xyLineSetLength('xLine'),
},
},
},
yLineTo: {
free: {
equalLength: {
tooltip: 'yLine',
createNode:
({ referenceSegName, tag }) =>
() =>
createCallWrapper('yLine', createSegLen(referenceSegName), tag),
},
setLength: {
tooltip: 'yLine',
createNode: xyLineSetLength('yLine'),
},
},
},
}
export function getRemoveConstraintsTransform(
sketchFnExp: CallExpression,
constraintType: ConstraintType
): TransformInfo | false {
let name = sketchFnExp.callee.name as ToolTip
if (!toolTips.includes(name)) {
return false
}
const xyLineMap: {
[key in ToolTip]?: ToolTip
} = {
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)
if (isNotLiteralArrayOrStatic(firstArg.val)) {
return transformInfo
}
// check if the function has no constraints
const isTwoValFree =
Array.isArray(firstArg.val) && isLiteralArrayOrStatic(firstArg.val)
if (isTwoValFree) {
return false
}
const isOneValFree =
!Array.isArray(firstArg.val) && isLiteralArrayOrStatic(firstArg.val)
if (isOneValFree) {
return transformInfo
}
// check what constraints the function has
const lineInputType = getConstraintType(firstArg.val, name)
if (lineInputType) {
return transformInfo
}
return false
}
function getTransformMapPath(
sketchFnExp: CallExpression,
constraintType: ConstraintType
):
| {
toolTip: ToolTip
lineInputType: LineInputsType | 'free'
constraintType: ConstraintType
}
| false {
const name = sketchFnExp.callee.name as ToolTip
if (!toolTips.includes(name)) {
return false
}
// check if the function is locked down and so can't be transformed
const firstArg = getFirstArg(sketchFnExp)
if (isNotLiteralArrayOrStatic(firstArg.val)) {
return false
}
// check if the function has no constraints
if (isLiteralArrayOrStatic(firstArg.val)) {
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] | [Value, Value, Value],
fnName: ToolTip
): 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 = isNotLiteralArrayOrStatic(val[0])
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: Selections,
ast: Program,
constraintType: ConstraintType
): TransformInfo[] {
const paths = selectionRanges.codeBasedSelections.map(({ range }) =>
getNodePathFromSourceRange(ast, range)
)
const nodes = paths.map(
(pathToNode) =>
getNodeFromPath<Value>(ast, pathToNode, 'CallExpression').node
)
try {
const theTransforms = nodes.map((node) => {
if (node?.type === 'CallExpression')
return getTransformInfo(node, constraintType)
return false
}) as TransformInfo[]
return theTransforms
} catch (error) {
console.log('error', error)
return []
}
}
export function getRemoveConstraintsTransforms(
selectionRanges: Selections,
ast: Program,
constraintType: ConstraintType
): TransformInfo[] {
// return ()
const paths = selectionRanges.codeBasedSelections.map((selectionRange) =>
getNodePathFromSourceRange(ast, selectionRange.range)
)
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
}
export type PathToNodeMap = { [key: number]: PathToNode }
export function transformSecondarySketchLinesTagFirst({
ast,
selectionRanges,
transformInfos,
programMemory,
forceSegName,
forceValueUsedInTransform,
}: {
ast: Program
selectionRanges: Selections
transformInfos: TransformInfo[]
programMemory: ProgramMemory
forceSegName?: string
forceValueUsedInTransform?: Value
}): {
modifiedAst: Program
valueUsedInTransform?: number
pathToNodeMap: PathToNodeMap
tagInfo: {
tag: string
isTagExisting: boolean
}
} {
// let node = JSON.parse(JSON.stringify(ast))
const primarySelection = selectionRanges.codeBasedSelections[0].range
const { modifiedAst, tag, isTagExisting, pathToNode } = giveSketchFnCallTag(
ast,
primarySelection,
forceSegName
)
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
return {
...result,
pathToNodeMap: updatedPathToNodeMap,
tagInfo: {
tag,
isTagExisting,
},
}
}
function incrementPathToNodeMap(
pathToNodeMap: PathToNodeMap,
increment = 1
): PathToNodeMap {
const newMap: PathToNodeMap = {}
Object.entries(pathToNodeMap).forEach(([key, path]) => {
newMap[Number(key) + increment] = path
})
return newMap
}
export function transformAstSketchLines({
ast,
selectionRanges,
transformInfos,
programMemory,
referenceSegName,
forceValueUsedInTransform,
referencedSegmentRange,
}: {
ast: Program
selectionRanges: Selections
transformInfos: TransformInfo[]
programMemory: ProgramMemory
referenceSegName: string
forceValueUsedInTransform?: Value
referencedSegmentRange?: Selection['range']
}): {
modifiedAst: Program
valueUsedInTransform?: number
pathToNodeMap: PathToNodeMap
} {
// deep clone since we are mutating in a loop, of which any could fail
let node = JSON.parse(JSON.stringify(ast))
let _valueUsedInTransform // TODO should this be an array?
const pathToNodeMap: PathToNodeMap = {}
selectionRanges.codeBasedSelections.forEach(({ range }, index) => {
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 callExp = getNode<CallExpression>('CallExpression')?.node
const varDec = getNode<VariableDeclarator>('VariableDeclarator').node
const { val, tag: callBackTag } = getFirstArg(callExp)
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)) ||
''
const [varValA, varValB] = Array.isArray(val) ? val : [val, val]
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 referencedSegment = referencedSegmentRange
? getSketchSegmentFromSourceRange(sketchGroup, referencedSegmentRange)
.segment
: sketchGroup.value.find((path) => path.name === _referencedSegmentName)
const { to, from } = seg
const { modifiedAst, valueUsedInTransform, pathToNode } = replaceSketchLine(
{
node: node,
programMemory,
sourceRange: range,
referencedSegment,
fnName: transformTo || (callExp.callee.name as ToolTip),
to,
from,
createCallback: callBack({
referenceSegName: _referencedSegmentName,
varValA,
varValB,
tag: callBackTag,
forceValueUsedInTransform,
}),
}
)
node = modifiedAst
pathToNodeMap[index] = pathToNode
if (typeof valueUsedInTransform === 'number') {
_valueUsedInTransform = valueUsedInTransform
}
})
return {
modifiedAst: node,
valueUsedInTransform: _valueUsedInTransform,
pathToNodeMap,
}
}
function createSegLen(referenceSegName: string): Value {
return createCallExpression('segLen', [
createLiteral(referenceSegName),
createPipeSubstitution(),
])
}
function createSegAngle(referenceSegName: string): Value {
return createCallExpression('segAng', [
createLiteral(referenceSegName),
createPipeSubstitution(),
])
}
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
}
export function getConstraintLevelFromSourceRange(
cursorRange: Selection['range'],
ast: Program
): 'free' | 'partial' | 'full' {
const { node: sketchFnExp } = getNodeFromPath<CallExpression>(
ast,
getNodePathFromSourceRange(ast, cursorRange),
'CallExpression'
)
const name = sketchFnExp?.callee?.name as ToolTip
if (!toolTips.includes(name)) return 'free'
const firstArg = getFirstArg(sketchFnExp)
// check if the function is fully constrained
if (isNotLiteralArrayOrStatic(firstArg.val)) {
return 'full'
}
// check if the function has no constraints
const isTwoValFree =
Array.isArray(firstArg.val) && isLiteralArrayOrStatic(firstArg.val)
const isOneValFree =
!Array.isArray(firstArg.val) && isLiteralArrayOrStatic(firstArg.val)
if (isTwoValFree) return 'free'
if (isOneValFree) return 'partial'
return 'partial'
}
export function isLiteralArrayOrStatic(
val: Value | [Value, Value] | [Value, Value, Value] | undefined
): boolean {
if (!val) return false
if (Array.isArray(val)) {
const [a, b] = val
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)) {
const [a, b] = val
return isNotLiteralArrayOrStatic(a) && isNotLiteralArrayOrStatic(b)
}
return (
(val.type !== 'Literal' && val.type !== 'UnaryExpression') ||
(val.type === 'UnaryExpression' && val.argument.type !== 'Literal')
)
}