2024-05-24 20:54:42 +10:00
|
|
|
import { useRef, useEffect, useState, useMemo, Fragment } from 'react'
|
2024-02-14 08:03:20 +11:00
|
|
|
import { useModelingContext } from 'hooks/useModelingContext'
|
|
|
|
|
|
|
|
import { cameraMouseDragGuards } from 'lib/cameraControls'
|
2024-03-11 20:26:13 -04:00
|
|
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
2024-04-04 11:07:51 +11:00
|
|
|
import { ARROWHEAD, DEBUG_SHOW_BOTH_SCENES } from './sceneInfra'
|
2024-02-26 19:53:44 +11:00
|
|
|
import { ReactCameraProperties } from './CameraControls'
|
2024-09-09 18:17:45 -04:00
|
|
|
import { throttle, toSync } from 'lib/utils'
|
2024-05-24 20:54:42 +10:00
|
|
|
import {
|
|
|
|
sceneInfra,
|
|
|
|
kclManager,
|
|
|
|
codeManager,
|
|
|
|
editorManager,
|
|
|
|
sceneEntitiesManager,
|
|
|
|
engineCommandManager,
|
|
|
|
} from 'lib/singletons'
|
2024-04-04 11:07:51 +11:00
|
|
|
import {
|
|
|
|
EXTRA_SEGMENT_HANDLE,
|
|
|
|
PROFILE_START,
|
|
|
|
getParentGroup,
|
|
|
|
} from './sceneEntities'
|
2024-05-24 20:54:42 +10:00
|
|
|
import { SegmentOverlay, SketchDetails } from 'machines/modelingMachine'
|
|
|
|
import { findUsesOfTagInPipe, getNodeFromPath } from 'lang/queryAst'
|
|
|
|
import {
|
|
|
|
CallExpression,
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
CallExpressionKw,
|
2024-05-24 20:54:42 +10:00
|
|
|
PathToNode,
|
|
|
|
Program,
|
2024-08-12 15:38:42 -05:00
|
|
|
Expr,
|
2024-05-24 20:54:42 +10:00
|
|
|
parse,
|
|
|
|
recast,
|
2024-12-06 13:57:31 +13:00
|
|
|
defaultSourceRange,
|
|
|
|
resultIsOk,
|
2025-01-17 14:34:36 -05:00
|
|
|
topLevelRange,
|
2024-05-24 20:54:42 +10:00
|
|
|
} from 'lang/wasm'
|
|
|
|
import { CustomIcon, CustomIconName } from 'components/CustomIcon'
|
|
|
|
import { ConstrainInfo } from 'lang/std/stdTypes'
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
import { getConstraintInfo, getConstraintInfoKw } from 'lang/std/sketch'
|
2024-05-24 20:54:42 +10:00
|
|
|
import { Dialog, Popover, Transition } from '@headlessui/react'
|
|
|
|
import toast from 'react-hot-toast'
|
|
|
|
import { InstanceProps, create } from 'react-modal-promise'
|
2024-07-02 17:16:27 +10:00
|
|
|
import { executeAst } from 'lang/langHelpers'
|
2024-05-24 20:54:42 +10:00
|
|
|
import {
|
|
|
|
deleteSegmentFromPipeExpression,
|
|
|
|
removeSingleConstraintInfo,
|
|
|
|
} from 'lang/modifyAst'
|
|
|
|
import { ActionButton } from 'components/ActionButton'
|
2024-09-09 18:17:45 -04:00
|
|
|
import { err, reportRejection, trap } from 'lib/trap'
|
2024-10-30 16:52:17 -04:00
|
|
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
2025-01-23 10:25:21 -05:00
|
|
|
import { commandBarActor } from 'machines/commandBarMachine'
|
2024-02-14 08:03:20 +11:00
|
|
|
|
|
|
|
function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } {
|
|
|
|
const [isCamMoving, setIsCamMoving] = useState(false)
|
|
|
|
const [isTween, setIsTween] = useState(false)
|
|
|
|
|
|
|
|
const { state } = useModelingContext()
|
|
|
|
|
|
|
|
useEffect(() => {
|
2024-02-26 19:53:44 +11:00
|
|
|
sceneInfra.camControls.setIsCamMovingCallback((isMoving, isTween) => {
|
2024-02-14 08:03:20 +11:00
|
|
|
setIsCamMoving(isMoving)
|
|
|
|
setIsTween(isTween)
|
|
|
|
})
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
if (DEBUG_SHOW_BOTH_SCENES || !isCamMoving)
|
|
|
|
return { hideClient: false, hideServer: false }
|
2024-02-17 07:04:24 +11:00
|
|
|
let hideServer = state.matches('Sketch')
|
2024-02-14 08:03:20 +11:00
|
|
|
if (isTween) {
|
|
|
|
hideServer = false
|
|
|
|
}
|
|
|
|
|
|
|
|
return { hideClient: !hideServer, hideServer }
|
|
|
|
}
|
|
|
|
|
|
|
|
export const ClientSideScene = ({
|
|
|
|
cameraControls,
|
|
|
|
}: {
|
|
|
|
cameraControls: ReturnType<
|
2024-03-11 20:26:13 -04:00
|
|
|
typeof useSettingsAuthContext
|
2024-04-02 10:29:34 -04:00
|
|
|
>['settings']['context']['modeling']['mouseControls']['current']
|
2024-02-14 08:03:20 +11:00
|
|
|
}) => {
|
|
|
|
const canvasRef = useRef<HTMLDivElement>(null)
|
2024-04-04 11:07:51 +11:00
|
|
|
const { state, send, context } = useModelingContext()
|
2024-02-14 08:03:20 +11:00
|
|
|
const { hideClient, hideServer } = useShouldHideScene()
|
|
|
|
|
|
|
|
// Listen for changes to the camera controls setting
|
|
|
|
// and update the client-side scene's controls accordingly.
|
|
|
|
useEffect(() => {
|
2024-02-26 19:53:44 +11:00
|
|
|
sceneInfra.camControls.interactionGuards =
|
|
|
|
cameraMouseDragGuards[cameraControls]
|
2024-02-14 08:03:20 +11:00
|
|
|
}, [cameraControls])
|
|
|
|
useEffect(() => {
|
|
|
|
sceneInfra.updateOtherSelectionColors(
|
|
|
|
state?.context?.selectionRanges?.otherSelections || []
|
|
|
|
)
|
|
|
|
}, [state?.context?.selectionRanges?.otherSelections])
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (!canvasRef.current) return
|
|
|
|
const canvas = canvasRef.current
|
|
|
|
canvas.appendChild(sceneInfra.renderer.domElement)
|
2024-07-08 16:41:00 -04:00
|
|
|
canvas.appendChild(sceneInfra.labelRenderer.domElement)
|
2024-02-14 08:03:20 +11:00
|
|
|
sceneInfra.animate()
|
2024-09-23 22:42:51 +10:00
|
|
|
canvas.addEventListener(
|
|
|
|
'mousemove',
|
|
|
|
toSync(sceneInfra.onMouseMove, reportRejection),
|
|
|
|
false
|
|
|
|
)
|
2024-02-14 08:03:20 +11:00
|
|
|
canvas.addEventListener('mousedown', sceneInfra.onMouseDown, false)
|
2024-09-23 22:42:51 +10:00
|
|
|
canvas.addEventListener(
|
|
|
|
'mouseup',
|
|
|
|
toSync(sceneInfra.onMouseUp, reportRejection),
|
|
|
|
false
|
|
|
|
)
|
2024-02-14 08:03:20 +11:00
|
|
|
sceneInfra.setSend(send)
|
2024-08-03 07:05:35 +10:00
|
|
|
engineCommandManager.modelingSend = send
|
2024-02-14 08:03:20 +11:00
|
|
|
return () => {
|
2024-09-23 22:42:51 +10:00
|
|
|
canvas?.removeEventListener(
|
|
|
|
'mousemove',
|
|
|
|
toSync(sceneInfra.onMouseMove, reportRejection)
|
|
|
|
)
|
2024-02-14 08:03:20 +11:00
|
|
|
canvas?.removeEventListener('mousedown', sceneInfra.onMouseDown)
|
2024-09-23 22:42:51 +10:00
|
|
|
canvas?.removeEventListener(
|
|
|
|
'mouseup',
|
|
|
|
toSync(sceneInfra.onMouseUp, reportRejection)
|
|
|
|
)
|
2025-02-15 00:57:04 +11:00
|
|
|
sceneEntitiesManager.tearDownSketch({ removeAxis: true })
|
2024-02-14 08:03:20 +11:00
|
|
|
}
|
|
|
|
}, [])
|
|
|
|
|
2024-04-04 11:07:51 +11:00
|
|
|
let cursor = 'default'
|
|
|
|
if (state.matches('Sketch')) {
|
|
|
|
if (
|
|
|
|
context.mouseState.type === 'isHovering' &&
|
|
|
|
getParentGroup(context.mouseState.on, [
|
|
|
|
ARROWHEAD,
|
|
|
|
EXTRA_SEGMENT_HANDLE,
|
|
|
|
PROFILE_START,
|
|
|
|
])
|
|
|
|
) {
|
|
|
|
cursor = 'move'
|
|
|
|
} else if (context.mouseState.type === 'isDragging') {
|
|
|
|
cursor = 'grabbing'
|
|
|
|
} else if (
|
2024-09-09 19:59:36 +03:00
|
|
|
state.matches({ Sketch: 'Line tool' }) ||
|
|
|
|
state.matches({ Sketch: 'Tangential arc to' }) ||
|
2024-09-23 22:42:51 +10:00
|
|
|
state.matches({ Sketch: 'Rectangle tool' }) ||
|
2025-02-15 00:57:04 +11:00
|
|
|
state.matches({ Sketch: 'Circle tool' }) ||
|
|
|
|
state.matches({ Sketch: 'Circle three point tool' })
|
2024-04-04 11:07:51 +11:00
|
|
|
) {
|
|
|
|
cursor = 'crosshair'
|
|
|
|
} else {
|
|
|
|
cursor = 'default'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-14 08:03:20 +11:00
|
|
|
return (
|
2024-05-24 20:54:42 +10:00
|
|
|
<>
|
|
|
|
<div
|
|
|
|
ref={canvasRef}
|
|
|
|
style={{ cursor: cursor }}
|
2024-06-18 16:08:41 +10:00
|
|
|
data-testid="client-side-scene"
|
2024-05-24 20:54:42 +10:00
|
|
|
className={`absolute inset-0 h-full w-full transition-all duration-300 ${
|
|
|
|
hideClient ? 'opacity-0' : 'opacity-100'
|
|
|
|
} ${hideServer ? 'bg-chalkboard-10 dark:bg-chalkboard-100' : ''} ${
|
|
|
|
!hideClient && !hideServer && state.matches('Sketch')
|
|
|
|
? 'bg-chalkboard-10/80 dark:bg-chalkboard-100/80'
|
|
|
|
: ''
|
|
|
|
}`}
|
|
|
|
></div>
|
|
|
|
<Overlays />
|
|
|
|
</>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const Overlays = () => {
|
|
|
|
const { context } = useModelingContext()
|
|
|
|
if (context.mouseState.type === 'isDragging') return null
|
2024-12-17 15:12:18 -05:00
|
|
|
// Set a large zIndex, the overlay for hover dropdown menu on line segments needs to render
|
|
|
|
// over the length labels on the line segments
|
2024-05-24 20:54:42 +10:00
|
|
|
return (
|
2025-02-20 13:53:35 -05:00
|
|
|
<div className="absolute inset-0 pointer-events-none z-sketchOverlayDropdown">
|
2024-05-24 20:54:42 +10:00
|
|
|
{Object.entries(context.segmentOverlays)
|
2025-02-15 00:57:04 +11:00
|
|
|
.flatMap((a) =>
|
|
|
|
a[1].map((b) => ({ pathToNodeString: a[0], overlay: b }))
|
|
|
|
)
|
|
|
|
.filter((a) => a.overlay.visible)
|
|
|
|
.map(({ pathToNodeString, overlay }, index) => {
|
2024-05-24 20:54:42 +10:00
|
|
|
return (
|
|
|
|
<Overlay
|
|
|
|
overlay={overlay}
|
2025-02-15 00:57:04 +11:00
|
|
|
key={pathToNodeString + String(index)}
|
2024-05-24 20:54:42 +10:00
|
|
|
pathToNodeString={pathToNodeString}
|
|
|
|
overlayIndex={index}
|
|
|
|
/>
|
|
|
|
)
|
|
|
|
})}
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const Overlay = ({
|
|
|
|
overlay,
|
|
|
|
overlayIndex,
|
|
|
|
pathToNodeString,
|
|
|
|
}: {
|
|
|
|
overlay: SegmentOverlay
|
|
|
|
overlayIndex: number
|
|
|
|
pathToNodeString: string
|
|
|
|
}) => {
|
|
|
|
const { context, send, state } = useModelingContext()
|
|
|
|
let xAlignment = overlay.angle < 0 ? '0%' : '-100%'
|
|
|
|
let yAlignment = overlay.angle < -90 || overlay.angle >= 90 ? '0%' : '-100%'
|
|
|
|
|
2024-11-16 16:49:44 -05:00
|
|
|
// It's possible for the pathToNode to request a newer AST node
|
|
|
|
// than what's available in the AST at the moment of query.
|
|
|
|
// It eventually settles on being updated.
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
const _node1 = getNodeFromPath<Node<CallExpression | CallExpressionKw>>(
|
2024-05-24 20:54:42 +10:00
|
|
|
kclManager.ast,
|
|
|
|
overlay.pathToNode,
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
['CallExpression', 'CallExpressionKw']
|
2024-06-24 11:45:40 -04:00
|
|
|
)
|
2024-11-16 16:49:44 -05:00
|
|
|
|
|
|
|
// For that reason, to prevent console noise, we do not use err here.
|
|
|
|
if (_node1 instanceof Error) {
|
|
|
|
console.warn('ast older than pathToNode, not fatal, eventually settles', '')
|
|
|
|
return
|
|
|
|
}
|
2024-06-24 11:45:40 -04:00
|
|
|
const callExpression = _node1.node
|
|
|
|
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
const constraints =
|
|
|
|
callExpression.type === 'CallExpression'
|
2025-02-15 00:57:04 +11:00
|
|
|
? getConstraintInfo(
|
|
|
|
callExpression,
|
|
|
|
codeManager.code,
|
|
|
|
overlay.pathToNode,
|
|
|
|
overlay.filterValue
|
|
|
|
)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
: getConstraintInfoKw(
|
|
|
|
callExpression,
|
|
|
|
codeManager.code,
|
2025-02-15 00:57:04 +11:00
|
|
|
overlay.pathToNode,
|
|
|
|
overlay.filterValue
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
)
|
2024-05-24 20:54:42 +10:00
|
|
|
|
|
|
|
const offset = 20 // px
|
|
|
|
// We could put a boolean in settings that
|
|
|
|
const offsetAngle = 90
|
|
|
|
|
|
|
|
const xOffset =
|
|
|
|
Math.cos(((overlay.angle + offsetAngle) * Math.PI) / 180) * offset
|
|
|
|
const yOffset =
|
|
|
|
Math.sin(((overlay.angle + offsetAngle) * Math.PI) / 180) * offset
|
|
|
|
|
|
|
|
const shouldShow =
|
|
|
|
overlay.visible &&
|
|
|
|
typeof context?.segmentHoverMap?.[pathToNodeString] === 'number' &&
|
|
|
|
!(
|
2024-09-09 19:59:36 +03:00
|
|
|
state.matches({ Sketch: 'Line tool' }) ||
|
|
|
|
state.matches({ Sketch: 'Tangential arc to' }) ||
|
|
|
|
state.matches({ Sketch: 'Rectangle tool' })
|
2024-05-24 20:54:42 +10:00
|
|
|
)
|
|
|
|
return (
|
|
|
|
<div className={`absolute w-0 h-0`}>
|
|
|
|
<div
|
|
|
|
data-testid="segment-overlay"
|
|
|
|
data-path-to-node={pathToNodeString}
|
|
|
|
data-overlay-index={overlayIndex}
|
2024-06-24 11:45:40 -04:00
|
|
|
data-overlay-visible={shouldShow}
|
2024-05-24 20:54:42 +10:00
|
|
|
data-overlay-angle={overlay.angle}
|
|
|
|
className="pointer-events-auto absolute w-0 h-0"
|
|
|
|
style={{
|
|
|
|
transform: `translate3d(${overlay.windowCoords[0]}px, ${overlay.windowCoords[1]}px, 0)`,
|
|
|
|
}}
|
|
|
|
></div>
|
|
|
|
{shouldShow && (
|
|
|
|
<div
|
2024-06-24 11:45:40 -04:00
|
|
|
data-overlay-toolbar-index={overlayIndex}
|
2024-05-24 20:54:42 +10:00
|
|
|
className={`px-0 pointer-events-auto absolute flex gap-1`}
|
|
|
|
style={{
|
|
|
|
transform: `translate3d(calc(${
|
|
|
|
overlay.windowCoords[0] + xOffset
|
|
|
|
}px + ${xAlignment}), calc(${
|
|
|
|
overlay.windowCoords[1] - yOffset
|
|
|
|
}px + ${yAlignment}), 0)`,
|
|
|
|
}}
|
|
|
|
onMouseEnter={() =>
|
|
|
|
send({
|
|
|
|
type: 'Set mouse state',
|
|
|
|
data: {
|
|
|
|
type: 'isHovering',
|
|
|
|
on: overlay.group,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
onMouseLeave={() =>
|
|
|
|
send({
|
|
|
|
type: 'Set mouse state',
|
|
|
|
data: { type: 'idle' },
|
|
|
|
})
|
|
|
|
}
|
|
|
|
>
|
|
|
|
{constraints &&
|
|
|
|
constraints.map((constraintInfo, i) => (
|
|
|
|
<ConstraintSymbol
|
|
|
|
constrainInfo={constraintInfo}
|
|
|
|
key={i}
|
|
|
|
verticalPosition={
|
|
|
|
overlay.windowCoords[1] > window.innerHeight / 2
|
|
|
|
? 'top'
|
|
|
|
: 'bottom'
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
))}
|
2024-09-23 22:42:51 +10:00
|
|
|
{/* delete circle is complicated by the fact it's the only segment in the
|
|
|
|
pipe expression. Maybe it should delete the entire pipeExpression, however
|
|
|
|
this will likely change soon when we implement multi-profile so we'll leave it for now
|
|
|
|
issue: https://github.com/KittyCAD/modeling-app/issues/3910
|
|
|
|
*/}
|
2025-02-15 00:57:04 +11:00
|
|
|
{callExpression?.callee?.name !== 'circle' &&
|
|
|
|
callExpression?.callee?.name !== 'circleThreePoint' && (
|
|
|
|
<SegmentMenu
|
|
|
|
verticalPosition={
|
|
|
|
overlay.windowCoords[1] > window.innerHeight / 2
|
|
|
|
? 'top'
|
|
|
|
: 'bottom'
|
|
|
|
}
|
|
|
|
pathToNode={overlay.pathToNode}
|
|
|
|
stdLibFnName={constraints[0]?.stdLibFnName}
|
|
|
|
/>
|
|
|
|
)}
|
2024-05-24 20:54:42 +10:00
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
type ConfirmModalProps = InstanceProps<boolean, boolean> & { text: string }
|
|
|
|
|
|
|
|
export const ConfirmModal = ({
|
|
|
|
isOpen,
|
|
|
|
onResolve,
|
|
|
|
onReject,
|
|
|
|
text,
|
|
|
|
}: ConfirmModalProps) => {
|
|
|
|
return (
|
|
|
|
<Transition appear show={isOpen} as={Fragment}>
|
|
|
|
<Dialog
|
|
|
|
as="div"
|
|
|
|
className="relative z-10"
|
|
|
|
onClose={() => onResolve(false)}
|
|
|
|
>
|
|
|
|
<Transition.Child
|
|
|
|
as={Fragment}
|
|
|
|
enter="ease-out duration-300"
|
|
|
|
enterFrom="opacity-0"
|
|
|
|
enterTo="opacity-100"
|
|
|
|
leave="ease-in duration-200"
|
|
|
|
leaveFrom="opacity-100"
|
|
|
|
leaveTo="opacity-0"
|
|
|
|
>
|
|
|
|
<div className="fixed inset-0 bg-black bg-opacity-25" />
|
|
|
|
</Transition.Child>
|
|
|
|
|
|
|
|
<div className="fixed inset-0 overflow-y-auto">
|
|
|
|
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
|
|
|
<Transition.Child
|
|
|
|
as={Fragment}
|
|
|
|
enter="ease-out duration-300"
|
|
|
|
enterFrom="opacity-0 scale-95"
|
|
|
|
enterTo="opacity-100 scale-100"
|
|
|
|
leave="ease-in duration-200"
|
|
|
|
leaveFrom="opacity-100 scale-100"
|
|
|
|
leaveTo="opacity-0 scale-95"
|
|
|
|
>
|
|
|
|
<Dialog.Panel className="rounded relative mx-auto px-4 py-8 bg-chalkboard-10 dark:bg-chalkboard-100 border dark:border-chalkboard-70 max-w-xl w-full shadow-lg">
|
|
|
|
<div>{text}</div>
|
|
|
|
<div className="mt-8 flex justify-between">
|
|
|
|
<ActionButton
|
|
|
|
Element="button"
|
|
|
|
onClick={() => onResolve(true)}
|
|
|
|
>
|
|
|
|
Continue and unconstrain
|
|
|
|
</ActionButton>
|
|
|
|
<ActionButton
|
|
|
|
Element="button"
|
|
|
|
onClick={() => onReject(false)}
|
|
|
|
>
|
|
|
|
Cancel
|
|
|
|
</ActionButton>
|
|
|
|
</div>
|
|
|
|
</Dialog.Panel>
|
|
|
|
</Transition.Child>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</Dialog>
|
|
|
|
</Transition>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export const confirmModal = create<ConfirmModalProps, boolean, boolean>(
|
|
|
|
ConfirmModal
|
|
|
|
)
|
|
|
|
|
|
|
|
export async function deleteSegment({
|
|
|
|
pathToNode,
|
|
|
|
sketchDetails,
|
|
|
|
}: {
|
|
|
|
pathToNode: PathToNode
|
|
|
|
sketchDetails: SketchDetails | null
|
|
|
|
}) {
|
2024-10-30 16:52:17 -04:00
|
|
|
let modifiedAst: Node<Program> | Error = kclManager.ast
|
2024-05-24 20:54:42 +10:00
|
|
|
const dependentRanges = findUsesOfTagInPipe(modifiedAst, pathToNode)
|
|
|
|
|
|
|
|
const shouldContinueSegDelete = dependentRanges.length
|
|
|
|
? await confirmModal({
|
|
|
|
text: `At least ${dependentRanges.length} segment rely on the segment you're deleting.\nDo you want to continue and unconstrain these segments?`,
|
|
|
|
isOpen: true,
|
|
|
|
})
|
|
|
|
: true
|
|
|
|
|
|
|
|
if (!shouldContinueSegDelete) return
|
2024-06-24 11:45:40 -04:00
|
|
|
|
2024-05-24 20:54:42 +10:00
|
|
|
modifiedAst = deleteSegmentFromPipeExpression(
|
|
|
|
dependentRanges,
|
|
|
|
modifiedAst,
|
2025-02-12 10:22:56 +13:00
|
|
|
kclManager.variables,
|
2024-05-24 20:54:42 +10:00
|
|
|
codeManager.code,
|
|
|
|
pathToNode
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(modifiedAst)) return Promise.reject(modifiedAst)
|
2024-05-24 20:54:42 +10:00
|
|
|
|
|
|
|
const newCode = recast(modifiedAst)
|
2024-12-06 13:57:31 +13:00
|
|
|
const pResult = parse(newCode)
|
|
|
|
if (err(pResult) || !resultIsOk(pResult)) return Promise.reject(pResult)
|
|
|
|
modifiedAst = pResult.program
|
2024-06-24 11:45:40 -04:00
|
|
|
|
2024-05-24 20:54:42 +10:00
|
|
|
const testExecute = await executeAst({
|
|
|
|
ast: modifiedAst,
|
|
|
|
engineCommandManager: engineCommandManager,
|
2025-02-12 10:22:56 +13:00
|
|
|
isMock: true,
|
|
|
|
usePrevMemory: false,
|
2024-05-24 20:54:42 +10:00
|
|
|
})
|
|
|
|
if (testExecute.errors.length) {
|
|
|
|
toast.error('Segment tag used outside of current Sketch. Could not delete.')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!sketchDetails) return
|
2024-06-24 11:45:40 -04:00
|
|
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
|
|
|
pathToNode,
|
2025-02-15 00:57:04 +11:00
|
|
|
sketchDetails.sketchNodePaths,
|
|
|
|
sketchDetails.planeNodePath,
|
2024-05-24 20:54:42 +10:00
|
|
|
modifiedAst,
|
|
|
|
sketchDetails.zAxis,
|
|
|
|
sketchDetails.yAxis,
|
|
|
|
sketchDetails.origin
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
|
|
|
|
// Now 'Set sketchDetails' is called with the modified pathToNode
|
2024-05-24 20:54:42 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
const SegmentMenu = ({
|
|
|
|
verticalPosition,
|
|
|
|
pathToNode,
|
|
|
|
stdLibFnName,
|
|
|
|
}: {
|
|
|
|
verticalPosition: 'top' | 'bottom'
|
|
|
|
pathToNode: PathToNode
|
|
|
|
stdLibFnName: string
|
|
|
|
}) => {
|
|
|
|
const { send } = useModelingContext()
|
|
|
|
const dependentSourceRanges = findUsesOfTagInPipe(kclManager.ast, pathToNode)
|
|
|
|
return (
|
|
|
|
<Popover className="relative">
|
|
|
|
{({ open }) => (
|
|
|
|
<>
|
|
|
|
<Popover.Button
|
|
|
|
data-testid="overlay-menu"
|
|
|
|
data-stdlib-fn-name={stdLibFnName}
|
|
|
|
className="bg-chalkboard-10 dark:bg-chalkboard-100 border !border-transparent hover:!border-chalkboard-40 dark:hover:!border-chalkboard-70 ui-open:!border-chalkboard-40 dark:ui-open:!border-chalkboard-70 h-[26px] w-[26px] rounded-sm p-0 m-0"
|
|
|
|
>
|
|
|
|
<CustomIcon name={'three-dots'} />
|
|
|
|
</Popover.Button>
|
|
|
|
<Popover.Panel
|
|
|
|
as="menu"
|
|
|
|
className={`absolute ${
|
|
|
|
verticalPosition === 'top' ? 'bottom-full' : 'top-full'
|
|
|
|
} z-10 w-36 flex flex-col gap-1 divide-y divide-chalkboard-20 dark:divide-chalkboard-70 align-stretch px-0 py-1 bg-chalkboard-10 dark:bg-chalkboard-100 rounded-sm shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50`}
|
|
|
|
>
|
|
|
|
<button
|
|
|
|
className="!border-transparent rounded-sm text-left p-1 text-nowrap"
|
2024-06-04 16:29:20 +10:00
|
|
|
onClick={() => {
|
|
|
|
send({ type: 'Constrain remove constraints', data: pathToNode })
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
Remove constraints
|
|
|
|
</button>
|
|
|
|
<button
|
|
|
|
className="!border-transparent rounded-sm text-left p-1 text-nowrap"
|
2024-05-24 20:54:42 +10:00
|
|
|
title={
|
|
|
|
dependentSourceRanges.length > 0
|
|
|
|
? `At least ${dependentSourceRanges.length} segment rely on this segment's tag.`
|
|
|
|
: ''
|
|
|
|
}
|
|
|
|
onClick={() => {
|
|
|
|
send({ type: 'Delete segment', data: pathToNode })
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
Delete Segment
|
|
|
|
</button>
|
|
|
|
</Popover.Panel>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
</Popover>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const ConstraintSymbol = ({
|
|
|
|
constrainInfo: { type: _type, isConstrained, value, pathToNode, argPosition },
|
|
|
|
verticalPosition,
|
|
|
|
}: {
|
|
|
|
constrainInfo: ConstrainInfo
|
|
|
|
verticalPosition: 'top' | 'bottom'
|
|
|
|
}) => {
|
2024-12-09 16:43:58 -05:00
|
|
|
const { context } = useModelingContext()
|
2024-05-24 20:54:42 +10:00
|
|
|
const varNameMap: {
|
|
|
|
[key in ConstrainInfo['type']]: {
|
|
|
|
varName: string
|
|
|
|
displayName: string
|
|
|
|
iconName: CustomIconName
|
|
|
|
implicitConstraintDesc?: string
|
|
|
|
}
|
|
|
|
} = {
|
|
|
|
xRelative: {
|
|
|
|
varName: 'xRel',
|
|
|
|
displayName: 'X Relative',
|
|
|
|
iconName: 'xRelative',
|
|
|
|
},
|
|
|
|
xAbsolute: {
|
|
|
|
varName: 'xAbs',
|
|
|
|
displayName: 'X Absolute',
|
|
|
|
iconName: 'xAbsolute',
|
|
|
|
},
|
|
|
|
yRelative: {
|
|
|
|
varName: 'yRel',
|
|
|
|
displayName: 'Y Relative',
|
|
|
|
iconName: 'yRelative',
|
|
|
|
},
|
|
|
|
yAbsolute: {
|
|
|
|
varName: 'yAbs',
|
|
|
|
displayName: 'Y Absolute',
|
|
|
|
iconName: 'yAbsolute',
|
|
|
|
},
|
|
|
|
angle: {
|
|
|
|
varName: 'angle',
|
|
|
|
displayName: 'Angle',
|
|
|
|
iconName: 'angle',
|
|
|
|
},
|
|
|
|
length: {
|
|
|
|
varName: 'len',
|
|
|
|
displayName: 'Length',
|
|
|
|
iconName: 'dimension',
|
|
|
|
},
|
|
|
|
intersectionOffset: {
|
|
|
|
varName: 'perpDist',
|
|
|
|
displayName: 'Intersection Offset',
|
|
|
|
iconName: 'intersection-offset',
|
|
|
|
},
|
2024-09-23 22:42:51 +10:00
|
|
|
radius: {
|
|
|
|
varName: 'radius',
|
|
|
|
displayName: 'Radius',
|
|
|
|
iconName: 'dimension',
|
|
|
|
},
|
2024-05-24 20:54:42 +10:00
|
|
|
|
|
|
|
// implicit constraints
|
|
|
|
vertical: {
|
|
|
|
varName: '',
|
|
|
|
displayName: '',
|
|
|
|
iconName: 'vertical',
|
|
|
|
implicitConstraintDesc: 'vertically',
|
|
|
|
},
|
|
|
|
horizontal: {
|
|
|
|
varName: '',
|
|
|
|
displayName: '',
|
|
|
|
iconName: 'horizontal',
|
|
|
|
implicitConstraintDesc: 'horizontally',
|
|
|
|
},
|
|
|
|
tangentialWithPrevious: {
|
|
|
|
varName: '',
|
|
|
|
displayName: '',
|
|
|
|
iconName: 'tangent',
|
|
|
|
implicitConstraintDesc: 'tangential to previous segment',
|
|
|
|
},
|
|
|
|
|
|
|
|
// we don't render this one
|
|
|
|
intersectionTag: {
|
|
|
|
varName: '',
|
|
|
|
displayName: '',
|
|
|
|
iconName: 'dimension',
|
|
|
|
},
|
|
|
|
}
|
2024-09-13 21:14:14 +10:00
|
|
|
const varName = varNameMap?.[_type]?.varName || 'var'
|
|
|
|
const name: CustomIconName = varNameMap[_type].iconName
|
|
|
|
const displayName = varNameMap[_type]?.displayName
|
|
|
|
const implicitDesc = varNameMap[_type]?.implicitConstraintDesc
|
2024-05-24 20:54:42 +10:00
|
|
|
|
2024-06-24 11:45:40 -04:00
|
|
|
const _node = useMemo(
|
2024-08-12 15:38:42 -05:00
|
|
|
() => getNodeFromPath<Expr>(kclManager.ast, pathToNode),
|
2024-05-24 20:54:42 +10:00
|
|
|
[kclManager.ast, pathToNode]
|
|
|
|
)
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(_node)) return
|
|
|
|
const node = _node.node
|
|
|
|
|
2025-01-17 14:34:36 -05:00
|
|
|
const range = node
|
|
|
|
? topLevelRange(node.start, node.end)
|
2024-12-06 13:57:31 +13:00
|
|
|
: defaultSourceRange()
|
2024-05-24 20:54:42 +10:00
|
|
|
|
|
|
|
if (_type === 'intersectionTag') return null
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="relative group">
|
|
|
|
<button
|
|
|
|
data-testid="constraint-symbol"
|
|
|
|
data-is-implicit-constraint={implicitDesc ? 'true' : 'false'}
|
|
|
|
data-constraint-type={_type}
|
|
|
|
data-is-constrained={isConstrained ? 'true' : 'false'}
|
|
|
|
className={`${
|
|
|
|
implicitDesc
|
|
|
|
? 'bg-chalkboard-10 dark:bg-chalkboard-100 border-transparent border-0 rounded'
|
|
|
|
: isConstrained
|
|
|
|
? 'bg-chalkboard-10 dark:bg-chalkboard-90 dark:hover:bg-chalkboard-80 border-chalkboard-40 dark:border-chalkboard-70 rounded-sm'
|
|
|
|
: 'bg-primary/30 dark:bg-primary text-primary dark:text-chalkboard-10 dark:border-transparent group-hover:bg-primary/40 group-hover:border-primary/50 group-hover:brightness-125'
|
|
|
|
} h-[26px] w-[26px] rounded-sm relative m-0 p-0`}
|
|
|
|
onMouseEnter={() => {
|
2024-08-03 18:08:51 +10:00
|
|
|
editorManager.setHighlightRange([range])
|
2024-05-24 20:54:42 +10:00
|
|
|
}}
|
|
|
|
onMouseLeave={() => {
|
2024-12-06 13:57:31 +13:00
|
|
|
editorManager.setHighlightRange([defaultSourceRange()])
|
2024-05-24 20:54:42 +10:00
|
|
|
}}
|
|
|
|
// disabled={isConstrained || !convertToVarEnabled}
|
|
|
|
// disabled={implicitDesc} TODO why does this change styles that are hard to override?
|
2024-09-09 18:17:45 -04:00
|
|
|
onClick={toSync(async () => {
|
2024-05-24 20:54:42 +10:00
|
|
|
if (!isConstrained) {
|
2025-01-23 10:25:21 -05:00
|
|
|
commandBarActor.send({
|
2024-12-09 16:43:58 -05:00
|
|
|
type: 'Find and select command',
|
2024-05-24 20:54:42 +10:00
|
|
|
data: {
|
2024-12-09 16:43:58 -05:00
|
|
|
name: 'Constrain with named value',
|
|
|
|
groupId: 'modeling',
|
|
|
|
argDefaultValues: {
|
|
|
|
currentValue: {
|
|
|
|
pathToNode,
|
|
|
|
variableName: varName,
|
|
|
|
valueText: value,
|
|
|
|
},
|
|
|
|
},
|
2024-05-24 20:54:42 +10:00
|
|
|
},
|
|
|
|
})
|
|
|
|
} else if (isConstrained) {
|
|
|
|
try {
|
2024-12-06 13:57:31 +13:00
|
|
|
const pResult = parse(recast(kclManager.ast))
|
|
|
|
if (trap(pResult) || !resultIsOk(pResult))
|
|
|
|
return Promise.reject(pResult)
|
|
|
|
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
const _node1 = getNodeFromPath<CallExpression | CallExpressionKw>(
|
2024-12-06 13:57:31 +13:00
|
|
|
pResult.program!,
|
2024-05-24 20:54:42 +10:00
|
|
|
pathToNode,
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
['CallExpression', 'CallExpressionKw'],
|
2024-05-24 20:54:42 +10:00
|
|
|
true
|
2024-06-24 11:45:40 -04:00
|
|
|
)
|
|
|
|
if (trap(_node1)) return Promise.reject(_node1)
|
|
|
|
const shallowPath = _node1.shallowPath
|
|
|
|
|
2024-09-13 21:14:14 +10:00
|
|
|
if (!context.sketchDetails || !argPosition) return
|
2024-05-24 20:54:42 +10:00
|
|
|
const transform = removeSingleConstraintInfo(
|
2024-09-13 21:14:14 +10:00
|
|
|
shallowPath,
|
|
|
|
argPosition,
|
2024-05-24 20:54:42 +10:00
|
|
|
kclManager.ast,
|
2025-02-12 10:22:56 +13:00
|
|
|
kclManager.variables
|
2024-05-24 20:54:42 +10:00
|
|
|
)
|
2024-11-16 16:49:44 -05:00
|
|
|
|
2024-05-24 20:54:42 +10:00
|
|
|
if (!transform) return
|
|
|
|
const { modifiedAst } = transform
|
2024-11-16 16:49:44 -05:00
|
|
|
|
|
|
|
await kclManager.updateAst(modifiedAst, true)
|
|
|
|
|
|
|
|
// Code editor will be updated in the modelingMachine.
|
|
|
|
const newCode = recast(modifiedAst)
|
|
|
|
if (err(newCode)) return
|
|
|
|
await codeManager.updateCodeEditor(newCode)
|
2024-05-24 20:54:42 +10:00
|
|
|
} catch (e) {
|
|
|
|
console.log('error', e)
|
|
|
|
}
|
|
|
|
toast.success('Constraint removed')
|
|
|
|
}
|
2024-09-09 18:17:45 -04:00
|
|
|
}, reportRejection)}
|
2024-05-24 20:54:42 +10:00
|
|
|
>
|
|
|
|
<CustomIcon name={name} />
|
|
|
|
</button>
|
|
|
|
|
|
|
|
<div
|
|
|
|
className={`absolute ${
|
|
|
|
verticalPosition === 'top'
|
|
|
|
? 'top-0 -translate-y-full'
|
|
|
|
: 'bottom-0 translate-y-full'
|
|
|
|
} group-hover:block hidden w-[2px] h-2 translate-x-[12px] bg-white/40`}
|
|
|
|
></div>
|
|
|
|
<div
|
|
|
|
className={`absolute ${
|
|
|
|
verticalPosition === 'top' ? 'top-0' : 'bottom-0'
|
|
|
|
} group-hover:block hidden`}
|
|
|
|
style={{
|
|
|
|
transform: `translate3d(calc(-50% + 13px), ${
|
|
|
|
verticalPosition === 'top' ? '-100%' : '100%'
|
|
|
|
}, 0)`,
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<div
|
|
|
|
className="bg-chalkboard-10 dark:bg-chalkboard-90 p-2 px-3 rounded-sm border border-solid border-chalkboard-20 dark:border-chalkboard-80 shadow-sm"
|
|
|
|
data-testid="constraint-symbol-popover"
|
|
|
|
>
|
|
|
|
{implicitDesc ? (
|
|
|
|
<div className="min-w-48">
|
|
|
|
<pre className="inline-block">
|
|
|
|
<code className="text-primary">{value}</code>
|
|
|
|
</pre>{' '}
|
|
|
|
<span>is implicitly constrained {implicitDesc}</span>
|
|
|
|
</div>
|
|
|
|
) : (
|
|
|
|
<>
|
|
|
|
<div className="flex mb-1">
|
|
|
|
<span className="text-nowrap">
|
|
|
|
<span className="font-bold">
|
|
|
|
{isConstrained ? 'Constrained' : 'Unconstrained'}
|
|
|
|
</span>
|
|
|
|
<span className="text-white/80 text-sm pl-2">
|
|
|
|
{displayName}
|
|
|
|
</span>
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
<div className="flex mb-1">
|
|
|
|
<span className="pr-2 whitespace-nowrap">Set to</span>
|
|
|
|
<pre>
|
|
|
|
<code className="text-primary">{value}</code>
|
|
|
|
</pre>
|
|
|
|
</div>
|
|
|
|
<div className="text-sm text-chalkboard-70 dark:text-chalkboard-40 text-nowrap">
|
|
|
|
{isConstrained
|
|
|
|
? 'Click to unconstrain with raw number'
|
|
|
|
: 'Click to constrain with variable'}
|
|
|
|
</div>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
2024-02-14 08:03:20 +11:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const throttled = throttle((a: ReactCameraProperties) => {
|
|
|
|
if (a.type === 'perspective' && a.fov) {
|
2024-09-09 18:17:45 -04:00
|
|
|
sceneInfra.camControls.dollyZoom(a.fov).catch(reportRejection)
|
2024-02-14 08:03:20 +11:00
|
|
|
}
|
|
|
|
}, 1000 / 15)
|
|
|
|
|
|
|
|
export const CamDebugSettings = () => {
|
2024-04-15 12:04:17 -04:00
|
|
|
const [camSettings, setCamSettings] = useState<ReactCameraProperties>(
|
|
|
|
sceneInfra.camControls.reactCameraProperties
|
|
|
|
)
|
2024-02-14 08:03:20 +11:00
|
|
|
const [fov, setFov] = useState(12)
|
|
|
|
|
|
|
|
useEffect(() => {
|
2024-02-26 19:53:44 +11:00
|
|
|
sceneInfra.camControls.setReactCameraPropertiesCallback(setCamSettings)
|
2024-02-14 08:03:20 +11:00
|
|
|
}, [sceneInfra])
|
|
|
|
useEffect(() => {
|
|
|
|
if (camSettings.type === 'perspective' && camSettings.fov) {
|
|
|
|
setFov(camSettings.fov)
|
|
|
|
}
|
|
|
|
}, [(camSettings as any)?.fov])
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<h3>cam settings</h3>
|
|
|
|
perspective cam
|
|
|
|
<input
|
|
|
|
type="checkbox"
|
|
|
|
checked={camSettings.type === 'perspective'}
|
2024-09-30 11:40:00 -04:00
|
|
|
onChange={() =>
|
2025-01-23 10:25:21 -05:00
|
|
|
commandBarActor.send({
|
2024-09-30 11:40:00 -04:00
|
|
|
type: 'Find and select command',
|
|
|
|
data: {
|
|
|
|
groupId: 'settings',
|
|
|
|
name: 'modeling.cameraProjection',
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
2024-02-14 08:03:20 +11:00
|
|
|
/>
|
2024-06-05 14:43:12 +02:00
|
|
|
<div>
|
|
|
|
<button
|
|
|
|
onClick={() => {
|
2024-09-09 18:17:45 -04:00
|
|
|
sceneInfra.camControls.resetCameraPosition().catch(reportRejection)
|
2024-06-05 14:43:12 +02:00
|
|
|
}}
|
|
|
|
>
|
|
|
|
Reset Camera Position
|
|
|
|
</button>
|
|
|
|
</div>
|
2024-02-14 08:03:20 +11:00
|
|
|
{camSettings.type === 'perspective' && (
|
|
|
|
<input
|
|
|
|
type="range"
|
|
|
|
min="4"
|
|
|
|
max="90"
|
|
|
|
step={0.5}
|
|
|
|
value={fov}
|
|
|
|
onChange={(e) => {
|
|
|
|
setFov(parseFloat(e.target.value))
|
|
|
|
|
|
|
|
throttled({
|
|
|
|
...camSettings,
|
|
|
|
fov: parseFloat(e.target.value),
|
|
|
|
})
|
|
|
|
}}
|
|
|
|
className="w-full cursor-pointer pointer-events-auto"
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
{camSettings.type === 'perspective' && (
|
|
|
|
<div>
|
|
|
|
<span>fov</span>
|
|
|
|
<input
|
|
|
|
type="number"
|
|
|
|
value={camSettings.fov}
|
|
|
|
className="text-black w-16"
|
|
|
|
onChange={(e) => {
|
2024-02-26 19:53:44 +11:00
|
|
|
sceneInfra.camControls.setCam({
|
2024-02-14 08:03:20 +11:00
|
|
|
...camSettings,
|
|
|
|
fov: parseFloat(e.target.value),
|
|
|
|
})
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
{camSettings.type === 'orthographic' && (
|
|
|
|
<>
|
|
|
|
<div>
|
|
|
|
<span>fov</span>
|
|
|
|
<input
|
|
|
|
type="number"
|
|
|
|
value={camSettings.zoom}
|
|
|
|
className="text-black w-16"
|
|
|
|
onChange={(e) => {
|
2024-02-26 19:53:44 +11:00
|
|
|
sceneInfra.camControls.setCam({
|
2024-02-14 08:03:20 +11:00
|
|
|
...camSettings,
|
|
|
|
zoom: parseFloat(e.target.value),
|
|
|
|
})
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
<div>
|
|
|
|
Position
|
|
|
|
<ul className="flex">
|
|
|
|
<li>
|
|
|
|
<span className="pl-2 pr-1">x:</span>
|
|
|
|
<input
|
|
|
|
type="number"
|
|
|
|
step={5}
|
|
|
|
data-testid="cam-x-position"
|
|
|
|
value={camSettings.position[0]}
|
|
|
|
className="text-black w-16"
|
|
|
|
onChange={(e) => {
|
2024-02-26 19:53:44 +11:00
|
|
|
sceneInfra.camControls.setCam({
|
2024-02-14 08:03:20 +11:00
|
|
|
...camSettings,
|
|
|
|
position: [
|
|
|
|
parseFloat(e.target.value),
|
|
|
|
camSettings.position[1],
|
|
|
|
camSettings.position[2],
|
|
|
|
],
|
|
|
|
})
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
<span className="pl-2 pr-1">y:</span>
|
|
|
|
<input
|
|
|
|
type="number"
|
|
|
|
step={5}
|
|
|
|
data-testid="cam-y-position"
|
|
|
|
value={camSettings.position[1]}
|
|
|
|
className="text-black w-16"
|
|
|
|
onChange={(e) => {
|
2024-02-26 19:53:44 +11:00
|
|
|
sceneInfra.camControls.setCam({
|
2024-02-14 08:03:20 +11:00
|
|
|
...camSettings,
|
|
|
|
position: [
|
|
|
|
camSettings.position[0],
|
|
|
|
parseFloat(e.target.value),
|
|
|
|
camSettings.position[2],
|
|
|
|
],
|
|
|
|
})
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
<span className="pl-2 pr-1">z:</span>
|
|
|
|
<input
|
|
|
|
type="number"
|
|
|
|
step={5}
|
|
|
|
data-testid="cam-z-position"
|
|
|
|
value={camSettings.position[2]}
|
|
|
|
className="text-black w-16"
|
|
|
|
onChange={(e) => {
|
2024-02-26 19:53:44 +11:00
|
|
|
sceneInfra.camControls.setCam({
|
2024-02-14 08:03:20 +11:00
|
|
|
...camSettings,
|
|
|
|
position: [
|
|
|
|
camSettings.position[0],
|
|
|
|
camSettings.position[1],
|
|
|
|
parseFloat(e.target.value),
|
|
|
|
],
|
|
|
|
})
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</div>
|
2024-06-05 14:43:12 +02:00
|
|
|
<div>
|
|
|
|
target
|
|
|
|
<ul className="flex">
|
|
|
|
<li>
|
|
|
|
<span className="pl-2 pr-1">x:</span>
|
|
|
|
<input
|
|
|
|
type="number"
|
|
|
|
step={5}
|
|
|
|
data-testid="cam-x-target"
|
|
|
|
value={camSettings.target[0]}
|
|
|
|
className="text-black w-16"
|
|
|
|
onChange={(e) => {
|
|
|
|
sceneInfra.camControls.setCam({
|
|
|
|
...camSettings,
|
|
|
|
target: [
|
|
|
|
parseFloat(e.target.value),
|
|
|
|
camSettings.target[1],
|
|
|
|
camSettings.target[2],
|
|
|
|
],
|
|
|
|
})
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
<span className="pl-2 pr-1">y:</span>
|
|
|
|
<input
|
|
|
|
type="number"
|
|
|
|
step={5}
|
|
|
|
data-testid="cam-y-target"
|
|
|
|
value={camSettings.target[1]}
|
|
|
|
className="text-black w-16"
|
|
|
|
onChange={(e) => {
|
|
|
|
sceneInfra.camControls.setCam({
|
|
|
|
...camSettings,
|
|
|
|
target: [
|
|
|
|
camSettings.target[0],
|
|
|
|
parseFloat(e.target.value),
|
|
|
|
camSettings.target[2],
|
|
|
|
],
|
|
|
|
})
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
<span className="pl-2 pr-1">z:</span>
|
|
|
|
<input
|
|
|
|
type="number"
|
|
|
|
step={5}
|
|
|
|
data-testid="cam-z-target"
|
|
|
|
value={camSettings.target[2]}
|
|
|
|
className="text-black w-16"
|
|
|
|
onChange={(e) => {
|
|
|
|
sceneInfra.camControls.setCam({
|
|
|
|
...camSettings,
|
|
|
|
target: [
|
|
|
|
camSettings.target[0],
|
|
|
|
camSettings.target[1],
|
|
|
|
parseFloat(e.target.value),
|
|
|
|
],
|
|
|
|
})
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</div>
|
2024-02-14 08:03:20 +11:00
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|