2025-04-01 23:54:26 -07:00
import toast from 'react-hot-toast'
import { Mesh , Vector2 , Vector3 } from 'three'
import { assign , fromPromise , setup } from 'xstate'
import type { Node } from '@rust/kcl-lib/bindings/Node'
2025-04-02 15:10:57 -07:00
import { deleteSegment } from '@src/clientSideScene/deleteSegment'
2024-03-28 17:22:11 +11:00
import {
2025-04-01 23:54:26 -07:00
orthoScale ,
quaternionFromUpNForward ,
} from '@src/clientSideScene/helpers'
import { DRAFT_DASHED_LINE } from '@src/clientSideScene/sceneConstants'
2025-04-02 15:10:57 -07:00
import { DRAFT_POINT } from '@src/clientSideScene/sceneUtils'
2025-04-01 23:54:26 -07:00
import { createProfileStartHandle } from '@src/clientSideScene/segments'
import type { MachineManager } from '@src/components/MachineManagerProvider'
import type { ModelingMachineContext } from '@src/components/ModelingMachineProvider'
import type { SidebarType } from '@src/components/ModelingSidebar/ModelingPanes'
2025-04-01 14:20:42 -07:00
import {
2025-04-01 23:54:26 -07:00
applyConstraintEqualAngle ,
equalAngleInfo ,
} from '@src/components/Toolbar/EqualAngle'
2025-04-01 15:31:19 -07:00
import {
2025-04-01 23:54:26 -07:00
applyConstraintEqualLength ,
setEqualLengthInfo ,
} from '@src/components/Toolbar/EqualLength'
2023-10-11 13:36:54 +11:00
import {
2025-04-01 15:31:19 -07:00
applyConstraintHorzVert ,
2025-04-01 23:54:26 -07:00
horzVertInfo ,
} from '@src/components/Toolbar/HorzVert'
import { intersectInfo } from '@src/components/Toolbar/Intersect'
2023-10-11 13:36:54 +11:00
import {
2025-04-01 23:54:26 -07:00
applyRemoveConstrainingValues ,
removeConstrainingValuesInfo ,
} from '@src/components/Toolbar/RemoveConstrainingValues'
2025-04-01 15:31:19 -07:00
import {
2025-04-01 23:54:26 -07:00
absDistanceInfo ,
applyConstraintAxisAlign ,
} from '@src/components/Toolbar/SetAbsDistance'
import { angleBetweenInfo } from '@src/components/Toolbar/SetAngleBetween'
2025-04-01 15:31:19 -07:00
import {
2025-04-01 23:54:26 -07:00
applyConstraintHorzVertAlign ,
horzVertDistanceInfo ,
} from '@src/components/Toolbar/SetHorzVertDistance'
2025-04-16 00:11:25 +10:00
import { angleLengthInfo } from '@src/components/Toolbar/angleLengthInfo'
2025-04-01 23:54:26 -07:00
import { createLiteral , createLocalName } from '@src/lang/create'
import { updateModelingState } from '@src/lang/modelingWorkflows'
2024-09-17 08:29:52 -05:00
import {
2025-04-26 18:26:39 -04:00
addClone ,
2025-02-07 10:16:36 -05:00
addHelix ,
2024-11-26 11:36:14 -05:00
addOffsetPlane ,
2025-03-14 16:05:41 -04:00
addShell ,
2025-03-19 11:58:53 -04:00
insertNamedConstant ,
2025-04-17 11:44:31 -04:00
insertVariableAndOffsetPathToNode ,
2025-04-01 23:54:26 -07:00
} from '@src/lang/modifyAst'
import type {
2025-01-14 18:08:32 +01:00
ChamferParameters ,
2024-12-02 21:43:59 +01:00
FilletParameters ,
2025-04-01 23:54:26 -07:00
} from '@src/lang/modifyAst/addEdgeTreatment'
import {
EdgeTreatmentType ,
2025-05-08 22:16:36 +02:00
modifyAstWithEdgeTreatmentAndTag ,
editEdgeTreatment ,
2025-03-14 16:05:41 -04:00
getPathToExtrudeForSegmentSelection ,
mutateAstWithTagForSketchSegment ,
2025-04-01 23:54:26 -07:00
} from '@src/lang/modifyAst/addEdgeTreatment'
2023-10-16 08:54:38 +11:00
import {
2025-05-06 17:57:27 -04:00
addExtrude ,
addLoft ,
addRevolve ,
addSweep ,
2025-04-01 23:54:26 -07:00
getAxisExpressionAndIndex ,
2025-05-06 17:57:27 -04:00
} from '@src/lang/modifyAst/addSweep'
2025-04-01 14:20:42 -07:00
import {
2025-04-01 23:54:26 -07:00
applyIntersectFromTargetOperatorSelections ,
applySubtractFromTargetOperatorSelections ,
applyUnionFromTargetOperatorSelections ,
} from '@src/lang/modifyAst/boolean'
2025-04-01 15:31:19 -07:00
import {
deleteSelectionPromise ,
deletionErrorMessage ,
2025-04-01 23:54:26 -07:00
} from '@src/lang/modifyAst/deleteSelection'
import { setAppearance } from '@src/lang/modifyAst/setAppearance'
2025-04-30 13:07:39 -04:00
import {
setTranslate ,
setRotate ,
insertExpressionNode ,
2025-05-02 17:25:33 -04:00
retrievePathToNodeFromTransformSelection ,
2025-04-30 13:07:39 -04:00
} from '@src/lang/modifyAst/setTransform'
2025-04-01 15:31:19 -07:00
import {
2025-04-01 23:54:26 -07:00
getNodeFromPath ,
2025-04-30 13:07:39 -04:00
findPipesWithImportAlias ,
findImportNodeAndAlias ,
2025-04-01 23:54:26 -07:00
isNodeSafeToReplacePath ,
stringifyPathToNode ,
2025-04-15 23:27:50 +10:00
updatePathToNodesAfterEdit ,
2025-04-17 11:44:31 -04:00
valueOrVariable ,
2025-04-01 23:54:26 -07:00
} from '@src/lang/queryAst'
2025-04-15 23:27:50 +10:00
import {
getFaceCodeRef ,
getPathsFromPlaneArtifact ,
} from '@src/lang/std/artifactGraph'
2025-04-01 23:54:26 -07:00
import type { Coords2d } from '@src/lang/std/sketch'
import type {
2025-04-15 23:27:50 +10:00
Artifact ,
2025-04-01 23:54:26 -07:00
CallExpressionKw ,
Expr ,
Literal ,
Name ,
PathToNode ,
2025-04-26 18:26:39 -04:00
PipeExpression ,
2025-04-01 23:54:26 -07:00
VariableDeclaration ,
VariableDeclarator ,
} from '@src/lang/wasm'
import { parse , recast , resultIsOk , sketchFromKclValue } from '@src/lang/wasm'
import type { ModelingCommandSchema } from '@src/lib/commandBarConfigs/modelingCommandConfig'
import type { KclCommandValue } from '@src/lib/commandTypes'
import { EXECUTION_TYPE_REAL } from '@src/lib/constants'
import type { DefaultPlaneStr } from '@src/lib/planes'
import type {
Axis ,
DefaultPlaneSelection ,
Selection ,
Selections ,
} from '@src/lib/selections'
import { updateSelections } from '@src/lib/selections'
import {
codeManager ,
editorManager ,
engineCommandManager ,
kclManager ,
sceneEntitiesManager ,
sceneInfra ,
} from '@src/lib/singletons'
import type { ToolbarModeName } from '@src/lib/toolbar'
import { err , reportRejection , trap } from '@src/lib/trap'
2025-05-06 17:57:27 -04:00
import { uuidv4 } from '@src/lib/utils'
2025-04-30 13:07:39 -04:00
import type { ImportStatement } from '@rust/kcl-lib/bindings/ImportStatement'
2023-10-11 13:36:54 +11:00
export type SetSelections =
| {
selectionType : 'singleCodeCursor'
selection? : Selection
2024-12-20 16:19:59 -05:00
scrollIntoView? : boolean
2023-10-11 13:36:54 +11:00
}
| {
2024-11-26 11:36:14 -05:00
selectionType : 'axisSelection'
2023-10-11 13:36:54 +11:00
selection : Axis
}
2024-11-26 11:36:14 -05:00
| {
selectionType : 'defaultPlaneSelection'
selection : DefaultPlaneSelection
}
2023-10-11 13:36:54 +11:00
| {
selectionType : 'completeSelection'
selection : Selections
2025-02-15 00:57:04 +11:00
updatedSketchEntryNodePath? : PathToNode
updatedSketchNodePaths? : PathToNode [ ]
updatedPlaneNodePath? : PathToNode
2023-10-11 13:36:54 +11:00
}
| {
selectionType : 'mirrorCodeMirrorSelections'
selection : Selections
}
2024-04-04 11:07:51 +11:00
export type MouseState =
| {
type : 'idle'
}
| {
type : 'isHovering'
on : any
}
| {
type : 'isDragging'
on : any
}
2024-05-24 20:54:42 +10:00
| {
type : 'timeoutEnd'
pathToNodeString : string
}
2024-04-04 11:07:51 +11:00
2024-03-22 10:23:04 +11:00
export interface SketchDetails {
2025-04-15 23:27:50 +10:00
// there is no artifactGraph in sketch mode, so this is only used as vital information when entering sketch mode
// or on full/nonMock execution in sketch mode (manual code edit) as the entry point, as it will be accurate in these situations
2025-02-15 00:57:04 +11:00
sketchEntryNodePath : PathToNode
sketchNodePaths : PathToNode [ ]
planeNodePath : PathToNode
2024-03-22 10:23:04 +11:00
zAxis : [ number , number , number ]
yAxis : [ number , number , number ]
origin : [ number , number , number ]
2025-01-16 12:48:13 -05:00
// face id or plane id, both are strings
animateTargetId? : string
2025-02-15 00:57:04 +11:00
// this is the expression that was added when as sketch tool was used but not completed
// i.e first click for the center of the circle, but not the second click for the radius
// we added a circle to editor, but they bailed out early so we should remove it, set to -1 to ignore
expressionIndexToDelete? : number
}
export interface SketchDetailsUpdate {
updatedEntryNodePath : PathToNode
updatedSketchNodePaths : PathToNode [ ]
updatedPlaneNodePath? : PathToNode
// see comment in SketchDetails
expressionIndexToDelete : number
2024-03-22 10:23:04 +11:00
}
2024-05-24 20:54:42 +10:00
export interface SegmentOverlay {
windowCoords : Coords2d
angle : number
group : any
pathToNode : PathToNode
visible : boolean
2025-02-15 00:57:04 +11:00
hasThreeDotMenu : boolean
filterValue? : string
2024-05-24 20:54:42 +10:00
}
export interface SegmentOverlays {
2025-02-15 00:57:04 +11:00
[ pathToNodeString : string ] : SegmentOverlay [ ]
2024-05-24 20:54:42 +10:00
}
2024-09-26 18:25:05 +10:00
export interface EdgeCutInfo {
type : 'edgeCut'
tagName : string
subType : 'base' | 'opposite' | 'adjacent'
}
export interface CapInfo {
type : 'cap'
subType : 'start' | 'end'
}
2024-09-09 19:59:36 +03:00
export type ExtrudeFacePlane = {
type : 'extrudeFace'
position : [ number , number , number ]
sketchPathToNode : PathToNode
extrudePathToNode : PathToNode
2024-09-26 18:25:05 +10:00
faceInfo :
| {
type : 'wall'
}
| CapInfo
| EdgeCutInfo
2024-09-09 19:59:36 +03:00
faceId : string
zAxis : [ number , number , number ]
yAxis : [ number , number , number ]
}
export type DefaultPlane = {
type : 'defaultPlane'
plane : DefaultPlaneStr
planeId : string
zAxis : [ number , number , number ]
yAxis : [ number , number , number ]
}
2024-11-18 16:25:25 -05:00
export type OffsetPlane = {
type : 'offsetPlane'
position : [ number , number , number ]
planeId : string
pathToNode : PathToNode
zAxis : [ number , number , number ]
yAxis : [ number , number , number ]
}
2024-05-24 20:54:42 +10:00
export type SegmentOverlayPayload =
| {
type : 'set-one'
pathToNodeString : string
2025-02-15 00:57:04 +11:00
seg : SegmentOverlay [ ]
2024-05-24 20:54:42 +10:00
}
| {
type : 'delete-one'
pathToNodeString : string
}
| { type : 'clear' }
| {
2025-04-30 12:08:45 +10:00
type : 'set-many'
2024-05-24 20:54:42 +10:00
overlays : SegmentOverlays
}
2024-08-14 14:26:44 -04:00
export interface Store {
2024-07-04 01:55:06 -04:00
videoElement? : HTMLVideoElement
2024-07-02 17:16:27 +10:00
openPanes : SidebarType [ ]
}
2024-09-23 22:42:51 +10:00
export type SketchTool =
| 'line'
| 'tangentialArc'
| 'rectangle'
2024-11-18 10:04:09 -05:00
| 'center rectangle'
2024-09-23 22:42:51 +10:00
| 'circle'
2025-02-15 00:57:04 +11:00
| 'circleThreePoint'
2025-03-18 11:14:12 +11:00
| 'arc'
| 'arcThreePoint'
2024-09-23 22:42:51 +10:00
| 'none'
2024-07-12 16:16:26 -04:00
2023-10-16 21:20:05 +11:00
export type ModelingMachineEvent =
2024-02-19 17:23:03 +11:00
| {
type : 'Enter sketch'
data ? : {
forceNewSketch? : boolean
}
}
2024-03-22 10:23:04 +11:00
| { type : 'Sketch On Face' }
2023-10-16 21:20:05 +11:00
| {
2025-05-07 08:25:12 +10:00
type : 'Select sketch plane'
2024-11-18 16:25:25 -05:00
data : DefaultPlane | ExtrudeFacePlane | OffsetPlane
2023-10-16 21:20:05 +11:00
}
2024-05-30 13:28:29 +10:00
| {
type : 'Set selection'
data : SetSelections
}
2024-06-29 10:36:04 -07:00
| {
type : 'Delete selection'
}
2023-10-16 21:20:05 +11:00
| { type : 'Sketch no face' }
2024-12-20 14:30:37 -05:00
| { type : 'Cancel' ; cleanup ? : ( ) = > void }
2023-10-16 21:20:05 +11:00
| { type : 'CancelSketch' }
2025-02-15 00:57:04 +11:00
| {
type : 'Add start point' | 'Continue existing profile'
data : {
sketchNodePaths : PathToNode [ ]
sketchEntryNodePath : PathToNode
}
}
| { type : 'Close sketch' }
2023-10-16 21:20:05 +11:00
| { type : 'Make segment horizontal' }
| { type : 'Make segment vertical' }
| { type : 'Constrain horizontal distance' }
2023-12-01 20:18:51 +11:00
| { type : 'Constrain ABS X' }
| { type : 'Constrain ABS Y' }
2023-10-16 21:20:05 +11:00
| { type : 'Constrain vertical distance' }
| { type : 'Constrain angle' }
| { type : 'Constrain perpendicular distance' }
| { type : 'Constrain horizontally align' }
| { type : 'Constrain vertically align' }
2023-12-01 20:18:51 +11:00
| { type : 'Constrain snap to X' }
| { type : 'Constrain snap to Y' }
2024-12-09 16:43:58 -05:00
| {
type : 'Constrain length'
data : ModelingCommandSchema [ 'Constrain length' ]
}
2023-10-16 21:20:05 +11:00
| { type : 'Constrain equal length' }
| { type : 'Constrain parallel' }
2024-06-04 16:29:20 +10:00
| { type : 'Constrain remove constraints' ; data? : PathToNode }
2025-03-19 11:58:53 -04:00
| {
type : 'event.parameter.create'
data : ModelingCommandSchema [ 'event.parameter.create' ]
}
2025-03-20 16:41:09 -04:00
| {
type : 'event.parameter.edit'
data : ModelingCommandSchema [ 'event.parameter.edit' ]
}
2024-03-04 16:06:43 -05:00
| { type : 'Export' ; data : ModelingCommandSchema [ 'Export' ] }
2025-03-28 14:56:48 +11:00
| {
type : 'Boolean Subtract'
data : ModelingCommandSchema [ 'Boolean Subtract' ]
}
| {
type : 'Boolean Union'
data : ModelingCommandSchema [ 'Boolean Union' ]
}
| {
type : 'Boolean Intersect'
data : ModelingCommandSchema [ 'Boolean Intersect' ]
}
2024-08-04 00:51:30 -04:00
| { type : 'Make' ; data : ModelingCommandSchema [ 'Make' ] }
Command bar: add extrude command, nonlinear editing, etc (#1204)
* Tweak toaster look and feel
* Add icons, tweak plus icon names
* Rename commandBarMeta to commandBarConfig
* Refactor command bar, add support for icons
* Create a tailwind plugin for aria-pressed button state
* Remove overlay from behind command bar
* Clean up toolbar
* Button and other style tweaks
* Icon tweaks follow-up: make old icons work with new sizing
* Delete unused static icons
* More CSS tweaks
* Small CSS tweak to project sidebar
* Add command bar E2E test
* fumpt
* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)
* fix typo in a comment
* Fix icon padding (built version only)
* Update onboarding and warning banner icons padding
* Misc minor style fixes
* Get Extrude opening and canceling from command bar
* Iconography tweaks
* Get extrude kind of working
* Refactor command bar config types and organization
* Move command bar configs to be co-located with each other
* Start building a state machine for the command bar
* Start converting command bar to state machine
* Add support for multiple args, confirmation step
* Submission behavior, hotkeys, code organization
* Add new test for extruding from command bar
* Polish step back and selection hotkeys, CSS tweaks
* Loading style tweaks
* Validate selection inputs, polish UX of args re-editing
* Prevent submission with multiple selection on singlular arg
* Remove stray console logs
* Tweak test, CSS nit, remove extrude "result" argument
* Fix linting warnings
* Show Ctrl+/ instead of ⌘K on all platforms but Mac
* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)
* Add "Enter sketch" to command bar
* fix command bar test
* Fix flaky cmd bar extrude test by waiting for engine select response
* Cover both button labels '⌘K' and 'Ctrl+/' in test
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-12-13 12:49:01 -05:00
| { type : 'Extrude' ; data? : ModelingCommandSchema [ 'Extrude' ] }
2025-01-11 08:20:49 -05:00
| { type : 'Sweep' ; data? : ModelingCommandSchema [ 'Sweep' ] }
2024-12-04 17:24:16 -05:00
| { type : 'Loft' ; data? : ModelingCommandSchema [ 'Loft' ] }
2024-12-09 15:20:48 -05:00
| { type : 'Shell' ; data? : ModelingCommandSchema [ 'Shell' ] }
2024-09-17 08:29:52 -05:00
| { type : 'Revolve' ; data? : ModelingCommandSchema [ 'Revolve' ] }
2024-07-15 19:20:32 +10:00
| { type : 'Fillet' ; data? : ModelingCommandSchema [ 'Fillet' ] }
2025-01-14 18:08:32 +01:00
| { type : 'Chamfer' ; data? : ModelingCommandSchema [ 'Chamfer' ] }
2024-11-26 11:36:14 -05:00
| { type : 'Offset plane' ; data : ModelingCommandSchema [ 'Offset plane' ] }
2025-02-07 10:16:36 -05:00
| { type : 'Helix' ; data : ModelingCommandSchema [ 'Helix' ] }
2024-12-20 13:39:06 +11:00
| { type : 'Prompt-to-edit' ; data : ModelingCommandSchema [ 'Prompt-to-edit' ] }
2025-02-19 13:43:27 -05:00
| {
type : 'Delete selection'
data : ModelingCommandSchema [ 'Delete selection' ]
}
2025-02-22 08:09:54 -05:00
| { type : 'Appearance' ; data : ModelingCommandSchema [ 'Appearance' ] }
2025-04-17 11:44:31 -04:00
| { type : 'Translate' ; data : ModelingCommandSchema [ 'Translate' ] }
| { type : 'Rotate' ; data : ModelingCommandSchema [ 'Rotate' ] }
2025-04-26 18:26:39 -04:00
| { type : 'Clone' ; data : ModelingCommandSchema [ 'Clone' ] }
2024-04-19 11:56:21 -04:00
| {
2025-03-18 11:14:12 +11:00
type :
| 'Add circle origin'
| 'Add circle center'
| 'Add center rectangle origin'
| 'click in scene'
| 'Add first point'
2025-02-15 00:57:04 +11:00
data : [ x : number , y : number ]
}
| {
type : 'Add second point'
data : {
p1 : [ x : number , y : number ]
p2 : [ x : number , y : number ]
}
}
2024-02-11 12:59:00 +11:00
| {
2024-09-09 19:59:36 +03:00
type : 'xstate.done.actor.animate-to-face'
output : SketchDetails
2024-02-11 12:59:00 +11:00
}
2024-09-09 19:59:36 +03:00
| { type : 'xstate.done.actor.animate-to-sketch' ; output : SketchDetails }
| { type : ` xstate.done.actor.do-constrain ${ string } ` ; output : SetSelections }
2025-02-15 00:57:04 +11:00
| {
type :
| 'xstate.done.actor.set-up-draft-circle'
| 'xstate.done.actor.set-up-draft-rectangle'
| 'xstate.done.actor.set-up-draft-center-rectangle'
| 'xstate.done.actor.set-up-draft-circle-three-point'
2025-03-18 11:14:12 +11:00
| 'xstate.done.actor.set-up-draft-arc'
| 'xstate.done.actor.set-up-draft-arc-three-point'
2025-02-15 00:57:04 +11:00
| 'xstate.done.actor.split-sketch-pipe-if-needed'
| 'xstate.done.actor.actor-circle-three-point'
2025-04-15 23:27:50 +10:00
| 'xstate.done.actor.reeval-node-paths'
2025-03-18 11:14:12 +11:00
2025-02-15 00:57:04 +11:00
output : SketchDetailsUpdate
}
2025-03-18 11:14:12 +11:00
| {
type : 'xstate.done.actor.setup-client-side-sketch-segments9'
}
2024-04-04 11:07:51 +11:00
| { type : 'Set mouse state' ; data : MouseState }
2024-07-02 17:16:27 +10:00
| { type : 'Set context' ; data : Partial < Store > }
2024-05-24 20:54:42 +10:00
| {
type : 'Set Segment Overlays'
data : SegmentOverlayPayload
}
2024-10-04 13:47:44 -07:00
| {
type : 'Center camera on selection'
}
2024-05-24 20:54:42 +10:00
| {
type : 'Delete segment'
data : PathToNode
}
2024-05-06 19:28:30 +10:00
| {
type : 'code edit during sketch'
}
2024-05-24 20:54:42 +10:00
| {
2024-12-09 16:43:58 -05:00
type : 'Constrain with named value'
data : ModelingCommandSchema [ 'Constrain with named value' ]
2024-05-24 20:54:42 +10:00
}
2024-07-05 13:40:16 +10:00
| {
type : 'change tool'
2024-07-12 16:16:26 -04:00
data : {
tool : SketchTool
}
2024-07-05 13:40:16 +10:00
}
| { type : 'Finish rectangle' }
2024-11-18 10:04:09 -05:00
| { type : 'Finish center rectangle' }
2024-09-23 22:42:51 +10:00
| { type : 'Finish circle' }
2025-02-15 00:57:04 +11:00
| { type : 'Finish circle three point' }
2025-03-18 11:14:12 +11:00
| { type : 'Finish arc' }
2024-08-03 07:05:35 +10:00
| { type : 'Artifact graph populated' }
| { type : 'Artifact graph emptied' }
2025-04-25 18:21:19 +02:00
| { type : 'Artifact graph initialized' }
| {
type : 'Toggle default plane visibility'
planeId : string
planeKey : keyof PlaneVisibilityMap
}
| {
type : 'Save default plane visibility'
planeId : string
planeKey : keyof PlaneVisibilityMap
}
| {
type : 'Restore default plane visibility'
}
2024-02-11 12:59:00 +11:00
export type MoveDesc = { line : number ; snippet : string }
2023-10-16 21:20:05 +11:00
2024-07-02 17:16:27 +10:00
export const PERSIST_MODELING_CONTEXT = 'persistModelingContext'
2025-04-25 18:21:19 +02:00
2024-07-02 17:16:27 +10:00
interface PersistedModelingContext {
openPanes : Store [ 'openPanes' ]
}
type PersistedKeys = keyof PersistedModelingContext
export const PersistedValues : PersistedKeys [ ] = [ 'openPanes' ]
2024-08-02 15:39:05 -04:00
export const getPersistedContext = ( ) : Partial < PersistedModelingContext > = > {
const c = ( typeof window !== 'undefined' &&
JSON . parse ( localStorage . getItem ( PERSIST_MODELING_CONTEXT ) || '{}' ) ) || {
openPanes : [ 'code' ] ,
}
return c
}
2024-09-09 19:59:36 +03:00
export interface ModelingMachineContext {
currentMode : ToolbarModeName
currentTool : SketchTool
2025-04-15 23:27:50 +10:00
toastId : string | null
2024-10-25 19:28:10 -04:00
machineManager : MachineManager
2024-09-09 19:59:36 +03:00
selection : string [ ]
selectionRanges : Selections
sketchDetails : SketchDetails | null
sketchPlaneId : string
sketchEnginePathId : string
moveDescs : MoveDesc [ ]
mouseState : MouseState
segmentOverlays : SegmentOverlays
segmentHoverMap : { [ pathToNodeString : string ] : number }
store : Store
2025-04-25 18:21:19 +02:00
defaultPlaneVisibility : PlaneVisibilityMap
savedDefaultPlaneVisibility : PlaneVisibilityMap
planesInitialized : boolean
2024-09-09 19:59:36 +03:00
}
2025-04-25 18:21:19 +02:00
export type PlaneVisibilityMap = {
xy : boolean
xz : boolean
yz : boolean
}
2024-09-09 19:59:36 +03:00
export const modelingMachineDefaultContext : ModelingMachineContext = {
currentMode : 'modeling' ,
currentTool : 'none' ,
2025-04-15 23:27:50 +10:00
toastId : null ,
2024-10-25 19:28:10 -04:00
machineManager : {
machines : [ ] ,
machineApiIp : null ,
currentMachine : null ,
setCurrentMachine : ( ) = > { } ,
noMachinesReason : ( ) = > undefined ,
} ,
2024-09-09 19:59:36 +03:00
selection : [ ] ,
2024-08-02 15:39:05 -04:00
selectionRanges : {
otherSelections : [ ] ,
2024-11-21 15:04:30 +11:00
graphSelections : [ ] ,
2024-09-09 19:59:36 +03:00
} ,
2025-04-15 23:27:50 +10:00
2024-08-02 15:39:05 -04:00
sketchDetails : {
2025-02-15 00:57:04 +11:00
sketchEntryNodePath : [ ] ,
planeNodePath : [ ] ,
sketchNodePaths : [ ] ,
2024-08-02 15:39:05 -04:00
zAxis : [ 0 , 0 , 1 ] ,
yAxis : [ 0 , 1 , 0 ] ,
origin : [ 0 , 0 , 0 ] ,
2024-09-09 19:59:36 +03:00
} ,
sketchPlaneId : '' ,
sketchEnginePathId : '' ,
moveDescs : [ ] ,
mouseState : { type : 'idle' } ,
segmentOverlays : { } ,
segmentHoverMap : { } ,
2024-08-02 15:39:05 -04:00
store : {
openPanes : getPersistedContext ( ) . openPanes || [ 'code' ] ,
2024-09-09 19:59:36 +03:00
} ,
2025-04-25 18:21:19 +02:00
defaultPlaneVisibility : {
xy : true ,
xz : true ,
yz : true ,
} ,
// Manually toggled plane visibility is saved and restored when going back to modeling mode
savedDefaultPlaneVisibility : {
xy : true ,
xz : true ,
yz : true ,
} ,
planesInitialized : false ,
2024-07-02 17:16:27 +10:00
}
2024-09-09 19:59:36 +03:00
export const modelingMachine = setup ( {
types : {
context : { } as ModelingMachineContext ,
events : { } as ModelingMachineEvent ,
input : { } as ModelingMachineContext ,
} ,
guards : {
'Selection is on face' : ( ) = > false ,
'Has exportable geometry' : ( ) = > false ,
'has valid selection for deletion' : ( ) = > false ,
2025-04-15 23:27:50 +10:00
'is-error-free' : ( ) = > false ,
2025-03-21 11:44:44 +01:00
'no kcl errors' : ( ) = > {
return ! kclManager . hasErrors ( )
} ,
2024-09-09 19:59:36 +03:00
'is editing existing sketch' : ( { context : { sketchDetails } } ) = >
isEditingExistingSketch ( { sketchDetails } ) ,
'Can make selection horizontal' : ( { context : { selectionRanges } } ) = > {
const info = horzVertInfo ( selectionRanges , 'horizontal' )
2025-04-15 23:27:50 +10:00
if ( err ( info ) ) return false
2024-09-09 19:59:36 +03:00
return info . enabled
} ,
'Can make selection vertical' : ( { context : { selectionRanges } } ) = > {
const info = horzVertInfo ( selectionRanges , 'vertical' )
2025-04-15 23:27:50 +10:00
if ( err ( info ) ) return false
2024-09-09 19:59:36 +03:00
return info . enabled
} ,
'Can constrain horizontal distance' : ( { context : { selectionRanges } } ) = > {
const info = horzVertDistanceInfo ( {
2024-11-21 15:04:30 +11:00
selectionRanges : selectionRanges ,
2024-09-09 19:59:36 +03:00
constraint : 'setHorzDistance' ,
} )
2025-04-15 23:27:50 +10:00
if ( err ( info ) ) return false
2024-09-09 19:59:36 +03:00
return info . enabled
} ,
'Can constrain vertical distance' : ( { context : { selectionRanges } } ) = > {
const info = horzVertDistanceInfo ( {
2024-11-21 15:04:30 +11:00
selectionRanges : selectionRanges ,
2024-09-09 19:59:36 +03:00
constraint : 'setVertDistance' ,
} )
2025-04-15 23:27:50 +10:00
if ( err ( info ) ) return false
2024-09-09 19:59:36 +03:00
return info . enabled
} ,
'Can constrain ABS X' : ( { context : { selectionRanges } } ) = > {
2024-11-21 15:04:30 +11:00
const info = absDistanceInfo ( {
selectionRanges ,
constraint : 'xAbs' ,
} )
2025-04-15 23:27:50 +10:00
if ( err ( info ) ) return false
2024-09-09 19:59:36 +03:00
return info . enabled
} ,
'Can constrain ABS Y' : ( { context : { selectionRanges } } ) = > {
2024-11-21 15:04:30 +11:00
const info = absDistanceInfo ( {
selectionRanges ,
constraint : 'yAbs' ,
} )
2025-04-15 23:27:50 +10:00
if ( err ( info ) ) return false
2024-09-09 19:59:36 +03:00
return info . enabled
} ,
'Can constrain angle' : ( { context : { selectionRanges } } ) = > {
2024-11-21 15:04:30 +11:00
const angleBetween = angleBetweenInfo ( {
selectionRanges ,
} )
2025-04-15 23:27:50 +10:00
if ( err ( angleBetween ) ) return false
2024-09-09 19:59:36 +03:00
const angleLength = angleLengthInfo ( {
selectionRanges ,
angleOrLength : 'setAngle' ,
} )
2025-04-15 23:27:50 +10:00
if ( err ( angleLength ) ) return false
2024-09-09 19:59:36 +03:00
return angleBetween . enabled || angleLength . enabled
} ,
'Can constrain length' : ( { context : { selectionRanges } } ) = > {
2024-11-21 15:04:30 +11:00
const angleLength = angleLengthInfo ( {
selectionRanges ,
} )
2025-04-15 23:27:50 +10:00
if ( err ( angleLength ) ) return false
2024-09-09 19:59:36 +03:00
return angleLength . enabled
} ,
'Can constrain perpendicular distance' : ( {
context : { selectionRanges } ,
} ) = > {
const info = intersectInfo ( { selectionRanges } )
2025-04-15 23:27:50 +10:00
if ( err ( info ) ) return false
2024-09-09 19:59:36 +03:00
return info . enabled
} ,
'Can constrain horizontally align' : ( { context : { selectionRanges } } ) = > {
const info = horzVertDistanceInfo ( {
2024-11-21 15:04:30 +11:00
selectionRanges : selectionRanges ,
2024-09-09 19:59:36 +03:00
constraint : 'setHorzDistance' ,
} )
2025-04-15 23:27:50 +10:00
if ( err ( info ) ) return false
2024-09-09 19:59:36 +03:00
return info . enabled
} ,
'Can constrain vertically align' : ( { context : { selectionRanges } } ) = > {
const info = horzVertDistanceInfo ( {
2024-11-21 15:04:30 +11:00
selectionRanges : selectionRanges ,
2024-09-09 19:59:36 +03:00
constraint : 'setHorzDistance' ,
} )
2025-04-15 23:27:50 +10:00
if ( err ( info ) ) return false
2024-09-09 19:59:36 +03:00
return info . enabled
} ,
'Can constrain snap to X' : ( { context : { selectionRanges } } ) = > {
const info = absDistanceInfo ( {
selectionRanges ,
constraint : 'snapToXAxis' ,
} )
2025-04-15 23:27:50 +10:00
if ( err ( info ) ) return false
2024-09-09 19:59:36 +03:00
return info . enabled
} ,
'Can constrain snap to Y' : ( { context : { selectionRanges } } ) = > {
const info = absDistanceInfo ( {
selectionRanges ,
constraint : 'snapToYAxis' ,
} )
2025-04-15 23:27:50 +10:00
if ( err ( info ) ) return false
2024-09-09 19:59:36 +03:00
return info . enabled
} ,
'Can constrain equal length' : ( { context : { selectionRanges } } ) = > {
2024-11-21 15:04:30 +11:00
const info = setEqualLengthInfo ( {
selectionRanges ,
} )
2025-04-15 23:27:50 +10:00
if ( err ( info ) ) return false
2024-09-09 19:59:36 +03:00
return info . enabled
} ,
2025-04-15 23:27:50 +10:00
'Can constrain parallel' : ( { context : { selectionRanges } } ) = > {
2024-11-21 15:04:30 +11:00
const info = equalAngleInfo ( {
selectionRanges ,
} )
2024-09-09 19:59:36 +03:00
if ( err ( info ) ) return false
return info . enabled
} ,
'Can constrain remove constraints' : ( {
context : { selectionRanges } ,
event ,
} ) = > {
if ( event . type !== 'Constrain remove constraints' ) return false
const info = removeConstrainingValuesInfo ( {
selectionRanges ,
pathToNodes : event.data && [ event . data ] ,
} )
2025-04-15 23:27:50 +10:00
if ( err ( info ) ) return false
2024-09-09 19:59:36 +03:00
return info . enabled
} ,
2024-12-09 16:43:58 -05:00
'Can convert to named value' : ( { event } ) = > {
if ( event . type !== 'Constrain with named value' ) return false
2024-09-09 19:59:36 +03:00
if ( ! event . data ) return false
const ast = parse ( recast ( kclManager . ast ) )
2024-12-06 13:57:31 +13:00
if ( err ( ast ) || ! ast . program || ast . errors . length > 0 ) return false
const isSafeRetVal = isNodeSafeToReplacePath (
ast . program ,
2024-12-09 16:43:58 -05:00
event . data . currentValue . pathToNode
2024-12-06 13:57:31 +13:00
)
2024-09-09 19:59:36 +03:00
if ( err ( isSafeRetVal ) ) return false
return isSafeRetVal . isSafe
} ,
'next is tangential arc' : ( { context : { sketchDetails , currentTool } } ) = >
currentTool === 'tangentialArc' &&
isEditingExistingSketch ( { sketchDetails } ) ,
2025-02-15 00:57:04 +11:00
'next is rectangle' : ( { context : { currentTool } } ) = >
currentTool === 'rectangle' ,
'next is center rectangle' : ( { context : { currentTool } } ) = >
currentTool === 'center rectangle' ,
'next is circle' : ( { context : { currentTool } } ) = >
currentTool === 'circle' ,
'next is circle three point' : ( { context : { currentTool } } ) = >
currentTool === 'circleThreePoint' ,
'next is circle three point neo' : ( { context : { currentTool } } ) = >
2025-03-18 11:14:12 +11:00
currentTool === 'circleThreePoint' ,
2024-09-09 19:59:36 +03:00
'next is line' : ( { context } ) = > context . currentTool === 'line' ,
2024-09-11 06:09:39 +10:00
'next is none' : ( { context } ) = > context . currentTool === 'none' ,
2025-03-18 11:14:12 +11:00
'next is arc' : ( { context } ) = > context . currentTool === 'arc' ,
'next is arc three point' : ( { context } ) = >
context . currentTool === 'arcThreePoint' ,
2024-09-09 19:59:36 +03:00
} ,
// end guards
actions : {
2025-01-31 13:34:38 -05:00
toastError : ( { event } ) = > {
if ( 'output' in event && event . output instanceof Error ) {
toast . error ( event . output . message )
} else if ( 'data' in event && event . data instanceof Error ) {
toast . error ( event . data . message )
2025-04-15 23:27:50 +10:00
} else if ( 'error' in event && event . error instanceof Error ) {
toast . error ( event . error . message )
2025-01-31 13:34:38 -05:00
}
} ,
2024-09-09 19:59:36 +03:00
'assign tool in context' : assign ( {
currentTool : ( { event } ) = >
2025-03-28 14:56:48 +11:00
event . type === 'change tool' ? event . data . tool || 'none' : 'none' ,
2024-09-09 19:59:36 +03:00
} ) ,
2024-11-26 20:43:28 +11:00
'reset selections' : assign ( {
selectionRanges : { graphSelections : [ ] , otherSelections : [ ] } ,
} ) ,
2024-09-09 19:59:36 +03:00
'enter sketching mode' : assign ( { currentMode : 'sketching' } ) ,
'enter modeling mode' : assign ( { currentMode : 'modeling' } ) ,
'set sketchMetadata from pathToNode' : assign (
( { context : { sketchDetails } } ) = > {
2025-02-15 00:57:04 +11:00
if ( ! sketchDetails ? . sketchEntryNodePath || ! sketchDetails ) return { }
2024-09-09 19:59:36 +03:00
return {
sketchDetails : {
. . . sketchDetails ,
2025-02-15 00:57:04 +11:00
sketchEntryNodePath : sketchDetails.sketchEntryNodePath ,
2024-08-14 14:26:44 -04:00
} ,
2024-09-09 19:59:36 +03:00
}
}
) ,
2025-04-25 18:21:19 +02:00
'hide default planes' : assign ( {
defaultPlaneVisibility : ( ) = > {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
kclManager . hidePlanes ( )
return { xy : false , xz : false , yz : false }
} ,
} ) ,
2024-09-09 19:59:36 +03:00
'reset sketch metadata' : assign ( {
sketchDetails : null ,
sketchEnginePathId : '' ,
sketchPlaneId : '' ,
} ) ,
2024-09-09 18:17:45 -04:00
'reset camera position' : ( ) = > {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
2024-09-09 19:59:36 +03:00
engineCommandManager . sendSceneCommand ( {
type : 'modeling_cmd_req' ,
cmd_id : uuidv4 ( ) ,
cmd : {
type : 'default_camera_look_at' ,
center : { x : 0 , y : 0 , z : 0 } ,
vantage : { x : 0 , y : - 1250 , z : 580 } ,
up : { x : 0 , y : 0 , z : 1 } ,
2023-10-11 13:36:54 +11:00
} ,
2024-09-09 18:17:45 -04:00
} )
} ,
2024-09-09 19:59:36 +03:00
'set new sketch metadata' : assign ( ( { event } ) = > {
if (
event . type !== 'xstate.done.actor.animate-to-sketch' &&
event . type !== 'xstate.done.actor.animate-to-face'
)
return { }
return {
sketchDetails : event.output ,
}
} ) ,
'tear down client sketch' : ( ) = > {
2025-04-17 22:15:54 +10:00
sceneEntitiesManager . tearDownSketch ( { removeAxis : false } )
2024-09-09 19:59:36 +03:00
} ,
'remove sketch grid' : ( ) = > sceneEntitiesManager . removeSketchGrid ( ) ,
2025-02-15 00:57:04 +11:00
'set up draft line' : assign ( ( { context : { sketchDetails } , event } ) = > {
if ( ! sketchDetails ) return { }
if ( event . type !== 'Add start point' ) return { }
2024-11-16 16:49:44 -05:00
2024-09-09 18:17:45 -04:00
// eslint-disable-next-line @typescript-eslint/no-floating-promises
2024-11-16 16:49:44 -05:00
sceneEntitiesManager
. setupDraftSegment (
2025-02-15 00:57:04 +11:00
event . data . sketchEntryNodePath || sketchDetails . sketchEntryNodePath ,
event . data . sketchNodePaths || sketchDetails . sketchNodePaths ,
sketchDetails . planeNodePath ,
2024-11-16 16:49:44 -05:00
sketchDetails . zAxis ,
sketchDetails . yAxis ,
sketchDetails . origin ,
'line'
)
. then ( ( ) = > {
return codeManager . updateEditorWithAstAndWriteToFile ( kclManager . ast )
} )
2025-02-15 00:57:04 +11:00
return {
sketchDetails : {
. . . sketchDetails ,
sketchEntryNodePath : event.data.sketchEntryNodePath ,
sketchNodePaths : event.data.sketchNodePaths ,
} ,
}
} ) ,
'set up draft arc' : assign ( ( { context : { sketchDetails } , event } ) = > {
if ( ! sketchDetails ) return { }
if ( event . type !== 'Continue existing profile' ) return { }
2024-11-16 16:49:44 -05:00
2024-09-09 18:17:45 -04:00
// eslint-disable-next-line @typescript-eslint/no-floating-promises
2024-11-16 16:49:44 -05:00
sceneEntitiesManager
. setupDraftSegment (
2025-02-15 00:57:04 +11:00
event . data . sketchEntryNodePath || sketchDetails . sketchEntryNodePath ,
event . data . sketchNodePaths || sketchDetails . sketchNodePaths ,
sketchDetails . planeNodePath ,
2024-11-16 16:49:44 -05:00
sketchDetails . zAxis ,
sketchDetails . yAxis ,
sketchDetails . origin ,
2025-04-11 14:17:20 -04:00
'tangentialArc'
2024-11-16 16:49:44 -05:00
)
. then ( ( ) = > {
return codeManager . updateEditorWithAstAndWriteToFile ( kclManager . ast )
} )
2025-02-15 00:57:04 +11:00
return {
sketchDetails : {
. . . sketchDetails ,
sketchEntryNodePath : event.data.sketchEntryNodePath ,
sketchNodePaths : event.data.sketchNodePaths ,
} ,
}
} ) ,
2024-09-09 19:59:36 +03:00
'listen for rectangle origin' : ( { context : { sketchDetails } } ) = > {
if ( ! sketchDetails ) return
2025-02-15 00:57:04 +11:00
const quaternion = quaternionFromUpNForward (
new Vector3 ( . . . sketchDetails . yAxis ) ,
new Vector3 ( . . . sketchDetails . zAxis )
)
// Position the click raycast plane
2025-03-21 01:55:20 +01:00
sceneEntitiesManager . intersectionPlane . setRotationFromQuaternion (
quaternion
)
sceneEntitiesManager . intersectionPlane . position . copy (
new Vector3 ( . . . ( sketchDetails ? . origin || [ 0 , 0 , 0 ] ) )
)
2025-02-15 00:57:04 +11:00
sceneInfra . setCallbacks ( {
onClick : ( args ) = > {
2025-03-17 19:45:20 +01:00
if ( ! args ) return
if ( args . mouseEvent . which !== 1 ) return
2024-09-09 19:59:36 +03:00
const twoD = args . intersectionPoint ? . twoD
if ( twoD ) {
sceneInfra . modelingSend ( {
2025-03-18 11:14:12 +11:00
type : 'click in scene' ,
2025-04-14 23:51:14 +02:00
data : sceneEntitiesManager.getSnappedDragPoint (
twoD ,
args . intersects ,
args . mouseEvent
) . snappedPoint ,
2024-09-09 19:59:36 +03:00
} )
} else {
console . error ( 'No intersection point found' )
}
} ,
} )
} ,
2024-11-18 10:04:09 -05:00
'listen for center rectangle origin' : ( { context : { sketchDetails } } ) = > {
if ( ! sketchDetails ) return
2025-02-15 00:57:04 +11:00
const quaternion = quaternionFromUpNForward (
new Vector3 ( . . . sketchDetails . yAxis ) ,
new Vector3 ( . . . sketchDetails . zAxis )
)
// Position the click raycast plane
2025-03-21 01:55:20 +01:00
sceneEntitiesManager . intersectionPlane . setRotationFromQuaternion (
quaternion
)
sceneEntitiesManager . intersectionPlane . position . copy (
new Vector3 ( . . . ( sketchDetails ? . origin || [ 0 , 0 , 0 ] ) )
)
2025-02-15 00:57:04 +11:00
sceneInfra . setCallbacks ( {
onClick : ( args ) = > {
2025-03-17 19:45:20 +01:00
if ( ! args ) return
if ( args . mouseEvent . which !== 1 ) return
2024-11-18 10:04:09 -05:00
const twoD = args . intersectionPoint ? . twoD
if ( twoD ) {
sceneInfra . modelingSend ( {
type : 'Add center rectangle origin' ,
data : [ twoD . x , twoD . y ] ,
} )
} else {
console . error ( 'No intersection point found' )
}
} ,
} )
} ,
2024-09-23 22:42:51 +10:00
'listen for circle origin' : ( { context : { sketchDetails } } ) = > {
if ( ! sketchDetails ) return
const quaternion = quaternionFromUpNForward (
new Vector3 ( . . . sketchDetails . yAxis ) ,
new Vector3 ( . . . sketchDetails . zAxis )
)
// Position the click raycast plane
2025-03-21 01:55:20 +01:00
sceneEntitiesManager . intersectionPlane . setRotationFromQuaternion (
quaternion
)
sceneEntitiesManager . intersectionPlane . position . copy (
new Vector3 ( . . . ( sketchDetails ? . origin || [ 0 , 0 , 0 ] ) )
)
2024-09-23 22:42:51 +10:00
sceneInfra . setCallbacks ( {
onClick : ( args ) = > {
if ( ! args ) return
if ( args . mouseEvent . which !== 1 ) return
const { intersectionPoint } = args
2025-03-20 08:30:11 +11:00
if ( ! intersectionPoint ? . twoD ) return
2024-09-23 22:42:51 +10:00
const twoD = args . intersectionPoint ? . twoD
if ( twoD ) {
sceneInfra . modelingSend ( {
type : 'Add circle origin' ,
data : [ twoD . x , twoD . y ] ,
} )
} else {
console . error ( 'No intersection point found' )
}
} ,
} )
} ,
2025-02-15 00:57:04 +11:00
'listen for circle first point' : ( { context : { sketchDetails } } ) = > {
if ( ! sketchDetails ) return
const quaternion = quaternionFromUpNForward (
new Vector3 ( . . . sketchDetails . yAxis ) ,
new Vector3 ( . . . sketchDetails . zAxis )
)
2024-12-16 10:34:11 -05:00
2025-02-15 00:57:04 +11:00
// Position the click raycast plane
2025-03-21 01:55:20 +01:00
sceneEntitiesManager . intersectionPlane . setRotationFromQuaternion (
quaternion
)
sceneEntitiesManager . intersectionPlane . position . copy (
new Vector3 ( . . . ( sketchDetails ? . origin || [ 0 , 0 , 0 ] ) )
)
2025-02-15 00:57:04 +11:00
sceneInfra . setCallbacks ( {
onClick : ( args ) = > {
if ( ! args ) return
if ( args . mouseEvent . which !== 1 ) return
const { intersectionPoint } = args
2025-03-20 08:30:11 +11:00
if ( ! intersectionPoint ? . twoD ) return
2025-02-15 00:57:04 +11:00
const twoD = args . intersectionPoint ? . twoD
if ( twoD ) {
sceneInfra . modelingSend ( {
type : 'Add first point' ,
data : [ twoD . x , twoD . y ] ,
} )
} else {
console . error ( 'No intersection point found' )
}
} ,
} )
2024-12-16 10:34:11 -05:00
} ,
2025-02-15 00:57:04 +11:00
'listen for circle second point' : ( {
2024-12-16 10:34:11 -05:00
context : { sketchDetails } ,
event ,
} ) = > {
2025-02-15 00:57:04 +11:00
if ( ! sketchDetails ) return
if ( event . type !== 'Add first point' ) return
const quaternion = quaternionFromUpNForward (
new Vector3 ( . . . sketchDetails . yAxis ) ,
new Vector3 ( . . . sketchDetails . zAxis )
2024-12-14 09:57:33 +11:00
)
2024-12-16 10:34:11 -05:00
2025-02-15 00:57:04 +11:00
// Position the click raycast plane
2025-03-21 01:55:20 +01:00
sceneEntitiesManager . intersectionPlane . setRotationFromQuaternion (
quaternion
)
sceneEntitiesManager . intersectionPlane . position . copy (
new Vector3 ( . . . ( sketchDetails ? . origin || [ 0 , 0 , 0 ] ) )
)
2025-02-15 00:57:04 +11:00
const dummy = new Mesh ( )
dummy . position . set ( 0 , 0 , 0 )
const scale = sceneInfra . getClientSceneScaleFactor ( dummy )
const position = new Vector3 ( event . data [ 0 ] , event . data [ 1 ] , 0 )
position . applyQuaternion ( quaternion )
const draftPoint = createProfileStartHandle ( {
isDraft : true ,
from : event . data ,
scale ,
theme : sceneInfra._theme ,
} )
draftPoint . position . copy ( position )
sceneInfra . scene . add ( draftPoint )
sceneInfra . setCallbacks ( {
onClick : ( args ) = > {
if ( ! args ) return
if ( args . mouseEvent . which !== 1 ) return
const { intersectionPoint } = args
2025-03-20 08:30:11 +11:00
if ( ! intersectionPoint ? . twoD ) return
2025-02-15 00:57:04 +11:00
const twoD = args . intersectionPoint ? . twoD
if ( twoD ) {
sceneInfra . modelingSend ( {
type : 'Add second point' ,
data : {
p1 : event.data ,
p2 : [ twoD . x , twoD . y ] ,
} ,
} )
} else {
console . error ( 'No intersection point found' )
}
} ,
} )
2024-12-16 10:34:11 -05:00
} ,
2025-02-15 00:57:04 +11:00
'update sketchDetails' : assign ( ( { event , context } ) = > {
if (
event . type !== 'xstate.done.actor.actor-circle-three-point' &&
event . type !== 'xstate.done.actor.set-up-draft-circle' &&
2025-03-18 11:14:12 +11:00
event . type !== 'xstate.done.actor.set-up-draft-arc' &&
event . type !== 'xstate.done.actor.set-up-draft-arc-three-point' &&
2025-02-15 00:57:04 +11:00
event . type !== 'xstate.done.actor.set-up-draft-circle-three-point' &&
event . type !== 'xstate.done.actor.set-up-draft-rectangle' &&
event . type !== 'xstate.done.actor.set-up-draft-center-rectangle' &&
2025-04-15 23:27:50 +10:00
event . type !== 'xstate.done.actor.split-sketch-pipe-if-needed' &&
event . type !== 'xstate.done.actor.reeval-node-paths'
) {
2025-02-15 00:57:04 +11:00
return { }
2025-04-15 23:27:50 +10:00
}
2025-02-15 00:57:04 +11:00
if ( ! context . sketchDetails ) return { }
return {
sketchDetails : {
. . . context . sketchDetails ,
planeNodePath :
event . output . updatedPlaneNodePath ||
context . sketchDetails ? . planeNodePath ||
[ ] ,
sketchEntryNodePath : event.output.updatedEntryNodePath ,
sketchNodePaths : event.output.updatedSketchNodePaths ,
expressionIndexToDelete : event.output.expressionIndexToDelete ,
} ,
}
} ) ,
2025-03-18 11:14:12 +11:00
'update sketchDetails arc' : assign ( ( { event , context } ) = > {
if ( event . type !== 'Add start point' ) return { }
if ( ! context . sketchDetails ) return { }
return {
sketchDetails : {
. . . context . sketchDetails ,
sketchEntryNodePath : event.data.sketchEntryNodePath ,
sketchNodePaths : event.data.sketchNodePaths ,
} ,
}
} ) ,
2025-04-25 18:21:19 +02:00
'show default planes' : assign ( {
defaultPlaneVisibility : ( ) = > {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
kclManager . showPlanes ( )
return { xy : true , xz : true , yz : true }
} ,
} ) ,
'show default planes if no errors' : assign ( {
defaultPlaneVisibility : ( { context } ) = > {
if ( ! kclManager . hasErrors ( ) ) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
kclManager . showPlanes ( )
return { xy : true , xz : true , yz : true }
}
return { . . . context . defaultPlaneVisibility }
} ,
} ) ,
2025-02-15 00:57:04 +11:00
'setup noPoints onClick listener' : ( {
context : { sketchDetails , currentTool } ,
} ) = > {
2024-09-09 19:59:36 +03:00
if ( ! sketchDetails ) return
sceneEntitiesManager . setupNoPointsListener ( {
sketchDetails ,
2025-02-15 00:57:04 +11:00
currentTool ,
afterClick : ( _ , data ) = >
sceneInfra . modelingSend (
currentTool === 'tangentialArc'
? { type : 'Continue existing profile' , data }
2025-03-18 11:14:12 +11:00
: currentTool === 'arc'
2025-04-01 23:54:26 -07:00
? { type : 'Add start point' , data }
: { type : 'Add start point' , data }
2025-02-15 00:57:04 +11:00
) ,
2024-09-09 19:59:36 +03:00
} )
} ,
'add axis n grid' : ( { context : { sketchDetails } } ) = > {
if ( ! sketchDetails ) return
if ( localStorage . getItem ( 'disableAxis' ) ) return
2024-11-16 16:49:44 -05:00
// eslint-disable-next-line @typescript-eslint/no-floating-promises
2024-09-09 19:59:36 +03:00
sceneEntitiesManager . createSketchAxis (
sketchDetails . zAxis ,
sketchDetails . yAxis ,
sketchDetails . origin
)
2024-11-16 16:49:44 -05:00
// eslint-disable-next-line @typescript-eslint/no-floating-promises
codeManager . updateEditorWithAstAndWriteToFile ( kclManager . ast )
2024-09-09 19:59:36 +03:00
} ,
'reset client scene mouse handlers' : ( ) = > {
// when not in sketch mode we don't need any mouse listeners
// (note the orbit controls are always active though)
sceneInfra . resetMouseListeners ( )
} ,
'clientToEngine cam sync direction' : ( ) = > {
sceneInfra . camControls . syncDirection = 'clientToEngine'
} ,
'engineToClient cam sync direction' : ( ) = > {
sceneInfra . camControls . syncDirection = 'engineToClient'
} ,
2024-11-26 11:36:14 -05:00
/** TODO: this action is hiding unawaited asynchronous code */
2024-09-09 18:17:45 -04:00
'set selection filter to faces only' : ( ) = > {
2024-11-26 11:36:14 -05:00
kclManager . setSelectionFilter ( [ 'face' , 'object' ] )
2024-09-09 18:17:45 -04:00
} ,
2024-11-26 11:36:14 -05:00
/** TODO: this action is hiding unawaited asynchronous code */
2025-04-25 18:21:19 +02:00
'set selection filter to defaults' : ( ) = > {
kclManager . defaultSelectionFilter ( )
} ,
2024-09-09 19:59:36 +03:00
'Delete segment' : ( { context : { sketchDetails } , event } ) = > {
if ( event . type !== 'Delete segment' ) return
if ( ! sketchDetails || ! event . data ) return
2024-11-16 16:49:44 -05:00
2024-09-09 18:17:45 -04:00
// eslint-disable-next-line @typescript-eslint/no-floating-promises
deleteSegment ( {
2024-09-09 19:59:36 +03:00
pathToNode : event.data ,
sketchDetails ,
2024-11-16 16:49:44 -05:00
} ) . then ( ( ) = > {
return codeManager . updateEditorWithAstAndWriteToFile ( kclManager . ast )
2024-09-09 19:59:36 +03:00
} )
} ,
'Reset Segment Overlays' : ( ) = > sceneEntitiesManager . resetOverlays ( ) ,
'Set context' : assign ( {
store : ( { context : { store } , event } ) = > {
if ( event . type !== 'Set context' ) return store
if ( ! event . data ) return store
2024-08-03 07:05:35 +10:00
2024-09-09 19:59:36 +03:00
const result = {
. . . store ,
. . . event . data ,
}
const persistedContext : Partial < PersistedModelingContext > = { }
for ( const key of PersistedValues ) {
persistedContext [ key ] = result [ key ]
}
if ( typeof window !== 'undefined' ) {
window . localStorage . setItem (
PERSIST_MODELING_CONTEXT ,
JSON . stringify ( persistedContext )
)
}
return result
2023-10-11 13:36:54 +11:00
} ,
2024-09-09 19:59:36 +03:00
} ) ,
2025-03-18 11:14:12 +11:00
'remove draft entities' : ( ) = > {
2025-02-15 00:57:04 +11:00
const draftPoint = sceneInfra . scene . getObjectByName ( DRAFT_POINT )
if ( draftPoint ) {
sceneInfra . scene . remove ( draftPoint )
}
2025-03-18 11:14:12 +11:00
const draftLine = sceneInfra . scene . getObjectByName ( DRAFT_DASHED_LINE )
if ( draftLine ) {
sceneInfra . scene . remove ( draftLine )
}
} ,
'add draft line' : ( { event , context } ) = > {
if (
event . type !== 'Add start point' &&
event . type !== 'xstate.done.actor.setup-client-side-sketch-segments9'
)
return
let sketchEntryNodePath : PathToNode | undefined
if ( event . type === 'Add start point' ) {
sketchEntryNodePath = event . data ? . sketchEntryNodePath
} else if (
event . type === 'xstate.done.actor.setup-client-side-sketch-segments9'
) {
sketchEntryNodePath =
context . sketchDetails ? . sketchNodePaths . slice ( - 1 ) [ 0 ]
}
if ( ! sketchEntryNodePath ) return
const varDec = getNodeFromPath < VariableDeclaration > (
kclManager . ast ,
sketchEntryNodePath ,
'VariableDeclaration'
)
if ( err ( varDec ) ) return
const varName = varDec . node . declaration . id . name
const sg = sketchFromKclValue ( kclManager . variables [ varName ] , varName )
if ( err ( sg ) ) return
const lastSegment = sg . paths [ sg . paths . length - 1 ] || sg . start
const to = lastSegment . to
const { group , updater } = sceneEntitiesManager . drawDashedLine ( {
from : to ,
to : [ to [ 0 ] + 0.001 , to [ 1 ] + 0.001 ] ,
} )
sceneInfra . scene . add ( group )
const orthoFactor = orthoScale ( sceneInfra . camControls . camera )
sceneInfra . setCallbacks ( {
onMove : ( args ) = > {
const { intersectionPoint } = args
if ( ! intersectionPoint ? . twoD ) return
if ( ! context . sketchDetails ) return
const { snappedPoint , isSnapped } =
2025-04-14 23:51:14 +02:00
sceneEntitiesManager . getSnappedDragPoint (
intersectionPoint . twoD ,
args . intersects ,
args . mouseEvent
)
2025-03-18 11:14:12 +11:00
if ( isSnapped ) {
sceneEntitiesManager . positionDraftPoint ( {
snappedPoint : new Vector2 ( . . . snappedPoint ) ,
origin : context.sketchDetails.origin ,
yAxis : context.sketchDetails.yAxis ,
zAxis : context.sketchDetails.zAxis ,
} )
} else {
sceneEntitiesManager . removeDraftPoint ( )
}
updater (
group ,
[ intersectionPoint . twoD . x , intersectionPoint . twoD . y ] ,
orthoFactor
)
} ,
} )
2025-02-15 00:57:04 +11:00
} ,
'reset deleteIndex' : assign ( ( { context : { sketchDetails } } ) = > {
if ( ! sketchDetails ) return { }
return {
sketchDetails : {
. . . sketchDetails ,
expressionIndexToDelete : - 1 ,
} ,
}
} ) ,
2024-09-09 19:59:36 +03:00
'enable copilot' : ( ) = > { } ,
'disable copilot' : ( ) = > { } ,
'Set selection' : ( ) = > { } ,
'Set mouse state' : ( ) = > { } ,
'Set Segment Overlays' : ( ) = > { } ,
2024-10-04 13:47:44 -07:00
'Center camera on selection' : ( ) = > { } ,
2024-09-09 19:59:36 +03:00
'Submit to Text-to-CAD API' : ( ) = > { } ,
'Set sketchDetails' : ( ) = > { } ,
'sketch exit execute' : ( ) = > { } ,
2025-03-20 08:30:11 +11:00
'debug-action' : ( data ) = > {
console . log ( 're-eval debug-action' , data )
} ,
2025-04-25 18:21:19 +02:00
'Toggle default plane visibility' : assign ( ( { context , event } ) = > {
if ( event . type !== 'Toggle default plane visibility' ) return { }
const currentVisibilityMap = context . defaultPlaneVisibility
const currentVisibility = currentVisibilityMap [ event . planeKey ]
const newVisibility = ! currentVisibility
kclManager . engineCommandManager
. setPlaneHidden ( event . planeId , ! newVisibility )
. catch ( reportRejection )
return {
defaultPlaneVisibility : {
. . . currentVisibilityMap ,
[ event . planeKey ] : newVisibility ,
} ,
}
} ) ,
// Saves the default plane visibility to be able to restore when going back from sketch mode
'Save default plane visibility' : assign ( ( { context , event } ) = > {
return {
savedDefaultPlaneVisibility : {
. . . context . defaultPlaneVisibility ,
} ,
}
} ) ,
'Restore default plane visibility' : assign ( ( { context } ) = > {
for ( const planeKey of Object . keys (
context . savedDefaultPlaneVisibility
) as ( keyof PlaneVisibilityMap ) [ ] ) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
kclManager . setPlaneVisibilityByKey (
planeKey ,
context . savedDefaultPlaneVisibility [ planeKey ]
)
}
return {
defaultPlaneVisibility : {
. . . context . defaultPlaneVisibility ,
. . . context . savedDefaultPlaneVisibility ,
} ,
}
} ) ,
2025-04-15 23:27:50 +10:00
'show sketch error toast' : assign ( ( ) = > {
// toast message that stays open until closed programmatically
const toastId = toast . error (
"Error in kcl script, sketch cannot be drawn until it's fixed" ,
{ duration : Infinity }
)
return {
toastId ,
}
} ) ,
'remove sketch error toast' : assign ( ( { context } ) = > {
if ( context . toastId ) {
toast . dismiss ( context . toastId )
return { toastId : null }
}
return { }
} ) ,
2023-10-11 13:36:54 +11:00
} ,
2024-09-09 19:59:36 +03:00
// end actions
actors : {
'do-constrain-remove-constraint' : fromPromise (
async ( {
input : { selectionRanges , sketchDetails , data } ,
} : {
input : Pick <
ModelingMachineContext ,
'selectionRanges' | 'sketchDetails'
> & { data? : PathToNode }
2024-06-22 04:49:31 -04:00
} ) = > {
2024-06-24 11:45:40 -04:00
const constraint = applyRemoveConstrainingValues ( {
2024-06-03 22:40:59 +10:00
selectionRanges ,
2024-06-04 16:29:20 +10:00
pathToNodes : data && [ data ] ,
2024-06-03 22:40:59 +10:00
} )
2024-06-24 11:45:40 -04:00
if ( trap ( constraint ) ) return
const { pathToNodeMap } = constraint
2024-06-03 22:40:59 +10:00
if ( ! sketchDetails ) return
2024-06-24 11:45:40 -04:00
let updatedAst = await sceneEntitiesManager . updateAstAndRejigSketch (
pathToNodeMap [ 0 ] ,
2025-02-15 00:57:04 +11:00
sketchDetails . sketchNodePaths ,
sketchDetails . planeNodePath ,
2024-06-24 11:45:40 -04:00
constraint . modifiedAst ,
2024-06-03 22:40:59 +10:00
sketchDetails . zAxis ,
sketchDetails . yAxis ,
sketchDetails . origin
)
2024-06-24 11:45:40 -04:00
if ( trap ( updatedAst , { suppress : true } ) ) return
if ( ! updatedAst ) return
2024-11-16 16:49:44 -05:00
await codeManager . updateEditorWithAstAndWriteToFile ( updatedAst . newAst )
2024-06-03 22:40:59 +10:00
return {
selectionType : 'completeSelection' ,
selection : updateSelections (
pathToNodeMap ,
selectionRanges ,
2024-06-24 11:45:40 -04:00
updatedAst . newAst
2024-06-03 22:40:59 +10:00
) ,
}
2024-09-09 19:59:36 +03:00
}
) ,
'do-constrain-horizontally' : fromPromise (
async ( {
input : { selectionRanges , sketchDetails } ,
} : {
input : Pick < ModelingMachineContext , 'selectionRanges' | 'sketchDetails' >
2024-05-30 19:43:35 +10:00
} ) = > {
2024-06-24 11:45:40 -04:00
const constraint = applyConstraintHorzVert (
2024-05-30 19:43:35 +10:00
selectionRanges ,
'horizontal' ,
kclManager . ast ,
2025-02-12 10:22:56 +13:00
kclManager . variables
2024-05-30 19:43:35 +10:00
)
2024-06-24 11:45:40 -04:00
if ( trap ( constraint ) ) return false
const { modifiedAst , pathToNodeMap } = constraint
2024-05-30 19:43:35 +10:00
if ( ! sketchDetails ) return
2024-06-24 11:45:40 -04:00
const updatedAst = await sceneEntitiesManager . updateAstAndRejigSketch (
2025-02-15 00:57:04 +11:00
sketchDetails . sketchEntryNodePath ,
sketchDetails . sketchNodePaths ,
sketchDetails . planeNodePath ,
2024-05-30 19:43:35 +10:00
modifiedAst ,
sketchDetails . zAxis ,
sketchDetails . yAxis ,
sketchDetails . origin
)
2024-06-24 11:45:40 -04:00
if ( trap ( updatedAst , { suppress : true } ) ) return
if ( ! updatedAst ) return
2024-11-16 16:49:44 -05:00
await codeManager . updateEditorWithAstAndWriteToFile ( updatedAst . newAst )
2024-05-30 19:43:35 +10:00
return {
selectionType : 'completeSelection' ,
selection : updateSelections (
pathToNodeMap ,
selectionRanges ,
2024-06-24 11:45:40 -04:00
updatedAst . newAst
2024-05-30 19:43:35 +10:00
) ,
}
2024-09-09 19:59:36 +03:00
}
) ,
'do-constrain-vertically' : fromPromise (
async ( {
input : { selectionRanges , sketchDetails } ,
} : {
input : Pick < ModelingMachineContext , 'selectionRanges' | 'sketchDetails' >
} ) = > {
2024-06-24 11:45:40 -04:00
const constraint = applyConstraintHorzVert (
2024-05-30 19:43:35 +10:00
selectionRanges ,
'vertical' ,
kclManager . ast ,
2025-02-12 10:22:56 +13:00
kclManager . variables
2024-05-30 19:43:35 +10:00
)
2024-06-24 11:45:40 -04:00
if ( trap ( constraint ) ) return false
const { modifiedAst , pathToNodeMap } = constraint
2024-05-30 19:43:35 +10:00
if ( ! sketchDetails ) return
2024-06-24 11:45:40 -04:00
const updatedAst = await sceneEntitiesManager . updateAstAndRejigSketch (
2025-02-15 00:57:04 +11:00
sketchDetails . sketchEntryNodePath || [ ] ,
sketchDetails . sketchNodePaths ,
sketchDetails . planeNodePath ,
2024-05-30 19:43:35 +10:00
modifiedAst ,
sketchDetails . zAxis ,
sketchDetails . yAxis ,
sketchDetails . origin
)
2024-06-24 11:45:40 -04:00
if ( trap ( updatedAst , { suppress : true } ) ) return
if ( ! updatedAst ) return
2024-11-16 16:49:44 -05:00
await codeManager . updateEditorWithAstAndWriteToFile ( updatedAst . newAst )
2024-05-30 19:43:35 +10:00
return {
selectionType : 'completeSelection' ,
selection : updateSelections (
pathToNodeMap ,
selectionRanges ,
2024-06-24 11:45:40 -04:00
updatedAst . newAst
2024-05-30 19:43:35 +10:00
) ,
}
2024-09-09 19:59:36 +03:00
}
) ,
'do-constrain-horizontally-align' : fromPromise (
async ( {
input : { selectionRanges , sketchDetails } ,
} : {
input : Pick < ModelingMachineContext , 'selectionRanges' | 'sketchDetails' >
2024-05-30 13:28:29 +10:00
} ) = > {
2024-06-24 11:45:40 -04:00
const constraint = applyConstraintHorzVertAlign ( {
2024-11-21 15:04:30 +11:00
selectionRanges : selectionRanges ,
2024-05-30 13:28:29 +10:00
constraint : 'setVertDistance' ,
} )
2024-06-24 11:45:40 -04:00
if ( trap ( constraint ) ) return
const { modifiedAst , pathToNodeMap } = constraint
2024-05-30 13:28:29 +10:00
if ( ! sketchDetails ) return
2024-06-24 11:45:40 -04:00
const updatedAst = await sceneEntitiesManager . updateAstAndRejigSketch (
2025-02-15 00:57:04 +11:00
sketchDetails ? . sketchEntryNodePath || [ ] ,
sketchDetails . sketchNodePaths ,
sketchDetails . planeNodePath ,
2024-05-30 13:28:29 +10:00
modifiedAst ,
sketchDetails . zAxis ,
sketchDetails . yAxis ,
sketchDetails . origin
)
2024-06-24 11:45:40 -04:00
if ( trap ( updatedAst , { suppress : true } ) ) return
if ( ! updatedAst ) return
2024-11-16 16:49:44 -05:00
await codeManager . updateEditorWithAstAndWriteToFile ( updatedAst . newAst )
2024-05-30 13:28:29 +10:00
const updatedSelectionRanges = updateSelections (
pathToNodeMap ,
selectionRanges ,
2024-06-24 11:45:40 -04:00
updatedAst . newAst
2024-05-30 13:28:29 +10:00
)
return {
selectionType : 'completeSelection' ,
selection : updatedSelectionRanges ,
}
2024-09-09 19:59:36 +03:00
}
) ,
'do-constrain-vertically-align' : fromPromise (
async ( {
input : { selectionRanges , sketchDetails } ,
} : {
input : Pick < ModelingMachineContext , 'selectionRanges' | 'sketchDetails' >
2024-05-30 13:28:29 +10:00
} ) = > {
2024-06-24 11:45:40 -04:00
const constraint = applyConstraintHorzVertAlign ( {
2024-11-21 15:04:30 +11:00
selectionRanges : selectionRanges ,
2024-05-30 13:28:29 +10:00
constraint : 'setHorzDistance' ,
} )
2024-06-24 11:45:40 -04:00
if ( trap ( constraint ) ) return
const { modifiedAst , pathToNodeMap } = constraint
2024-05-30 13:28:29 +10:00
if ( ! sketchDetails ) return
2024-06-24 11:45:40 -04:00
const updatedAst = await sceneEntitiesManager . updateAstAndRejigSketch (
2025-02-15 00:57:04 +11:00
sketchDetails ? . sketchEntryNodePath || [ ] ,
sketchDetails . sketchNodePaths ,
sketchDetails . planeNodePath ,
2024-05-30 13:28:29 +10:00
modifiedAst ,
sketchDetails . zAxis ,
sketchDetails . yAxis ,
sketchDetails . origin
)
2024-06-24 11:45:40 -04:00
if ( trap ( updatedAst , { suppress : true } ) ) return
if ( ! updatedAst ) return
2024-11-16 16:49:44 -05:00
await codeManager . updateEditorWithAstAndWriteToFile ( updatedAst . newAst )
2024-05-30 13:28:29 +10:00
const updatedSelectionRanges = updateSelections (
pathToNodeMap ,
selectionRanges ,
2024-06-24 11:45:40 -04:00
updatedAst . newAst
2024-05-30 13:28:29 +10:00
)
return {
selectionType : 'completeSelection' ,
selection : updatedSelectionRanges ,
}
2024-09-09 19:59:36 +03:00
}
) ,
'do-constrain-snap-to-x' : fromPromise (
async ( {
input : { selectionRanges , sketchDetails } ,
} : {
input : Pick < ModelingMachineContext , 'selectionRanges' | 'sketchDetails' >
} ) = > {
2024-06-24 11:45:40 -04:00
const constraint = applyConstraintAxisAlign ( {
2024-05-30 13:28:29 +10:00
selectionRanges ,
constraint : 'snapToXAxis' ,
} )
2024-06-24 11:45:40 -04:00
if ( err ( constraint ) ) return false
const { modifiedAst , pathToNodeMap } = constraint
2024-05-30 13:28:29 +10:00
if ( ! sketchDetails ) return
2024-06-24 11:45:40 -04:00
const updatedAst = await sceneEntitiesManager . updateAstAndRejigSketch (
2025-02-15 00:57:04 +11:00
sketchDetails ? . sketchEntryNodePath || [ ] ,
sketchDetails . sketchNodePaths ,
sketchDetails . planeNodePath ,
2024-05-30 13:28:29 +10:00
modifiedAst ,
sketchDetails . zAxis ,
sketchDetails . yAxis ,
sketchDetails . origin
)
2024-06-24 11:45:40 -04:00
if ( trap ( updatedAst , { suppress : true } ) ) return
if ( ! updatedAst ) return
2024-11-16 16:49:44 -05:00
await codeManager . updateEditorWithAstAndWriteToFile ( updatedAst . newAst )
2024-05-30 13:28:29 +10:00
const updatedSelectionRanges = updateSelections (
pathToNodeMap ,
selectionRanges ,
2024-06-24 11:45:40 -04:00
updatedAst . newAst
2024-05-30 13:28:29 +10:00
)
return {
selectionType : 'completeSelection' ,
selection : updatedSelectionRanges ,
}
2024-09-09 19:59:36 +03:00
}
) ,
'do-constrain-snap-to-y' : fromPromise (
async ( {
input : { selectionRanges , sketchDetails } ,
} : {
input : Pick < ModelingMachineContext , 'selectionRanges' | 'sketchDetails' >
} ) = > {
2024-06-24 11:45:40 -04:00
const constraint = applyConstraintAxisAlign ( {
2024-05-30 13:28:29 +10:00
selectionRanges ,
constraint : 'snapToYAxis' ,
} )
2024-06-24 11:45:40 -04:00
if ( trap ( constraint ) ) return false
const { modifiedAst , pathToNodeMap } = constraint
2024-05-30 13:28:29 +10:00
if ( ! sketchDetails ) return
2024-06-24 11:45:40 -04:00
const updatedAst = await sceneEntitiesManager . updateAstAndRejigSketch (
2025-02-15 00:57:04 +11:00
sketchDetails ? . sketchEntryNodePath || [ ] ,
sketchDetails . sketchNodePaths ,
sketchDetails . planeNodePath ,
2024-05-30 13:28:29 +10:00
modifiedAst ,
sketchDetails . zAxis ,
sketchDetails . yAxis ,
sketchDetails . origin
)
2024-06-24 11:45:40 -04:00
if ( trap ( updatedAst , { suppress : true } ) ) return
if ( ! updatedAst ) return
2024-11-16 16:49:44 -05:00
await codeManager . updateEditorWithAstAndWriteToFile ( updatedAst . newAst )
2024-05-30 13:28:29 +10:00
const updatedSelectionRanges = updateSelections (
pathToNodeMap ,
selectionRanges ,
2024-06-24 11:45:40 -04:00
updatedAst . newAst
2024-05-30 13:28:29 +10:00
)
return {
selectionType : 'completeSelection' ,
selection : updatedSelectionRanges ,
}
2024-09-09 19:59:36 +03:00
}
) ,
'do-constrain-parallel' : fromPromise (
async ( {
input : { selectionRanges , sketchDetails } ,
} : {
input : Pick < ModelingMachineContext , 'selectionRanges' | 'sketchDetails' >
} ) = > {
2024-06-24 11:45:40 -04:00
const constraint = applyConstraintEqualAngle ( {
2024-05-30 13:28:29 +10:00
selectionRanges ,
} )
2024-06-24 11:45:40 -04:00
if ( trap ( constraint ) ) return false
const { modifiedAst , pathToNodeMap } = constraint
if ( ! sketchDetails ) {
trap ( new Error ( 'No sketch details' ) )
return
}
2024-12-06 13:57:31 +13:00
const recastAst = parse ( recast ( modifiedAst ) )
if ( err ( recastAst ) || ! resultIsOk ( recastAst ) ) return
2024-06-24 11:45:40 -04:00
const updatedAst = await sceneEntitiesManager . updateAstAndRejigSketch (
2025-02-15 00:57:04 +11:00
sketchDetails ? . sketchEntryNodePath || [ ] ,
sketchDetails . sketchNodePaths ,
sketchDetails . planeNodePath ,
2024-12-06 13:57:31 +13:00
recastAst . program ,
2024-05-30 13:28:29 +10:00
sketchDetails . zAxis ,
sketchDetails . yAxis ,
sketchDetails . origin
)
2024-06-24 11:45:40 -04:00
if ( trap ( updatedAst , { suppress : true } ) ) return
if ( ! updatedAst ) return
2024-11-16 16:49:44 -05:00
await codeManager . updateEditorWithAstAndWriteToFile ( updatedAst . newAst )
2024-05-30 13:28:29 +10:00
const updatedSelectionRanges = updateSelections (
pathToNodeMap ,
selectionRanges ,
2024-06-24 11:45:40 -04:00
updatedAst . newAst
2024-05-30 13:28:29 +10:00
)
return {
selectionType : 'completeSelection' ,
selection : updatedSelectionRanges ,
}
2024-09-09 19:59:36 +03:00
}
) ,
'do-constrain-equal-length' : fromPromise (
async ( {
input : { selectionRanges , sketchDetails } ,
} : {
input : Pick < ModelingMachineContext , 'selectionRanges' | 'sketchDetails' >
2024-05-30 13:28:29 +10:00
} ) = > {
2024-06-24 11:45:40 -04:00
const constraint = applyConstraintEqualLength ( {
2024-05-30 13:28:29 +10:00
selectionRanges ,
} )
2024-06-24 11:45:40 -04:00
if ( trap ( constraint ) ) return false
const { modifiedAst , pathToNodeMap } = constraint
2024-05-30 13:28:29 +10:00
if ( ! sketchDetails ) return
2024-06-24 11:45:40 -04:00
const updatedAst = await sceneEntitiesManager . updateAstAndRejigSketch (
2025-02-15 00:57:04 +11:00
sketchDetails ? . sketchEntryNodePath || [ ] ,
sketchDetails . sketchNodePaths ,
sketchDetails . planeNodePath ,
2024-05-30 13:28:29 +10:00
modifiedAst ,
sketchDetails . zAxis ,
sketchDetails . yAxis ,
sketchDetails . origin
)
2024-06-24 11:45:40 -04:00
if ( trap ( updatedAst , { suppress : true } ) ) return
if ( ! updatedAst ) return
2024-11-16 16:49:44 -05:00
await codeManager . updateEditorWithAstAndWriteToFile ( updatedAst . newAst )
2024-05-30 13:28:29 +10:00
const updatedSelectionRanges = updateSelections (
pathToNodeMap ,
selectionRanges ,
2024-06-24 11:45:40 -04:00
updatedAst . newAst
2024-05-30 13:28:29 +10:00
)
return {
selectionType : 'completeSelection' ,
selection : updatedSelectionRanges ,
}
2024-09-09 19:59:36 +03:00
}
) ,
'Get vertical info' : fromPromise (
async ( _ : {
input : Pick < ModelingMachineContext , 'selectionRanges' | 'sketchDetails' >
} ) = > {
return { } as SetSelections
}
) ,
'Get ABS X info' : fromPromise (
async ( _ : {
input : Pick < ModelingMachineContext , 'selectionRanges' | 'sketchDetails' >
} ) = > {
return { } as SetSelections
}
) ,
'Get ABS Y info' : fromPromise (
async ( _ : {
input : Pick < ModelingMachineContext , 'selectionRanges' | 'sketchDetails' >
} ) = > {
return { } as SetSelections
}
) ,
'Get angle info' : fromPromise (
async ( _ : {
input : Pick < ModelingMachineContext , 'selectionRanges' | 'sketchDetails' >
} ) = > {
return { } as SetSelections
}
) ,
'Get perpendicular distance info' : fromPromise (
async ( _ : {
input : Pick < ModelingMachineContext , 'selectionRanges' | 'sketchDetails' >
} ) = > {
return { } as SetSelections
}
) ,
'AST-undo-startSketchOn' : fromPromise (
async ( _ : { input : Pick < ModelingMachineContext , 'sketchDetails' > } ) = > {
return undefined
}
) ,
'animate-to-face' : fromPromise (
2024-11-18 16:25:25 -05:00
async ( _ : { input? : ExtrudeFacePlane | DefaultPlane | OffsetPlane } ) = > {
2025-02-15 00:57:04 +11:00
return { } as ModelingMachineContext [ 'sketchDetails' ]
2024-09-09 19:59:36 +03:00
}
) ,
'animate-to-sketch' : fromPromise (
async ( _ : { input : Pick < ModelingMachineContext , 'selectionRanges' > } ) = > {
2025-02-15 00:57:04 +11:00
return { } as ModelingMachineContext [ 'sketchDetails' ]
2024-09-09 19:59:36 +03:00
}
) ,
'Get horizontal info' : fromPromise (
async ( _ : {
input : Pick < ModelingMachineContext , 'sketchDetails' | 'selectionRanges' >
} ) = > {
return { } as SetSelections
}
) ,
2024-12-09 16:43:58 -05:00
astConstrainLength : fromPromise (
2024-09-09 19:59:36 +03:00
async ( _ : {
2024-12-09 16:43:58 -05:00
input : Pick <
ModelingMachineContext ,
'sketchDetails' | 'selectionRanges'
> & {
lengthValue? : KclCommandValue
}
2024-09-09 19:59:36 +03:00
} ) = > {
return { } as SetSelections
}
) ,
2024-12-09 16:43:58 -05:00
'Apply named value constraint' : fromPromise (
2024-09-09 19:59:36 +03:00
async ( _ : {
input : Pick <
ModelingMachineContext ,
'sketchDetails' | 'selectionRanges'
> & {
2024-12-09 16:43:58 -05:00
data? : ModelingCommandSchema [ 'Constrain with named value' ]
2024-09-09 19:59:36 +03:00
}
} ) = > {
return { } as SetSelections
}
) ,
2025-01-31 13:34:38 -05:00
extrudeAstMod : fromPromise <
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
unknown ,
2025-01-31 13:34:38 -05:00
ModelingCommandSchema [ 'Extrude' ] | undefined
> ( async ( { input } ) = > {
2025-05-06 17:57:27 -04:00
if ( ! input ) return Promise . reject ( new Error ( 'No input provided' ) )
const { nodeToEdit , sketches , length } = input
const { ast } = kclManager
const astResult = addExtrude ( {
2025-01-31 13:34:38 -05:00
ast ,
2025-05-06 17:57:27 -04:00
sketches ,
length ,
nodeToEdit ,
Add edit flows for extrude and offset plane operations (#5045)
* Start implementing a "prepareToEdit" callback for extrude
* Start of generic edit flow for operations
* Actually invoking command bar send generically on double-click
* Refactor: break out non-React hook helper to calculate Kcl expression value
* Add unit tests, fmt
* Integrate helper to get calculated KclExpression
* Clean up unused imports, simplify use of `programMemoryFromVariables`
* Implement basic extrude editing
* Refactor: move DefaultPlanesStr to its own lib file
* Add support for editing offset planes
* Add Edit right-click menu option
* Turn off edit flow for sketch for now
* Add e2e tests for sketch and offset plane editing, fix bug found with offset plane editing
* Add failing e2e extrude edit test
* Remove action version of extrude AST mod
* Fix behavior when adding a constant while editing operation, fixing e2e test
* Patch in changes from 61b02b570394f11afbd04d0d126d87305165c73c
* Remove shell's prepareToEdit
* Add other Surface types to `artifactIsPlaneWithPaths`
* refactor: rename `item` to `operation`
* Allow `prepareToEdit` to fail with a toast, signal sketch-on-offset is unimplemented
* Rework sketch e2e test to test several working and failing cases
* Fix tsc errors related to making `codeRef` optional
* Make basic error messages more friendly
* fmt
* Reset modifyAst.ts to main
* Fix broken artifactGraph unit test
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Remove unused import
* Look at this (photo)Graph *in the voice of Nickelback*
* Make the offset plane insert at the end, not one before
* Fix bug caught by e2e test failure with "Command needs review" logic
* Update src/machines/modelingMachine.ts
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
* Remove console logs per @pierremtb
* Update src/components/CommandBar/CommandBarHeader.tsx
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
* Use better programMemory init thanks @jtran
* Fix tsc post merge of #5068
* Fix logic for `artifactIsPlaneWithPaths` post-merge
* Need to disable the sketch-on-face case now that artifactGraph is in Rust. Will active in a future PR (cc @jtran)
* Re-run CI after snapshots
* Update FeatureTreePane to not use `useCommandsContext`, missed during merge
* Fix merge issue, import location change on edited file
* fix click test step, which I believe is waiting for context scripts to load
* Convert toolbarFixture.exeIndicator to getter
We need to convert all these selectors on fixtures to getters, because
they can go stale if called on the fixture constructor.
* Missed a dumb little thing in toolbarFixture.ts
* Fix goof with merge
* fmt
* Another dumb missed thing during merge
I gotta get used to the LazyGit merge tool I'm not good at it yet
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Conver sceneFixture's exeIndicator to a getter
Locators on fixtures will be frozen from the time of the fixture's
initialization, I'm increasingly convinced
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Post-kwargs E2E test cleanup
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-05 19:33:40 -05:00
} )
2025-05-06 17:57:27 -04:00
if ( err ( astResult ) ) {
return Promise . reject ( new Error ( "Couldn't add extrude statement" ) )
2025-01-31 13:34:38 -05:00
}
2025-05-06 17:57:27 -04:00
const { modifiedAst , pathToNode } = astResult
2025-03-25 05:06:27 -04:00
await updateModelingState (
modifiedAst ,
EXECUTION_TYPE_REAL ,
{
kclManager ,
editorManager ,
codeManager ,
2025-01-31 13:34:38 -05:00
} ,
2025-03-25 05:06:27 -04:00
{
2025-05-06 17:57:27 -04:00
focusPath : [ pathToNode ] ,
2025-03-25 05:06:27 -04:00
}
)
2025-01-31 13:34:38 -05:00
} ) ,
2025-05-06 17:57:27 -04:00
sweepAstMod : fromPromise <
2025-03-31 18:13:34 -04:00
unknown ,
2025-05-06 17:57:27 -04:00
ModelingCommandSchema [ 'Sweep' ] | undefined
2025-03-31 18:13:34 -04:00
> ( async ( { input } ) = > {
2025-05-06 17:57:27 -04:00
if ( ! input ) return Promise . reject ( new Error ( 'No input provided' ) )
const { nodeToEdit , sketches , path , sectional } = input
const { ast } = kclManager
const astResult = addSweep ( {
ast ,
sketches ,
path ,
sectional ,
nodeToEdit ,
} )
if ( err ( astResult ) ) {
return Promise . reject ( astResult )
2025-03-31 18:13:34 -04:00
}
2025-05-06 17:57:27 -04:00
const { modifiedAst , pathToNode } = astResult
await updateModelingState (
modifiedAst ,
EXECUTION_TYPE_REAL ,
{
kclManager ,
editorManager ,
codeManager ,
} ,
{
focusPath : [ pathToNode ] ,
2025-03-31 18:13:34 -04:00
}
)
2025-05-06 17:57:27 -04:00
} ) ,
loftAstMod : fromPromise (
async ( {
input ,
} : {
input : ModelingCommandSchema [ 'Loft' ] | undefined
} ) = > {
if ( ! input ) return Promise . reject ( new Error ( 'No input provided' ) )
const { sketches } = input
const { ast } = kclManager
const astResult = addLoft ( { ast , sketches } )
if ( err ( astResult ) ) {
return Promise . reject ( astResult )
}
2025-03-31 18:13:34 -04:00
2025-05-06 17:57:27 -04:00
const { modifiedAst , pathToNode } = astResult
await updateModelingState (
modifiedAst ,
EXECUTION_TYPE_REAL ,
{
kclManager ,
editorManager ,
codeManager ,
} ,
{
focusPath : [ pathToNode ] ,
}
)
}
) ,
revolveAstMod : fromPromise <
unknown ,
ModelingCommandSchema [ 'Revolve' ] | undefined
> ( async ( { input } ) = > {
if ( ! input ) return Promise . reject ( new Error ( 'No input provided' ) )
const { nodeToEdit , sketches , angle , axis , edge , axisOrEdge } = input
const { ast } = kclManager
const astResult = addRevolve ( {
2025-03-31 18:13:34 -04:00
ast ,
2025-05-06 17:57:27 -04:00
sketches ,
angle ,
2025-03-31 18:13:34 -04:00
axisOrEdge ,
axis ,
edge ,
2025-05-06 17:57:27 -04:00
nodeToEdit ,
} )
if ( err ( astResult ) ) {
return Promise . reject ( astResult )
}
const { modifiedAst , pathToNode } = astResult
2025-03-31 18:13:34 -04:00
await updateModelingState (
modifiedAst ,
EXECUTION_TYPE_REAL ,
{
kclManager ,
editorManager ,
codeManager ,
} ,
{
2025-05-06 17:57:27 -04:00
focusPath : [ pathToNode ] ,
2025-03-31 18:13:34 -04:00
}
)
} ) ,
2024-11-26 11:36:14 -05:00
offsetPlaneAstMod : fromPromise (
async ( {
input ,
} : {
input : ModelingCommandSchema [ 'Offset plane' ] | undefined
} ) = > {
if ( ! input ) return new Error ( 'No input provided' )
// Extract inputs
const ast = kclManager . ast
Add edit flows for extrude and offset plane operations (#5045)
* Start implementing a "prepareToEdit" callback for extrude
* Start of generic edit flow for operations
* Actually invoking command bar send generically on double-click
* Refactor: break out non-React hook helper to calculate Kcl expression value
* Add unit tests, fmt
* Integrate helper to get calculated KclExpression
* Clean up unused imports, simplify use of `programMemoryFromVariables`
* Implement basic extrude editing
* Refactor: move DefaultPlanesStr to its own lib file
* Add support for editing offset planes
* Add Edit right-click menu option
* Turn off edit flow for sketch for now
* Add e2e tests for sketch and offset plane editing, fix bug found with offset plane editing
* Add failing e2e extrude edit test
* Remove action version of extrude AST mod
* Fix behavior when adding a constant while editing operation, fixing e2e test
* Patch in changes from 61b02b570394f11afbd04d0d126d87305165c73c
* Remove shell's prepareToEdit
* Add other Surface types to `artifactIsPlaneWithPaths`
* refactor: rename `item` to `operation`
* Allow `prepareToEdit` to fail with a toast, signal sketch-on-offset is unimplemented
* Rework sketch e2e test to test several working and failing cases
* Fix tsc errors related to making `codeRef` optional
* Make basic error messages more friendly
* fmt
* Reset modifyAst.ts to main
* Fix broken artifactGraph unit test
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Remove unused import
* Look at this (photo)Graph *in the voice of Nickelback*
* Make the offset plane insert at the end, not one before
* Fix bug caught by e2e test failure with "Command needs review" logic
* Update src/machines/modelingMachine.ts
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
* Remove console logs per @pierremtb
* Update src/components/CommandBar/CommandBarHeader.tsx
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
* Use better programMemory init thanks @jtran
* Fix tsc post merge of #5068
* Fix logic for `artifactIsPlaneWithPaths` post-merge
* Need to disable the sketch-on-face case now that artifactGraph is in Rust. Will active in a future PR (cc @jtran)
* Re-run CI after snapshots
* Update FeatureTreePane to not use `useCommandsContext`, missed during merge
* Fix merge issue, import location change on edited file
* fix click test step, which I believe is waiting for context scripts to load
* Convert toolbarFixture.exeIndicator to getter
We need to convert all these selectors on fixtures to getters, because
they can go stale if called on the fixture constructor.
* Missed a dumb little thing in toolbarFixture.ts
* Fix goof with merge
* fmt
* Another dumb missed thing during merge
I gotta get used to the LazyGit merge tool I'm not good at it yet
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Conver sceneFixture's exeIndicator to a getter
Locators on fixtures will be frozen from the time of the fixture's
initialization, I'm increasingly convinced
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Post-kwargs E2E test cleanup
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-05 19:33:40 -05:00
const { plane : selection , distance , nodeToEdit } = input
2024-11-26 11:36:14 -05:00
Add edit flows for extrude and offset plane operations (#5045)
* Start implementing a "prepareToEdit" callback for extrude
* Start of generic edit flow for operations
* Actually invoking command bar send generically on double-click
* Refactor: break out non-React hook helper to calculate Kcl expression value
* Add unit tests, fmt
* Integrate helper to get calculated KclExpression
* Clean up unused imports, simplify use of `programMemoryFromVariables`
* Implement basic extrude editing
* Refactor: move DefaultPlanesStr to its own lib file
* Add support for editing offset planes
* Add Edit right-click menu option
* Turn off edit flow for sketch for now
* Add e2e tests for sketch and offset plane editing, fix bug found with offset plane editing
* Add failing e2e extrude edit test
* Remove action version of extrude AST mod
* Fix behavior when adding a constant while editing operation, fixing e2e test
* Patch in changes from 61b02b570394f11afbd04d0d126d87305165c73c
* Remove shell's prepareToEdit
* Add other Surface types to `artifactIsPlaneWithPaths`
* refactor: rename `item` to `operation`
* Allow `prepareToEdit` to fail with a toast, signal sketch-on-offset is unimplemented
* Rework sketch e2e test to test several working and failing cases
* Fix tsc errors related to making `codeRef` optional
* Make basic error messages more friendly
* fmt
* Reset modifyAst.ts to main
* Fix broken artifactGraph unit test
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Remove unused import
* Look at this (photo)Graph *in the voice of Nickelback*
* Make the offset plane insert at the end, not one before
* Fix bug caught by e2e test failure with "Command needs review" logic
* Update src/machines/modelingMachine.ts
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
* Remove console logs per @pierremtb
* Update src/components/CommandBar/CommandBarHeader.tsx
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
* Use better programMemory init thanks @jtran
* Fix tsc post merge of #5068
* Fix logic for `artifactIsPlaneWithPaths` post-merge
* Need to disable the sketch-on-face case now that artifactGraph is in Rust. Will active in a future PR (cc @jtran)
* Re-run CI after snapshots
* Update FeatureTreePane to not use `useCommandsContext`, missed during merge
* Fix merge issue, import location change on edited file
* fix click test step, which I believe is waiting for context scripts to load
* Convert toolbarFixture.exeIndicator to getter
We need to convert all these selectors on fixtures to getters, because
they can go stale if called on the fixture constructor.
* Missed a dumb little thing in toolbarFixture.ts
* Fix goof with merge
* fmt
* Another dumb missed thing during merge
I gotta get used to the LazyGit merge tool I'm not good at it yet
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Conver sceneFixture's exeIndicator to a getter
Locators on fixtures will be frozen from the time of the fixture's
initialization, I'm increasingly convinced
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Post-kwargs E2E test cleanup
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-05 19:33:40 -05:00
let insertIndex : number | undefined = undefined
let planeName : string | undefined = undefined
2024-11-26 11:36:14 -05:00
Add edit flows for extrude and offset plane operations (#5045)
* Start implementing a "prepareToEdit" callback for extrude
* Start of generic edit flow for operations
* Actually invoking command bar send generically on double-click
* Refactor: break out non-React hook helper to calculate Kcl expression value
* Add unit tests, fmt
* Integrate helper to get calculated KclExpression
* Clean up unused imports, simplify use of `programMemoryFromVariables`
* Implement basic extrude editing
* Refactor: move DefaultPlanesStr to its own lib file
* Add support for editing offset planes
* Add Edit right-click menu option
* Turn off edit flow for sketch for now
* Add e2e tests for sketch and offset plane editing, fix bug found with offset plane editing
* Add failing e2e extrude edit test
* Remove action version of extrude AST mod
* Fix behavior when adding a constant while editing operation, fixing e2e test
* Patch in changes from 61b02b570394f11afbd04d0d126d87305165c73c
* Remove shell's prepareToEdit
* Add other Surface types to `artifactIsPlaneWithPaths`
* refactor: rename `item` to `operation`
* Allow `prepareToEdit` to fail with a toast, signal sketch-on-offset is unimplemented
* Rework sketch e2e test to test several working and failing cases
* Fix tsc errors related to making `codeRef` optional
* Make basic error messages more friendly
* fmt
* Reset modifyAst.ts to main
* Fix broken artifactGraph unit test
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Remove unused import
* Look at this (photo)Graph *in the voice of Nickelback*
* Make the offset plane insert at the end, not one before
* Fix bug caught by e2e test failure with "Command needs review" logic
* Update src/machines/modelingMachine.ts
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
* Remove console logs per @pierremtb
* Update src/components/CommandBar/CommandBarHeader.tsx
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
* Use better programMemory init thanks @jtran
* Fix tsc post merge of #5068
* Fix logic for `artifactIsPlaneWithPaths` post-merge
* Need to disable the sketch-on-face case now that artifactGraph is in Rust. Will active in a future PR (cc @jtran)
* Re-run CI after snapshots
* Update FeatureTreePane to not use `useCommandsContext`, missed during merge
* Fix merge issue, import location change on edited file
* fix click test step, which I believe is waiting for context scripts to load
* Convert toolbarFixture.exeIndicator to getter
We need to convert all these selectors on fixtures to getters, because
they can go stale if called on the fixture constructor.
* Missed a dumb little thing in toolbarFixture.ts
* Fix goof with merge
* fmt
* Another dumb missed thing during merge
I gotta get used to the LazyGit merge tool I'm not good at it yet
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Conver sceneFixture's exeIndicator to a getter
Locators on fixtures will be frozen from the time of the fixture's
initialization, I'm increasingly convinced
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Post-kwargs E2E test cleanup
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-05 19:33:40 -05:00
// If this is an edit flow, first we're going to remove the old plane
if ( nodeToEdit && typeof nodeToEdit [ 1 ] [ 0 ] === 'number' ) {
// Extract the plane name from the node to edit
const planeNameNode = getNodeFromPath < VariableDeclaration > (
ast ,
nodeToEdit ,
'VariableDeclaration'
2024-11-26 11:36:14 -05:00
)
Add edit flows for extrude and offset plane operations (#5045)
* Start implementing a "prepareToEdit" callback for extrude
* Start of generic edit flow for operations
* Actually invoking command bar send generically on double-click
* Refactor: break out non-React hook helper to calculate Kcl expression value
* Add unit tests, fmt
* Integrate helper to get calculated KclExpression
* Clean up unused imports, simplify use of `programMemoryFromVariables`
* Implement basic extrude editing
* Refactor: move DefaultPlanesStr to its own lib file
* Add support for editing offset planes
* Add Edit right-click menu option
* Turn off edit flow for sketch for now
* Add e2e tests for sketch and offset plane editing, fix bug found with offset plane editing
* Add failing e2e extrude edit test
* Remove action version of extrude AST mod
* Fix behavior when adding a constant while editing operation, fixing e2e test
* Patch in changes from 61b02b570394f11afbd04d0d126d87305165c73c
* Remove shell's prepareToEdit
* Add other Surface types to `artifactIsPlaneWithPaths`
* refactor: rename `item` to `operation`
* Allow `prepareToEdit` to fail with a toast, signal sketch-on-offset is unimplemented
* Rework sketch e2e test to test several working and failing cases
* Fix tsc errors related to making `codeRef` optional
* Make basic error messages more friendly
* fmt
* Reset modifyAst.ts to main
* Fix broken artifactGraph unit test
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Remove unused import
* Look at this (photo)Graph *in the voice of Nickelback*
* Make the offset plane insert at the end, not one before
* Fix bug caught by e2e test failure with "Command needs review" logic
* Update src/machines/modelingMachine.ts
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
* Remove console logs per @pierremtb
* Update src/components/CommandBar/CommandBarHeader.tsx
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
* Use better programMemory init thanks @jtran
* Fix tsc post merge of #5068
* Fix logic for `artifactIsPlaneWithPaths` post-merge
* Need to disable the sketch-on-face case now that artifactGraph is in Rust. Will active in a future PR (cc @jtran)
* Re-run CI after snapshots
* Update FeatureTreePane to not use `useCommandsContext`, missed during merge
* Fix merge issue, import location change on edited file
* fix click test step, which I believe is waiting for context scripts to load
* Convert toolbarFixture.exeIndicator to getter
We need to convert all these selectors on fixtures to getters, because
they can go stale if called on the fixture constructor.
* Missed a dumb little thing in toolbarFixture.ts
* Fix goof with merge
* fmt
* Another dumb missed thing during merge
I gotta get used to the LazyGit merge tool I'm not good at it yet
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Conver sceneFixture's exeIndicator to a getter
Locators on fixtures will be frozen from the time of the fixture's
initialization, I'm increasingly convinced
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Post-kwargs E2E test cleanup
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-05 19:33:40 -05:00
if ( err ( planeNameNode ) ) {
console . error ( 'Error extracting plane name' )
} else {
planeName = planeNameNode . node . declaration . id . name
}
const newBody = [ . . . ast . body ]
newBody . splice ( nodeToEdit [ 1 ] [ 0 ] , 1 )
2024-11-26 11:36:14 -05:00
ast . body = newBody
Add edit flows for extrude and offset plane operations (#5045)
* Start implementing a "prepareToEdit" callback for extrude
* Start of generic edit flow for operations
* Actually invoking command bar send generically on double-click
* Refactor: break out non-React hook helper to calculate Kcl expression value
* Add unit tests, fmt
* Integrate helper to get calculated KclExpression
* Clean up unused imports, simplify use of `programMemoryFromVariables`
* Implement basic extrude editing
* Refactor: move DefaultPlanesStr to its own lib file
* Add support for editing offset planes
* Add Edit right-click menu option
* Turn off edit flow for sketch for now
* Add e2e tests for sketch and offset plane editing, fix bug found with offset plane editing
* Add failing e2e extrude edit test
* Remove action version of extrude AST mod
* Fix behavior when adding a constant while editing operation, fixing e2e test
* Patch in changes from 61b02b570394f11afbd04d0d126d87305165c73c
* Remove shell's prepareToEdit
* Add other Surface types to `artifactIsPlaneWithPaths`
* refactor: rename `item` to `operation`
* Allow `prepareToEdit` to fail with a toast, signal sketch-on-offset is unimplemented
* Rework sketch e2e test to test several working and failing cases
* Fix tsc errors related to making `codeRef` optional
* Make basic error messages more friendly
* fmt
* Reset modifyAst.ts to main
* Fix broken artifactGraph unit test
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Remove unused import
* Look at this (photo)Graph *in the voice of Nickelback*
* Make the offset plane insert at the end, not one before
* Fix bug caught by e2e test failure with "Command needs review" logic
* Update src/machines/modelingMachine.ts
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
* Remove console logs per @pierremtb
* Update src/components/CommandBar/CommandBarHeader.tsx
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
* Use better programMemory init thanks @jtran
* Fix tsc post merge of #5068
* Fix logic for `artifactIsPlaneWithPaths` post-merge
* Need to disable the sketch-on-face case now that artifactGraph is in Rust. Will active in a future PR (cc @jtran)
* Re-run CI after snapshots
* Update FeatureTreePane to not use `useCommandsContext`, missed during merge
* Fix merge issue, import location change on edited file
* fix click test step, which I believe is waiting for context scripts to load
* Convert toolbarFixture.exeIndicator to getter
We need to convert all these selectors on fixtures to getters, because
they can go stale if called on the fixture constructor.
* Missed a dumb little thing in toolbarFixture.ts
* Fix goof with merge
* fmt
* Another dumb missed thing during merge
I gotta get used to the LazyGit merge tool I'm not good at it yet
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Conver sceneFixture's exeIndicator to a getter
Locators on fixtures will be frozen from the time of the fixture's
initialization, I'm increasingly convinced
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Post-kwargs E2E test cleanup
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-05 19:33:40 -05:00
insertIndex = nodeToEdit [ 1 ] [ 0 ]
2024-11-26 11:36:14 -05:00
}
Add edit flows for extrude and offset plane operations (#5045)
* Start implementing a "prepareToEdit" callback for extrude
* Start of generic edit flow for operations
* Actually invoking command bar send generically on double-click
* Refactor: break out non-React hook helper to calculate Kcl expression value
* Add unit tests, fmt
* Integrate helper to get calculated KclExpression
* Clean up unused imports, simplify use of `programMemoryFromVariables`
* Implement basic extrude editing
* Refactor: move DefaultPlanesStr to its own lib file
* Add support for editing offset planes
* Add Edit right-click menu option
* Turn off edit flow for sketch for now
* Add e2e tests for sketch and offset plane editing, fix bug found with offset plane editing
* Add failing e2e extrude edit test
* Remove action version of extrude AST mod
* Fix behavior when adding a constant while editing operation, fixing e2e test
* Patch in changes from 61b02b570394f11afbd04d0d126d87305165c73c
* Remove shell's prepareToEdit
* Add other Surface types to `artifactIsPlaneWithPaths`
* refactor: rename `item` to `operation`
* Allow `prepareToEdit` to fail with a toast, signal sketch-on-offset is unimplemented
* Rework sketch e2e test to test several working and failing cases
* Fix tsc errors related to making `codeRef` optional
* Make basic error messages more friendly
* fmt
* Reset modifyAst.ts to main
* Fix broken artifactGraph unit test
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Remove unused import
* Look at this (photo)Graph *in the voice of Nickelback*
* Make the offset plane insert at the end, not one before
* Fix bug caught by e2e test failure with "Command needs review" logic
* Update src/machines/modelingMachine.ts
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
* Remove console logs per @pierremtb
* Update src/components/CommandBar/CommandBarHeader.tsx
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
* Use better programMemory init thanks @jtran
* Fix tsc post merge of #5068
* Fix logic for `artifactIsPlaneWithPaths` post-merge
* Need to disable the sketch-on-face case now that artifactGraph is in Rust. Will active in a future PR (cc @jtran)
* Re-run CI after snapshots
* Update FeatureTreePane to not use `useCommandsContext`, missed during merge
* Fix merge issue, import location change on edited file
* fix click test step, which I believe is waiting for context scripts to load
* Convert toolbarFixture.exeIndicator to getter
We need to convert all these selectors on fixtures to getters, because
they can go stale if called on the fixture constructor.
* Missed a dumb little thing in toolbarFixture.ts
* Fix goof with merge
* fmt
* Another dumb missed thing during merge
I gotta get used to the LazyGit merge tool I'm not good at it yet
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Conver sceneFixture's exeIndicator to a getter
Locators on fixtures will be frozen from the time of the fixture's
initialization, I'm increasingly convinced
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Post-kwargs E2E test cleanup
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-05 19:33:40 -05:00
// Extract the default plane from selection
const plane = selection . otherSelections [ 0 ]
if ( ! ( plane && plane instanceof Object && 'name' in plane ) )
return trap ( 'No plane selected' )
2024-11-26 11:36:14 -05:00
Add edit flows for extrude and offset plane operations (#5045)
* Start implementing a "prepareToEdit" callback for extrude
* Start of generic edit flow for operations
* Actually invoking command bar send generically on double-click
* Refactor: break out non-React hook helper to calculate Kcl expression value
* Add unit tests, fmt
* Integrate helper to get calculated KclExpression
* Clean up unused imports, simplify use of `programMemoryFromVariables`
* Implement basic extrude editing
* Refactor: move DefaultPlanesStr to its own lib file
* Add support for editing offset planes
* Add Edit right-click menu option
* Turn off edit flow for sketch for now
* Add e2e tests for sketch and offset plane editing, fix bug found with offset plane editing
* Add failing e2e extrude edit test
* Remove action version of extrude AST mod
* Fix behavior when adding a constant while editing operation, fixing e2e test
* Patch in changes from 61b02b570394f11afbd04d0d126d87305165c73c
* Remove shell's prepareToEdit
* Add other Surface types to `artifactIsPlaneWithPaths`
* refactor: rename `item` to `operation`
* Allow `prepareToEdit` to fail with a toast, signal sketch-on-offset is unimplemented
* Rework sketch e2e test to test several working and failing cases
* Fix tsc errors related to making `codeRef` optional
* Make basic error messages more friendly
* fmt
* Reset modifyAst.ts to main
* Fix broken artifactGraph unit test
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Remove unused import
* Look at this (photo)Graph *in the voice of Nickelback*
* Make the offset plane insert at the end, not one before
* Fix bug caught by e2e test failure with "Command needs review" logic
* Update src/machines/modelingMachine.ts
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
* Remove console logs per @pierremtb
* Update src/components/CommandBar/CommandBarHeader.tsx
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
* Use better programMemory init thanks @jtran
* Fix tsc post merge of #5068
* Fix logic for `artifactIsPlaneWithPaths` post-merge
* Need to disable the sketch-on-face case now that artifactGraph is in Rust. Will active in a future PR (cc @jtran)
* Re-run CI after snapshots
* Update FeatureTreePane to not use `useCommandsContext`, missed during merge
* Fix merge issue, import location change on edited file
* fix click test step, which I believe is waiting for context scripts to load
* Convert toolbarFixture.exeIndicator to getter
We need to convert all these selectors on fixtures to getters, because
they can go stale if called on the fixture constructor.
* Missed a dumb little thing in toolbarFixture.ts
* Fix goof with merge
* fmt
* Another dumb missed thing during merge
I gotta get used to the LazyGit merge tool I'm not good at it yet
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Conver sceneFixture's exeIndicator to a getter
Locators on fixtures will be frozen from the time of the fixture's
initialization, I'm increasingly convinced
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Post-kwargs E2E test cleanup
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-05 19:33:40 -05:00
// Get the default plane name from the selection
2024-11-26 11:36:14 -05:00
const offsetPlaneResult = addOffsetPlane ( {
node : ast ,
defaultPlane : plane.name ,
offset :
'variableName' in distance
? distance . variableIdentifierAst
: distance . valueAst ,
Add edit flows for extrude and offset plane operations (#5045)
* Start implementing a "prepareToEdit" callback for extrude
* Start of generic edit flow for operations
* Actually invoking command bar send generically on double-click
* Refactor: break out non-React hook helper to calculate Kcl expression value
* Add unit tests, fmt
* Integrate helper to get calculated KclExpression
* Clean up unused imports, simplify use of `programMemoryFromVariables`
* Implement basic extrude editing
* Refactor: move DefaultPlanesStr to its own lib file
* Add support for editing offset planes
* Add Edit right-click menu option
* Turn off edit flow for sketch for now
* Add e2e tests for sketch and offset plane editing, fix bug found with offset plane editing
* Add failing e2e extrude edit test
* Remove action version of extrude AST mod
* Fix behavior when adding a constant while editing operation, fixing e2e test
* Patch in changes from 61b02b570394f11afbd04d0d126d87305165c73c
* Remove shell's prepareToEdit
* Add other Surface types to `artifactIsPlaneWithPaths`
* refactor: rename `item` to `operation`
* Allow `prepareToEdit` to fail with a toast, signal sketch-on-offset is unimplemented
* Rework sketch e2e test to test several working and failing cases
* Fix tsc errors related to making `codeRef` optional
* Make basic error messages more friendly
* fmt
* Reset modifyAst.ts to main
* Fix broken artifactGraph unit test
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Remove unused import
* Look at this (photo)Graph *in the voice of Nickelback*
* Make the offset plane insert at the end, not one before
* Fix bug caught by e2e test failure with "Command needs review" logic
* Update src/machines/modelingMachine.ts
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
* Remove console logs per @pierremtb
* Update src/components/CommandBar/CommandBarHeader.tsx
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
* Use better programMemory init thanks @jtran
* Fix tsc post merge of #5068
* Fix logic for `artifactIsPlaneWithPaths` post-merge
* Need to disable the sketch-on-face case now that artifactGraph is in Rust. Will active in a future PR (cc @jtran)
* Re-run CI after snapshots
* Update FeatureTreePane to not use `useCommandsContext`, missed during merge
* Fix merge issue, import location change on edited file
* fix click test step, which I believe is waiting for context scripts to load
* Convert toolbarFixture.exeIndicator to getter
We need to convert all these selectors on fixtures to getters, because
they can go stale if called on the fixture constructor.
* Missed a dumb little thing in toolbarFixture.ts
* Fix goof with merge
* fmt
* Another dumb missed thing during merge
I gotta get used to the LazyGit merge tool I'm not good at it yet
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Conver sceneFixture's exeIndicator to a getter
Locators on fixtures will be frozen from the time of the fixture's
initialization, I'm increasingly convinced
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Post-kwargs E2E test cleanup
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-05 19:33:40 -05:00
insertIndex ,
planeName ,
2024-11-26 11:36:14 -05:00
} )
Add edit flows for extrude and offset plane operations (#5045)
* Start implementing a "prepareToEdit" callback for extrude
* Start of generic edit flow for operations
* Actually invoking command bar send generically on double-click
* Refactor: break out non-React hook helper to calculate Kcl expression value
* Add unit tests, fmt
* Integrate helper to get calculated KclExpression
* Clean up unused imports, simplify use of `programMemoryFromVariables`
* Implement basic extrude editing
* Refactor: move DefaultPlanesStr to its own lib file
* Add support for editing offset planes
* Add Edit right-click menu option
* Turn off edit flow for sketch for now
* Add e2e tests for sketch and offset plane editing, fix bug found with offset plane editing
* Add failing e2e extrude edit test
* Remove action version of extrude AST mod
* Fix behavior when adding a constant while editing operation, fixing e2e test
* Patch in changes from 61b02b570394f11afbd04d0d126d87305165c73c
* Remove shell's prepareToEdit
* Add other Surface types to `artifactIsPlaneWithPaths`
* refactor: rename `item` to `operation`
* Allow `prepareToEdit` to fail with a toast, signal sketch-on-offset is unimplemented
* Rework sketch e2e test to test several working and failing cases
* Fix tsc errors related to making `codeRef` optional
* Make basic error messages more friendly
* fmt
* Reset modifyAst.ts to main
* Fix broken artifactGraph unit test
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Remove unused import
* Look at this (photo)Graph *in the voice of Nickelback*
* Make the offset plane insert at the end, not one before
* Fix bug caught by e2e test failure with "Command needs review" logic
* Update src/machines/modelingMachine.ts
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
* Remove console logs per @pierremtb
* Update src/components/CommandBar/CommandBarHeader.tsx
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
* Use better programMemory init thanks @jtran
* Fix tsc post merge of #5068
* Fix logic for `artifactIsPlaneWithPaths` post-merge
* Need to disable the sketch-on-face case now that artifactGraph is in Rust. Will active in a future PR (cc @jtran)
* Re-run CI after snapshots
* Update FeatureTreePane to not use `useCommandsContext`, missed during merge
* Fix merge issue, import location change on edited file
* fix click test step, which I believe is waiting for context scripts to load
* Convert toolbarFixture.exeIndicator to getter
We need to convert all these selectors on fixtures to getters, because
they can go stale if called on the fixture constructor.
* Missed a dumb little thing in toolbarFixture.ts
* Fix goof with merge
* fmt
* Another dumb missed thing during merge
I gotta get used to the LazyGit merge tool I'm not good at it yet
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Conver sceneFixture's exeIndicator to a getter
Locators on fixtures will be frozen from the time of the fixture's
initialization, I'm increasingly convinced
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Post-kwargs E2E test cleanup
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-05 19:33:40 -05:00
// Insert the distance variable if the user has provided a variable name
if (
'variableName' in distance &&
distance . variableName &&
typeof offsetPlaneResult . pathToNode [ 1 ] [ 0 ] === 'number'
) {
const insertIndex = Math . min (
offsetPlaneResult . pathToNode [ 1 ] [ 0 ] ,
distance . insertIndex
)
const newBody = [ . . . offsetPlaneResult . modifiedAst . body ]
newBody . splice ( insertIndex , 0 , distance . variableDeclarationAst )
offsetPlaneResult . modifiedAst . body = newBody
// Since we inserted a new variable, we need to update the path to the extrude argument
offsetPlaneResult . pathToNode [ 1 ] [ 0 ] ++
}
2025-03-25 05:06:27 -04:00
await updateModelingState (
2024-11-26 11:36:14 -05:00
offsetPlaneResult . modifiedAst ,
2025-03-25 05:06:27 -04:00
EXECUTION_TYPE_REAL ,
{
kclManager ,
editorManager ,
codeManager ,
} ,
2024-11-26 11:36:14 -05:00
{
focusPath : [ offsetPlaneResult . pathToNode ] ,
}
)
2024-12-04 17:24:16 -05:00
}
) ,
2025-02-07 10:16:36 -05:00
helixAstMod : fromPromise (
async ( {
input ,
} : {
input : ModelingCommandSchema [ 'Helix' ] | undefined
} ) = > {
if ( ! input ) return new Error ( 'No input provided' )
// Extract inputs
2025-03-26 17:57:30 -04:00
console . log ( 'input' , input )
2025-02-07 10:16:36 -05:00
const ast = kclManager . ast
const {
2025-03-26 17:57:30 -04:00
mode ,
2025-03-24 16:08:19 -04:00
axis ,
edge ,
2025-03-26 17:57:30 -04:00
cylinder ,
2025-02-07 10:16:36 -05:00
revolutions ,
angleStart ,
2025-03-24 21:55:24 +13:00
ccw ,
2025-02-07 10:16:36 -05:00
radius ,
length ,
2025-02-24 18:44:13 -05:00
nodeToEdit ,
2025-02-07 10:16:36 -05:00
} = input
2025-02-24 18:44:13 -05:00
let opInsertIndex : number | undefined = undefined
let opVariableName : string | undefined = undefined
// If this is an edit flow, first we're going to remove the old one
if ( nodeToEdit && typeof nodeToEdit [ 1 ] [ 0 ] === 'number' ) {
// Extract the old name from the node to edit
const oldNode = getNodeFromPath < VariableDeclaration > (
ast ,
nodeToEdit ,
'VariableDeclaration'
)
if ( err ( oldNode ) ) {
console . error ( 'Error extracting plane name' )
} else {
opVariableName = oldNode . node . declaration . id . name
}
const newBody = [ . . . ast . body ]
newBody . splice ( nodeToEdit [ 1 ] [ 0 ] , 1 )
ast . body = newBody
opInsertIndex = nodeToEdit [ 1 ] [ 0 ]
}
2025-03-26 17:57:30 -04:00
let cylinderDeclarator : VariableDeclarator | undefined
let axisExpression :
2025-05-02 16:08:12 -05:00
| Node < CallExpressionKw | Name >
2025-03-26 17:57:30 -04:00
| Node < Literal >
| undefined
if ( mode === 'Cylinder' ) {
if (
! (
cylinder &&
cylinder . graphSelections [ 0 ] &&
cylinder . graphSelections [ 0 ] . artifact ? . type === 'wall'
)
) {
return new Error ( 'Cylinder argument not valid' )
}
const clonedAstForGetExtrude = structuredClone ( ast )
const extrudeLookupResult = getPathToExtrudeForSegmentSelection (
clonedAstForGetExtrude ,
cylinder . graphSelections [ 0 ] ,
2025-03-29 17:25:26 -07:00
kclManager . artifactGraph
2025-03-26 17:57:30 -04:00
)
if ( err ( extrudeLookupResult ) ) {
return extrudeLookupResult
}
const extrudeNode = getNodeFromPath < VariableDeclaration > (
ast ,
extrudeLookupResult . pathToExtrudeNode ,
'VariableDeclaration'
)
if ( err ( extrudeNode ) ) {
return extrudeNode
}
cylinderDeclarator = extrudeNode . node . declaration
} else if ( mode === 'Axis' || mode === 'Edge' ) {
const getAxisResult = getAxisExpressionAndIndex ( mode , axis , edge , ast )
if ( err ( getAxisResult ) ) {
return getAxisResult
}
axisExpression = getAxisResult . generatedAxis
} else {
return new Error (
'Generated axis or cylinder declarator selection is missing.'
)
2025-03-24 16:08:19 -04:00
}
// TODO: figure out if we want to smart insert after the sketch as below
// *or* after the sweep that consumes the sketch, in which case the below code doesn't work
// If an axis was selected in KCL, find the max index to insert the revolve command
// if (axisIndexIfAxis) {
// opInsertIndex = axisIndexIfAxis + 1
// }
2025-03-26 17:57:30 -04:00
for ( const v of [ revolutions , angleStart , radius , length ] ) {
if ( v === undefined ) {
continue
}
const variable = v as KclCommandValue
2025-02-07 10:16:36 -05:00
// Insert the variable if it exists
2025-03-26 17:57:30 -04:00
if ( 'variableName' in variable && variable . variableName ) {
2025-02-07 10:16:36 -05:00
const newBody = [ . . . ast . body ]
newBody . splice (
variable . insertIndex ,
0 ,
variable . variableDeclarationAst
)
ast . body = newBody
}
}
2025-03-24 16:08:19 -04:00
const { modifiedAst , pathToNode } = addHelix ( {
2025-02-07 10:16:36 -05:00
node : ast ,
revolutions : valueOrVariable ( revolutions ) ,
angleStart : valueOrVariable ( angleStart ) ,
2025-03-24 21:55:24 +13:00
ccw ,
2025-03-26 17:57:30 -04:00
radius : radius ? valueOrVariable ( radius ) : undefined ,
axis : axisExpression ,
cylinder : cylinderDeclarator ,
length : length ? valueOrVariable ( length ) : undefined ,
2025-02-24 18:44:13 -05:00
insertIndex : opInsertIndex ,
variableName : opVariableName ,
2025-02-07 10:16:36 -05:00
} )
2025-03-24 16:08:19 -04:00
await updateModelingState (
modifiedAst ,
2025-03-25 05:06:27 -04:00
EXECUTION_TYPE_REAL ,
2025-02-07 10:16:36 -05:00
{
2025-03-24 16:08:19 -04:00
kclManager ,
editorManager ,
codeManager ,
} ,
{
focusPath : [ pathToNode ] ,
2025-02-07 10:16:36 -05:00
}
)
}
) ,
2024-12-09 15:20:48 -05:00
shellAstMod : fromPromise (
async ( {
input ,
} : {
input : ModelingCommandSchema [ 'Shell' ] | undefined
} ) = > {
if ( ! input ) {
return new Error ( 'No input provided' )
}
// Extract inputs
const ast = kclManager . ast
2025-03-14 16:05:41 -04:00
const { selection , thickness , nodeToEdit } = input
let variableName : string | undefined = undefined
let insertIndex : number | undefined = undefined
2024-12-09 15:20:48 -05:00
2025-03-14 16:05:41 -04:00
// If this is an edit flow, first we're going to remove the old extrusion
if ( nodeToEdit && typeof nodeToEdit [ 1 ] [ 0 ] === 'number' ) {
// Extract the plane name from the node to edit
const variableNode = getNodeFromPath < VariableDeclaration > (
ast ,
nodeToEdit ,
'VariableDeclaration'
2024-12-09 15:20:48 -05:00
)
2025-03-14 16:05:41 -04:00
if ( err ( variableNode ) ) {
console . error ( 'Error extracting name' )
} else {
variableName = variableNode . node . declaration . id . name
}
// Removing the old statement
const newBody = [ . . . ast . body ]
newBody . splice ( nodeToEdit [ 1 ] [ 0 ] , 1 )
2024-12-09 15:20:48 -05:00
ast . body = newBody
2025-03-14 16:05:41 -04:00
insertIndex = nodeToEdit [ 1 ] [ 0 ]
}
// Turn the selection into the faces list
const clonedAstForGetExtrude = structuredClone ( ast )
const faces : Expr [ ] = [ ]
let pathToExtrudeNode : PathToNode | undefined = undefined
for ( const graphSelection of selection . graphSelections ) {
const extrudeLookupResult = getPathToExtrudeForSegmentSelection (
clonedAstForGetExtrude ,
graphSelection ,
2025-03-29 17:25:26 -07:00
kclManager . artifactGraph
2025-03-14 16:05:41 -04:00
)
if ( err ( extrudeLookupResult ) ) {
return new Error (
"Couldn't find extrude paths from getPathToExtrudeForSegmentSelection" ,
{ cause : extrudeLookupResult }
)
}
const extrudeNode = getNodeFromPath < VariableDeclaration > (
ast ,
extrudeLookupResult . pathToExtrudeNode ,
'VariableDeclaration'
)
if ( err ( extrudeNode ) ) {
return new Error ( "Couldn't find extrude node from selection" , {
cause : extrudeNode ,
} )
}
const segmentNode = getNodeFromPath < VariableDeclaration > (
ast ,
extrudeLookupResult . pathToSegmentNode ,
'VariableDeclaration'
)
if ( err ( segmentNode ) ) {
return new Error ( "Couldn't find segment node from selection" , {
cause : segmentNode ,
} )
}
2025-05-02 16:08:12 -05:00
if ( extrudeNode . node . declaration . init . type === 'CallExpressionKw' ) {
2025-03-14 16:05:41 -04:00
pathToExtrudeNode = extrudeLookupResult . pathToExtrudeNode
} else if (
segmentNode . node . declaration . init . type === 'PipeExpression'
) {
pathToExtrudeNode = extrudeLookupResult . pathToSegmentNode
} else {
return new Error (
"Couldn't find extrude node that was either a call expression or a pipe" ,
{ cause : segmentNode }
)
}
const selectedArtifact = graphSelection . artifact
if ( ! selectedArtifact ) {
return new Error ( 'Bad artifact from selection' )
}
2025-05-07 12:48:23 -05:00
// Check on the selection, and handle the wall vs cap cases
2025-03-14 16:05:41 -04:00
let expr : Expr
if ( selectedArtifact . type === 'cap' ) {
expr = createLiteral ( selectedArtifact . subType )
} else if ( selectedArtifact . type === 'wall' ) {
const tagResult = mutateAstWithTagForSketchSegment (
ast ,
extrudeLookupResult . pathToSegmentNode
)
if ( err ( tagResult ) ) {
return tagResult
}
const { tag } = tagResult
2025-03-24 20:58:55 +13:00
expr = createLocalName ( tag )
2025-03-14 16:05:41 -04:00
} else {
return new Error ( 'Artifact is neither a cap nor a wall' )
}
faces . push ( expr )
}
if ( ! pathToExtrudeNode ) {
return new Error ( 'No path to extrude node found' )
}
const extrudeNode = getNodeFromPath < VariableDeclarator > (
ast ,
pathToExtrudeNode ,
'VariableDeclarator'
)
if ( err ( extrudeNode ) ) {
2025-05-01 12:42:44 -05:00
return new Error ( "Couldn't find extrude node" , {
cause : extrudeNode ,
} )
2024-12-09 15:20:48 -05:00
}
// Perform the shell op
2025-03-14 16:05:41 -04:00
const sweepName = extrudeNode . node . id . name
const addResult = addShell ( {
2024-12-09 15:20:48 -05:00
node : ast ,
2025-03-14 16:05:41 -04:00
sweepName ,
faces : faces ,
2024-12-09 15:20:48 -05:00
thickness :
'variableName' in thickness
? thickness . variableIdentifierAst
: thickness . valueAst ,
2025-03-14 16:05:41 -04:00
insertIndex ,
variableName ,
2024-12-09 15:20:48 -05:00
} )
2025-03-14 16:05:41 -04:00
// Insert the thickness variable if the user has provided a variable name
if (
'variableName' in thickness &&
thickness . variableName &&
typeof addResult . pathToNode [ 1 ] [ 0 ] === 'number'
) {
const insertIndex = Math . min (
addResult . pathToNode [ 1 ] [ 0 ] ,
thickness . insertIndex
)
const newBody = [ . . . addResult . modifiedAst . body ]
newBody . splice ( insertIndex , 0 , thickness . variableDeclarationAst )
addResult . modifiedAst . body = newBody
// Since we inserted a new variable, we need to update the path to the extrude argument
addResult . pathToNode [ 1 ] [ 0 ] ++
2024-12-09 15:20:48 -05:00
}
2025-03-25 05:06:27 -04:00
await updateModelingState (
2025-03-14 16:05:41 -04:00
addResult . modifiedAst ,
2025-03-25 05:06:27 -04:00
EXECUTION_TYPE_REAL ,
{
kclManager ,
editorManager ,
codeManager ,
} ,
2024-12-09 15:20:48 -05:00
{
2025-03-14 16:05:41 -04:00
focusPath : [ addResult . pathToNode ] ,
2024-12-09 15:20:48 -05:00
}
)
2024-11-26 11:36:14 -05:00
}
) ,
2025-01-08 16:05:24 +01:00
filletAstMod : fromPromise (
async ( {
input ,
} : {
input : ModelingCommandSchema [ 'Fillet' ] | undefined
} ) = > {
if ( ! input ) {
return new Error ( 'No input provided' )
}
// Extract inputs
const ast = kclManager . ast
2025-05-08 22:16:36 +02:00
let modifiedAst = structuredClone ( ast )
let focusPath : PathToNode [ ] = [ ]
2025-03-26 07:57:08 -04:00
const { nodeToEdit , selection , radius } = input
2025-01-08 16:05:24 +01:00
const parameters : FilletParameters = {
type : EdgeTreatmentType . Fillet ,
radius ,
}
2025-05-08 22:16:36 +02:00
2025-02-04 01:14:53 +01:00
const dependencies = {
kclManager ,
engineCommandManager ,
editorManager ,
codeManager ,
}
2025-01-08 16:05:24 +01:00
2025-05-08 22:16:36 +02:00
// Apply or edit fillet
if ( nodeToEdit ) {
// Edit existing fillet
// selection is not the edge treatment itself,
// but just the first edge in the fillet expression >
// we need to find the edgeCut artifact
// and build a new selection from it
// TODO: this is a bit of a hack, we should be able
// to get the edgeCut artifact from the selection
const firstSelection = selection . graphSelections [ 0 ]
const edgeCutArtifact = Array . from (
kclManager . artifactGraph . values ( )
) . find (
( artifact ) = >
artifact . type === 'edgeCut' &&
artifact . consumedEdgeId === firstSelection . artifact ? . id
)
if ( ! edgeCutArtifact || edgeCutArtifact . type !== 'edgeCut' ) {
return Promise . reject (
new Error (
'Failed to retrieve edgeCut artifact from sweepEdge selection'
)
)
}
const edgeTreatmentSelection = {
artifact : edgeCutArtifact ,
codeRef : edgeCutArtifact.codeRef ,
}
const editResult = await editEdgeTreatment (
ast ,
edgeTreatmentSelection ,
parameters
)
if ( err ( editResult ) ) return Promise . reject ( editResult )
modifiedAst = editResult . modifiedAst
focusPath = [ editResult . pathToEdgeTreatmentNode ]
} else {
// Apply fillet to selection
const filletResult = await modifyAstWithEdgeTreatmentAndTag (
ast ,
selection ,
parameters ,
dependencies
)
if ( err ( filletResult ) ) return Promise . reject ( filletResult )
modifiedAst = filletResult . modifiedAst
focusPath = filletResult . pathToEdgeTreatmentNode
}
await updateModelingState (
modifiedAst ,
EXECUTION_TYPE_REAL ,
{
kclManager ,
editorManager ,
codeManager ,
} ,
{
focusPath : focusPath ,
}
)
}
) ,
chamferAstMod : fromPromise (
async ( {
input ,
} : {
input : ModelingCommandSchema [ 'Chamfer' ] | undefined
} ) = > {
if ( ! input ) {
return Promise . reject ( new Error ( 'No input provided' ) )
}
// Extract inputs
const ast = kclManager . ast
let modifiedAst = structuredClone ( ast )
let focusPath : PathToNode [ ] = [ ]
const { nodeToEdit , selection , length } = input
const parameters : ChamferParameters = {
type : EdgeTreatmentType . Chamfer ,
length ,
}
const dependencies = {
kclManager ,
engineCommandManager ,
editorManager ,
codeManager ,
}
// Apply or edit chamfer
if ( nodeToEdit ) {
// Edit existing chamfer
// selection is not the edge treatment itself,
// but just the first edge in the chamfer expression >
// we need to find the edgeCut artifact
// and build a new selection from it
// TODO: this is a bit of a hack, we should be able
// to get the edgeCut artifact from the selection
const firstSelection = selection . graphSelections [ 0 ]
const edgeCutArtifact = Array . from (
kclManager . artifactGraph . values ( )
) . find (
( artifact ) = >
artifact . type === 'edgeCut' &&
artifact . consumedEdgeId === firstSelection . artifact ? . id
)
if ( ! edgeCutArtifact || edgeCutArtifact . type !== 'edgeCut' ) {
return Promise . reject (
new Error (
'Failed to retrieve edgeCut artifact from sweepEdge selection'
)
)
}
const edgeTreatmentSelection = {
artifact : edgeCutArtifact ,
codeRef : edgeCutArtifact.codeRef ,
}
const editResult = await editEdgeTreatment (
ast ,
edgeTreatmentSelection ,
parameters
)
if ( err ( editResult ) ) return Promise . reject ( editResult )
modifiedAst = editResult . modifiedAst
focusPath = [ editResult . pathToEdgeTreatmentNode ]
} else {
// Apply chamfer to selection
const chamferResult = await modifyAstWithEdgeTreatmentAndTag (
ast ,
selection ,
parameters ,
dependencies
)
if ( err ( chamferResult ) ) return Promise . reject ( chamferResult )
modifiedAst = chamferResult . modifiedAst
focusPath = chamferResult . pathToEdgeTreatmentNode
}
await updateModelingState (
modifiedAst ,
EXECUTION_TYPE_REAL ,
{
kclManager ,
editorManager ,
codeManager ,
} ,
{
focusPath : focusPath ,
}
2025-01-08 16:05:24 +01:00
)
}
) ,
2025-03-19 11:58:53 -04:00
'actor.parameter.create' : fromPromise (
async ( {
input ,
} : {
input : ModelingCommandSchema [ 'event.parameter.create' ] | undefined
} ) = > {
if ( ! input ) return new Error ( 'No input provided' )
const { value } = input
if ( ! ( 'variableName' in value ) ) {
return new Error ( 'variable name is required' )
}
const newAst = insertNamedConstant ( {
node : kclManager.ast ,
newExpression : value ,
} )
2025-03-25 05:06:27 -04:00
await updateModelingState ( newAst , EXECUTION_TYPE_REAL , {
kclManager ,
editorManager ,
codeManager ,
} )
2025-03-19 11:58:53 -04:00
}
) ,
2025-03-20 16:41:09 -04:00
'actor.parameter.edit' : fromPromise (
async ( {
input ,
} : {
input : ModelingCommandSchema [ 'event.parameter.edit' ] | undefined
} ) = > {
if ( ! input ) return new Error ( 'No input provided' )
// Get the variable AST node to edit
const { nodeToEdit , value } = input
const newAst = structuredClone ( kclManager . ast )
const variableNode = getNodeFromPath < Node < VariableDeclarator > > (
newAst ,
nodeToEdit
)
if (
err ( variableNode ) ||
variableNode . node . type !== 'VariableDeclarator' ||
! variableNode . node
) {
return new Error ( 'No variable found, this is a bug' )
}
// Mutate the variable's value
variableNode . node . init = value . valueAst
2025-03-25 05:06:27 -04:00
await updateModelingState ( newAst , EXECUTION_TYPE_REAL , {
2025-03-20 16:41:09 -04:00
codeManager ,
editorManager ,
kclManager ,
} )
}
) ,
2025-02-15 00:57:04 +11:00
'set-up-draft-circle' : fromPromise (
async ( _ : {
input : Pick < ModelingMachineContext , 'sketchDetails' > & {
data : [ x : number , y : number ]
}
} ) = > {
return { } as SketchDetailsUpdate
}
) ,
'set-up-draft-circle-three-point' : fromPromise (
async ( _ : {
input : Pick < ModelingMachineContext , 'sketchDetails' > & {
data : { p1 : [ x : number , y : number ] ; p2 : [ x : number , y : number ] }
}
} ) = > {
return { } as SketchDetailsUpdate
}
) ,
'set-up-draft-rectangle' : fromPromise (
async ( _ : {
input : Pick < ModelingMachineContext , 'sketchDetails' > & {
data : [ x : number , y : number ]
}
} ) = > {
return { } as SketchDetailsUpdate
}
) ,
'set-up-draft-center-rectangle' : fromPromise (
async ( _ : {
input : Pick < ModelingMachineContext , 'sketchDetails' > & {
data : [ x : number , y : number ]
}
} ) = > {
return { } as SketchDetailsUpdate
}
) ,
2025-03-18 11:14:12 +11:00
'set-up-draft-arc' : fromPromise (
async ( _ : {
input : Pick < ModelingMachineContext , 'sketchDetails' > & {
data : [ x : number , y : number ]
}
} ) = > {
return { } as SketchDetailsUpdate
}
) ,
'set-up-draft-arc-three-point' : fromPromise (
async ( _ : {
input : Pick < ModelingMachineContext , 'sketchDetails' > & {
data : [ x : number , y : number ]
}
} ) = > {
return { } as SketchDetailsUpdate
}
) ,
2025-02-15 00:57:04 +11:00
'setup-client-side-sketch-segments' : fromPromise (
async ( _ : {
input : Pick < ModelingMachineContext , 'sketchDetails' | 'selectionRanges' >
} ) = > {
return undefined
}
) ,
'split-sketch-pipe-if-needed' : fromPromise (
async ( _ : { input : Pick < ModelingMachineContext , 'sketchDetails' > } ) = > {
return { } as SketchDetailsUpdate
}
) ,
2024-12-20 13:39:06 +11:00
'submit-prompt-edit' : fromPromise (
2025-02-15 00:57:04 +11:00
async ( {
input ,
} : {
input : ModelingCommandSchema [ 'Prompt-to-edit' ]
} ) = > { }
2024-12-20 13:39:06 +11:00
) ,
2025-02-19 13:43:27 -05:00
deleteSelectionAstMod : fromPromise (
( {
input : { selectionRanges } ,
} : {
input : { selectionRanges : Selections }
} ) = > {
return new Promise ( ( resolve , reject ) = > {
if ( ! selectionRanges ) {
reject ( new Error ( deletionErrorMessage ) )
}
const selection = selectionRanges . graphSelections [ 0 ]
if ( ! selectionRanges ) {
reject ( new Error ( deletionErrorMessage ) )
}
deleteSelectionPromise ( selection )
. then ( ( result ) = > {
if ( err ( result ) ) {
reject ( result )
return
}
resolve ( result )
} )
. catch ( reject )
} )
}
) ,
2025-02-22 08:09:54 -05:00
appearanceAstMod : fromPromise (
async ( {
input ,
} : {
input : ModelingCommandSchema [ 'Appearance' ] | undefined
} ) = > {
if ( ! input ) return new Error ( 'No input provided' )
// Extract inputs
const ast = kclManager . ast
const { color , nodeToEdit } = input
if ( ! ( nodeToEdit && typeof nodeToEdit [ 1 ] [ 0 ] === 'number' ) ) {
return new Error ( 'Appearance is only an edit flow' )
}
const result = setAppearance ( {
ast ,
nodeToEdit ,
color ,
} )
if ( err ( result ) ) {
return err ( result )
}
2025-03-25 05:06:27 -04:00
await updateModelingState (
2025-02-22 08:09:54 -05:00
result . modifiedAst ,
2025-03-25 05:06:27 -04:00
EXECUTION_TYPE_REAL ,
{
kclManager ,
editorManager ,
codeManager ,
} ,
2025-02-22 08:09:54 -05:00
{
focusPath : [ result . pathToNode ] ,
}
)
}
) ,
2025-04-17 11:44:31 -04:00
translateAstMod : fromPromise (
async ( {
input ,
} : {
input : ModelingCommandSchema [ 'Translate' ] | undefined
} ) = > {
2025-04-30 13:07:39 -04:00
if ( ! input ) return Promise . reject ( new Error ( 'No input provided' ) )
2025-04-17 11:44:31 -04:00
const ast = kclManager . ast
const modifiedAst = structuredClone ( ast )
const { x , y , z , nodeToEdit , selection } = input
let pathToNode = nodeToEdit
if ( ! ( pathToNode && typeof pathToNode [ 1 ] [ 0 ] === 'number' ) ) {
2025-05-02 17:25:33 -04:00
const result = retrievePathToNodeFromTransformSelection (
selection ,
kclManager . artifactGraph ,
ast
)
if ( err ( result ) ) {
return Promise . reject ( result )
2025-04-30 13:07:39 -04:00
}
2025-05-02 17:25:33 -04:00
pathToNode = result
2025-04-30 13:07:39 -04:00
}
// Look for the last pipe with the import alias and a call to translate, with a fallback to rotate.
// Otherwise create one
const importNodeAndAlias = findImportNodeAndAlias ( ast , pathToNode )
if ( importNodeAndAlias ) {
const pipes = findPipesWithImportAlias ( ast , pathToNode , 'translate' )
const lastPipe = pipes . at ( - 1 )
if ( lastPipe && lastPipe . pathToNode ) {
pathToNode = lastPipe . pathToNode
} else {
const otherRelevantPipes = findPipesWithImportAlias (
ast ,
pathToNode ,
'rotate'
)
const lastRelevantPipe = otherRelevantPipes . at ( - 1 )
if ( lastRelevantPipe && lastRelevantPipe . pathToNode ) {
pathToNode = lastRelevantPipe . pathToNode
} else {
pathToNode = insertExpressionNode (
modifiedAst ,
importNodeAndAlias . alias
)
}
2025-04-17 11:44:31 -04:00
}
}
insertVariableAndOffsetPathToNode ( x , modifiedAst , pathToNode )
insertVariableAndOffsetPathToNode ( y , modifiedAst , pathToNode )
insertVariableAndOffsetPathToNode ( z , modifiedAst , pathToNode )
const result = setTranslate ( {
pathToNode ,
modifiedAst ,
x : valueOrVariable ( x ) ,
y : valueOrVariable ( y ) ,
z : valueOrVariable ( z ) ,
} )
if ( err ( result ) ) {
2025-04-30 13:07:39 -04:00
return Promise . reject ( result )
2025-04-17 11:44:31 -04:00
}
await updateModelingState (
result . modifiedAst ,
EXECUTION_TYPE_REAL ,
{
kclManager ,
editorManager ,
codeManager ,
} ,
{
focusPath : [ result . pathToNode ] ,
}
)
}
) ,
rotateAstMod : fromPromise (
async ( {
input ,
} : {
input : ModelingCommandSchema [ 'Rotate' ] | undefined
} ) = > {
2025-04-30 13:07:39 -04:00
if ( ! input ) return Promise . reject ( new Error ( 'No input provided' ) )
2025-04-17 11:44:31 -04:00
const ast = kclManager . ast
const modifiedAst = structuredClone ( ast )
const { roll , pitch , yaw , nodeToEdit , selection } = input
let pathToNode = nodeToEdit
if ( ! ( pathToNode && typeof pathToNode [ 1 ] [ 0 ] === 'number' ) ) {
2025-05-02 17:25:33 -04:00
const result = retrievePathToNodeFromTransformSelection (
selection ,
kclManager . artifactGraph ,
ast
)
if ( err ( result ) ) {
return Promise . reject ( result )
2025-04-30 13:07:39 -04:00
}
2025-05-02 17:25:33 -04:00
pathToNode = result
2025-04-30 13:07:39 -04:00
}
// Look for the last pipe with the import alias and a call to rotate, with a fallback to translate.
// Otherwise create one
const importNodeAndAlias = findImportNodeAndAlias ( ast , pathToNode )
if ( importNodeAndAlias ) {
const pipes = findPipesWithImportAlias ( ast , pathToNode , 'rotate' )
const lastPipe = pipes . at ( - 1 )
if ( lastPipe && lastPipe . pathToNode ) {
pathToNode = lastPipe . pathToNode
} else {
const otherRelevantPipes = findPipesWithImportAlias (
ast ,
pathToNode ,
'translate'
)
const lastRelevantPipe = otherRelevantPipes . at ( - 1 )
if ( lastRelevantPipe && lastRelevantPipe . pathToNode ) {
pathToNode = lastRelevantPipe . pathToNode
} else {
pathToNode = insertExpressionNode (
modifiedAst ,
importNodeAndAlias . alias
)
}
2025-04-17 11:44:31 -04:00
}
}
insertVariableAndOffsetPathToNode ( roll , modifiedAst , pathToNode )
insertVariableAndOffsetPathToNode ( pitch , modifiedAst , pathToNode )
insertVariableAndOffsetPathToNode ( yaw , modifiedAst , pathToNode )
const result = setRotate ( {
pathToNode ,
modifiedAst ,
roll : valueOrVariable ( roll ) ,
pitch : valueOrVariable ( pitch ) ,
yaw : valueOrVariable ( yaw ) ,
} )
if ( err ( result ) ) {
2025-04-30 13:07:39 -04:00
return Promise . reject ( result )
2025-04-17 11:44:31 -04:00
}
await updateModelingState (
result . modifiedAst ,
EXECUTION_TYPE_REAL ,
{
kclManager ,
editorManager ,
codeManager ,
} ,
{
focusPath : [ result . pathToNode ] ,
}
)
}
) ,
2025-04-26 18:26:39 -04:00
cloneAstMod : fromPromise (
async ( {
input ,
} : {
input : ModelingCommandSchema [ 'Clone' ] | undefined
} ) = > {
if ( ! input ) return Promise . reject ( new Error ( 'No input provided' ) )
const ast = kclManager . ast
const { nodeToEdit , selection , variableName } = input
let pathToNode = nodeToEdit
if ( ! ( pathToNode && typeof pathToNode [ 1 ] [ 0 ] === 'number' ) ) {
2025-05-02 17:25:33 -04:00
const result = retrievePathToNodeFromTransformSelection (
selection ,
kclManager . artifactGraph ,
ast
)
if ( err ( result ) ) {
return Promise . reject ( result )
2025-04-26 18:26:39 -04:00
}
2025-05-02 17:25:33 -04:00
pathToNode = result
2025-04-26 18:26:39 -04:00
}
const returnEarly = true
const geometryNode = getNodeFromPath <
2025-04-30 13:07:39 -04:00
VariableDeclaration | ImportStatement | PipeExpression
2025-04-26 18:26:39 -04:00
> (
ast ,
pathToNode ,
2025-04-30 13:07:39 -04:00
[ 'VariableDeclaration' , 'ImportStatement' , 'PipeExpression' ] ,
2025-04-26 18:26:39 -04:00
returnEarly
)
if ( err ( geometryNode ) ) {
return Promise . reject (
new Error ( "Couldn't find corresponding path to node" )
)
}
let geometryName : string | undefined
if ( geometryNode . node . type === 'VariableDeclaration' ) {
geometryName = geometryNode . node . declaration . id . name
} else if (
2025-04-30 13:07:39 -04:00
geometryNode . node . type === 'ImportStatement' &&
geometryNode . node . selector . type === 'None' &&
geometryNode . node . selector . alias
2025-04-26 18:26:39 -04:00
) {
2025-04-30 13:07:39 -04:00
geometryName = geometryNode . node . selector . alias ? . name
2025-04-26 18:26:39 -04:00
} else {
return Promise . reject (
new Error ( "Couldn't find corresponding geometry" )
)
}
const result = addClone ( {
ast ,
geometryName ,
variableName ,
} )
if ( err ( result ) ) {
return Promise . reject ( err ( result ) )
}
await updateModelingState (
result . modifiedAst ,
EXECUTION_TYPE_REAL ,
{
kclManager ,
editorManager ,
codeManager ,
} ,
{
focusPath : [ result . pathToNode ] ,
}
)
}
) ,
2025-03-18 20:25:51 -07:00
exportFromEngine : fromPromise (
async ( { } : { input? : ModelingCommandSchema [ 'Export' ] } ) = > {
return undefined as Error | undefined
}
) ,
makeFromEngine : fromPromise (
async ( { } : {
input ? : {
machineManager : MachineManager
} & ModelingCommandSchema [ 'Make' ]
} ) = > {
return undefined as Error | undefined
}
) ,
2025-03-28 14:56:48 +11:00
boolSubtractAstMod : fromPromise (
async ( {
input ,
} : {
input : ModelingCommandSchema [ 'Boolean Subtract' ] | undefined
} ) = > {
if ( ! input ) {
return new Error ( 'No input provided' )
}
const { target , tool } = input
if (
! target . graphSelections [ 0 ] . artifact ||
! tool . graphSelections [ 0 ] . artifact
) {
return new Error ( 'No artifact in selections found' )
}
await applySubtractFromTargetOperatorSelections (
target . graphSelections [ 0 ] ,
tool . graphSelections [ 0 ] ,
{
kclManager ,
codeManager ,
engineCommandManager ,
editorManager ,
}
)
}
) ,
boolUnionAstMod : fromPromise (
async ( {
input ,
} : {
input : ModelingCommandSchema [ 'Boolean Union' ] | undefined
} ) = > {
if ( ! input ) {
return new Error ( 'No input provided' )
}
const { solids } = input
if ( ! solids . graphSelections [ 0 ] . artifact ) {
return new Error ( 'No artifact in selections found' )
}
await applyUnionFromTargetOperatorSelections ( solids , {
kclManager ,
codeManager ,
engineCommandManager ,
editorManager ,
} )
}
) ,
boolIntersectAstMod : fromPromise (
async ( {
input ,
} : {
input : ModelingCommandSchema [ 'Boolean Union' ] | undefined
} ) = > {
if ( ! input ) {
return new Error ( 'No input provided' )
}
const { solids } = input
if ( ! solids . graphSelections [ 0 ] . artifact ) {
return new Error ( 'No artifact in selections found' )
}
await applyIntersectFromTargetOperatorSelections ( solids , {
kclManager ,
codeManager ,
engineCommandManager ,
editorManager ,
} )
}
) ,
2025-04-15 23:27:50 +10:00
'reeval-node-paths' : fromPromise (
async ( {
input : { sketchDetails } ,
} : {
input : Pick < ModelingMachineContext , 'sketchDetails' >
} ) = > {
const errorMessage =
'Unable to maintain sketch mode - code changes affected sketch references. Please re-enter.'
if ( ! sketchDetails ) {
return Promise . reject ( new Error ( errorMessage ) )
}
// hasErrors is for parse errors, errors is for runtime errors
if ( kclManager . errors . length > 0 || kclManager . hasErrors ( ) ) {
// if there's an error in the execution, we don't actually want to disable sketch mode
// instead we'll give the user the chance to fix their error
return {
updatedEntryNodePath : sketchDetails.sketchEntryNodePath ,
updatedSketchNodePaths : sketchDetails.sketchNodePaths ,
updatedPlaneNodePath : sketchDetails.planeNodePath ,
}
}
const updatedPlaneNodePath = updatePathToNodesAfterEdit (
kclManager . _lastAst ,
kclManager . ast ,
sketchDetails . planeNodePath
)
if ( err ( updatedPlaneNodePath ) ) {
return Promise . reject ( new Error ( errorMessage ) )
}
const maybePlaneArtifact = [ . . . kclManager . artifactGraph . values ( ) ] . find (
( artifact ) = > {
const codeRef = getFaceCodeRef ( artifact )
if ( ! codeRef ) return false
return (
stringifyPathToNode ( codeRef . pathToNode ) ===
stringifyPathToNode ( updatedPlaneNodePath )
)
}
)
if (
! maybePlaneArtifact ||
( maybePlaneArtifact . type !== 'plane' &&
maybePlaneArtifact . type !== 'startSketchOnFace' )
) {
return Promise . reject ( new Error ( errorMessage ) )
}
let planeArtifact : Artifact | undefined
if ( maybePlaneArtifact . type === 'plane' ) {
planeArtifact = maybePlaneArtifact
} else {
const face = kclManager . artifactGraph . get ( maybePlaneArtifact . faceId )
if ( face ) {
planeArtifact = face
}
}
if (
! planeArtifact ||
( planeArtifact . type !== 'cap' &&
planeArtifact . type !== 'wall' &&
planeArtifact . type !== 'plane' )
) {
return Promise . reject ( new Error ( errorMessage ) )
}
const newPaths = getPathsFromPlaneArtifact (
planeArtifact ,
kclManager . artifactGraph ,
kclManager . ast
)
return {
updatedEntryNodePath : newPaths [ 0 ] ,
updatedSketchNodePaths : newPaths ,
updatedPlaneNodePath ,
}
}
) ,
2024-09-09 19:59:36 +03:00
} ,
2025-01-06 14:08:18 -05:00
// end actors
2024-09-09 19:59:36 +03:00
} ) . createMachine ( {
2025-05-05 22:13:13 +10:00
/ * * @ x s t a t e - l a y o u t N 4 I g p g J g 5 m D O I C 5 Q F k D 2 E w B s C W A 7 K A x A M I C G u A x l g N o A M A u o q A A 6 q z Y A u 2 q u j I A H o g C 0 A N m E B m A H Q B G A J w B W A B w K p S q T Q D s U s Q B o Q A T 0 Q A m d Q B Y J Y m m P V j D h 4 Y s M K Z A X 2 e 6 0 G H P g I B l M O w A C W C w w c k 5 u W g Y k E B Y 2 c J 5 o g Q R B N R k z O U s F O T F F G h N Z Q 1 0 D B B k Z C X U 7 S z l 5 K R M T W x N X d 3 Q s P E I - Q I B b V A B X Y K D 2 E n Y w S N 5 Y j i 4 E 0 C S U q S l D C Q U T G R p Z e f V 1 G W F 1 I s Q x b Y k T O S 0 F D R o 5 d R p D K T k m k A 9 W 7 w 6 A 8 m 5 h v n Z R 6 P H 4 3 h n U 4 Q S V Y W b Y y M Q K d Q K R y 7 B D C f L S Y T X Q z H U Q m J Q u N z 3 F p e d r + A J + K C d M C 4 Q I A e Q A b m A A E 6 Y E h 6 W B f Z i s C b c P 5 C O a K C T y d Q X H k y d R w j Y w 4 4 L S z 2 Y Q a W w 5 e x 3 B 4 4 4 j E 4 Z U l 4 k I l U k g B b h B E J h S a M m L M 3 6 J d l S S H m V b G Z Y 5 N L C B Q w 0 4 0 C S I m j K U r Z F S y 7 F t A g A F V Q U C g m D A A Q w A D M S D 1 M I E m L T c M H y d g 2 A A j b A 4 d h 6 f U - S Z s 5 L z N G H G g S m T z C G 1 W o w + S O 1 1 b M Q m H K V R q Y u V t C T Y C B B g g A U R J 1 K C A G t - O Q A B a Z w 3 Z 4 3 J Z F S Q 5 g 4 R q F S G Z b I m E V M q Q y E Q y F w + a 3 J t e - C t 9 t g L s 9 5 W w A f s Y d U K R R J l x c f T I Q N c p Q 2 3 X f l K e y F f S I U T T u Q A X M c I X D I T h S J 6 n g t m 2 H a d h 8 V I 9 B g o 7 3 q y E 4 i D Q j q b L U p i F h Y V g m D C h h W J I j h X B c c i 2 k s 5 E Q Y 8 U C H h 2 A B K Y D k q g m C U k h L J T P w Q i a O o 5 g X D c C i i M W V j C D C Y i l A o T r k T Y P E V L U 1 E 4 n R x 4 + A A 7 m A Y B M O x R q P s k 1 S 8 Y W P K 1 v k y L G N + x T X P s B a L j Y e Q S i Y A o K V B R 4 E A A M q g o a f P Q Y x j i h O m C I o g J p F O c i G D I 1 z b D C C 6 S C 6 q w m N s t a K G i j k H t B y l D l g m B a Q + X G 6 S s E j G I W y y 1 F C K i i T + C C m t c 5 R 5 H F T i b N s 4 F 7 p B K X O Q A Y q m Q a e b e B r I Z x M z V I 6 6 E Z L I l z 1 L Y c g E a s c g S N U s h z v Y A F W M l t G p c Q Q 6 q q G 1 J Z b 5 O V o Z I G w 4 Q K p p h b a B G L t N 8 g S W K 7 q G M t S k E E x i o S E w J D q k S S o S O Q V J g E M I x e d 8 P l 9 d x x Y z a F h m m m o x x 2 u V Y h S L a M 2 l k s K i W O C d 2 r Y 9 J L P a 9 q r + N S E i Q B w 2 1 A 8 k 1 h r s Y O R Q 3 W h F l c U t Y 8 o c S y b C s q x f m j z m w S w V J d d 5 v U 5 o I 1 g L F k t p w 6 U l y Q m J N U z c W e S h Y Y L r W I 2 z T N S t z n I C Q A 5 E 3 z 1 i S K c 1 o X L a A r L u V o W w 4 s 0 n K P s R x Q o r W L K - d A A i I T D N q Q a 6 h E - 1 3 h x f N Z J I I u X K U c K h X k B H 2 P + p r 1 E c p q X L Y b M d j 6 Y A f I I 7 C o I I R A A I L 2 5 r E 5 i K b j j k d b 6 z H P h 5 W g d O s 6 g d Y x y Z B i S s 0 f d p K h q G w T R r G f 3 d V m O 0 z H O + l y + I Y K O D c M i R R K k i V i P T g Z I R 6 i x 8 e A A S r R 8 F n f k D U 6 N D F l k x h z P M B E Z I N Q 3 8 b W M d N X X q 0 A A p U q g n R M O w S c p w T 3 M A 7 z q G I r x c x 1 P O V S U Z F x a A k 4 J h T T n W q N d b Y n 2 c m n J g T A f r q g o G 3 H m X t n 7 n H K D n D Y - c V j 2 B 0 M b T Y k l a z y z r L h R q t d F K r R 9 N A 2 A t J h h L 1 2 h U Q W y J i x h X s n U B Q G D i j 5 F M I s W c J k A L 9 x n g Q e i q B B g U I 9 j 1 e B f l N A L F t C F C E 5 F N h x S k D C e o S w E Z y 0 I m v A U L o e F E E w N w W B j 8 R F U L X l y f k P I 4 S g T y H I Y u L C 6 y 8 S W H n S O F h S g 8 I A E K o B Y j 9 X A + I e h J n Y O q M I l C Z g V E B H F e Q S w r H y B p n s I s f E r R 1 A 0 O C L Q T i X F B j I A E A A q r g P U Q i O 7 E 3 5 v m L I g l Z Y K P y G J C o k k s j m O y D U W 0 C T X H J I A J K n m C L 4 z J g M t b V C 5 J o G w c t b J o j E k o X i - 9 F x j U I r D G e E g h x t j A K f V u s A C B p y 5 t g c M Y Q A h Q H V E w I c A Q W B M E j L 9 C A f j u K 1 E M X F V Y 1 C L b h I q s i M w m h S q m F s J K G 2 z Y W p B g k L A I c q A V J T L I H A W Z 8 z F m B B W S Q N Z A Q w B X 0 4 J A P Z u Z 9 J g n E l C d C K x 9 Y 7 H K m o N h 5 w 6 j b G E F s Z 0 B C Q G K R 8 B e Y c x A y C U E w L i w c I 5 m l P x 0 r U K q a J R o 8 S u G k Z h i A 1 D i R n N U P 8 a J u S 7 k I S 2 E l l 4 h w E E d p 1 Y M w R C S K n B U s Y i k I b D o r S G i c E Y l p z o R U J o c i d Z B K m G n s f H F e L + W v A w E C i A H A Q w 9 C p G 0 f s p L w U 3 E 0 D N O J L o b g 5 A 2 B N c q K D 2 H A j h n M V Y - I 7 q 8 v x U Q b g s B v E k D w A E F S H B 1 m 4 B x h A A I 5 I S C Y B 6 D o z 2 2 k c p z A 2 F y N e V K A p L i H j D O y C J T B x X y F a r l 2 K e U 6 o k D 6 o c t T V b q 2 F W A U V J J o 3 U k 4 O Q W N l q Q q X M S k c a w e Q K g W K Z Q K M w c w c j i X 2 P L E Z W r S 2 k v L T q q t H Y 1 Y D m 1 P W w I r z T U A C 8 3 i t v J X o p I N w X T S B s Z Y Q e S J o b F D h o i P i Y U b h H G L E c b 1 Z a K 2 z u P H 6 3 A A b 1 T B p X d g d d J J Y 0 h k T I M G B l q a V O m O h o V Q l t G U I E t L - d F s V H A V E s H e q d D 7 n L P t f U G 9 x l J 5 k t s w L + g N h K E 3 C K T T u y e o M Q l b C k W D E O i 5 z A i V A h K b I A F 7 n 7 l o h W 6 d p L H 3 E H 9 Y G 4 N a d H E + A C A A D T b b I I E W g 5 h g w 2 F Y E 9 e w L D T Q O i b L I - 9 x B i E Q 3 y t j f K O O o e 4 + 4 3 j - G A C a w n S l R 3 o 4 i e c C L a Z 1 j K B U w s 5 j 9 h M K Y 3 b V j y G O x a b f e 4 s g g Y C N Z J z F a w K 1 R 6 i i E X I i I 4 B E 8 l A n g 8 W R K 6 J N X c o P E 5 m d K G u N u Y C E G f A 7 A y X t x a R O K 1 j o 0 j z A 0 K a C E E o z m h X R U o s E U d Z b b F U 8 O d T w 5 N N J f Q x s 6 k k D c C G v I N s 5 U h q 8 M A a 3 U R p l c V R S m G l o w k E c i Y Z w 2 m t k W s H I Q r Z B 5 D V o c d X K 2 J Z f d p g I H 6 v 2 D E w J g P Q A R Y 3 Y C g L g Q D W x D g 4 K Y e J F Q U N I r q u k G v f Y N w A L o M W 8 t h r a 3 k u Y e b b G 3 b + 2 c B H e E 9 F F 0 9 l j G O H - u Z p l - J A k o x 3 B 6 j Y L 3 n N P s a 8 G 2 A E a m A B G T o J t t c V F j i T B 1 c W q I X j Y j q B J d 0 4 e C a z w 4 S y 5 p H 7 i U c A v R 6 g A I B m + v Z W I 9 j 0 O k W p X m K 2 A R Z Y 2 D E S E X 5 E c R j t 0 J 1 x f v V T x H 7 2 m t g A A I 4 9 B - a l q A 6 W 2 3 G C B C s K w j q R Z h U m s o f K a q T h L H s M A h 5 L G J f s d W 2 h 4 N L 1 1 Q 7 e o C z z u T L y K O g l L Y O G x h + I C j O q B Q 4 h E Q r 8 n d Z s S n F v q f S + D d 9 b o l I X g 0 - Y A y B 3 x N i o L B U K c W z I J M g E R U N O J h e R D 5 z l N H C Y P G n n J 6 u D P f Y 1 p r 8 D m r 5 W 2 6 1 2 R l B 2 o Y 4 6 + 0 - 8 G Z L H Q u C L I V r C - 1 e L + t f A w Z k 4 s X B a F U o M 0 T M m R z v a e Y X J t g X B w u I Q S J v m P L d e 0 e Z 5 e I e h o 5 F U S E k M y I D a N b L g Z i A 5 1 - s E 3 4 I c g O B F S C D Y B g G - O q b 9 1 p 3 7 H s Q l r K x c k U P B r I h Y b S R Q s G Y c S I s e y U Q M R Y Q b v F b J 5 Z u A I T f R d Z - P f A - P A Y - M A U - c - S - b A a - W - M A e - U l R - J d W A V - G 8 O B f r C q N I S S Y X B o W Q Z Q O o P t C q K w R 0 L Q b Y Q q M E A d G L E t c X J D S X U - a A r f J - R U G Z a k C + K k Z 6 c h U M V A K k T o F A p g C - K - E k G - C Z b A v l X A u A 1 - e P L W d C U G O G B W N F J w b N Y o H k A A 2 J c e Z E E K V g 0 3 F f N O F S I N Z d M Q z 9 D d H D b r f 9 S g A I P A U Q g g f f O M Q - J A i Q G A G + T b O w w Q F w 1 A Y f O v I E W 5 D Q L c e o C D Y w S 4 J 0 U Q H O G V L Q 0 w 5 f V j C w q w j b G w r b H 9 B w - D Z w 3 A V w g Q s Q 4 Q o Y U Q 8 Q 7 w - w Q Q P w 7 9 T A A I v I o I 1 Q i c Z E c E J 0 Z m e 7 Y s S u A i A W M 2 Q t f I U 4 T v F 7 V I o 1 T 7 b A b D X D R w 4 M Q I t w h A o - V A E - H w w Q I Y 7 D G o 0 Q 4 I o 4 c o L Q f + J G f Y c E K I 8 Q M w C i A U N l e Y R E f o y w w Y p t Y Y r I v 9 H I i Y g o o Q m M Y o s Q i Q u Y h Y 2 N J Y u o z L C l H K C 5 Q E O J B 1 W w W w L I c H S D a o B Y f k Y s d 3 O G K T F T M X M 3 K d A Y w I X T Q T X I 1 w 9 w 5 A x A m Y 5 A u Y k g J M W A Q Q P g N 4 4 I j I G j N R S w d C U i C D H O a f e Q E d K 4 C U H i U X W L W E t T e E g I R E g T Z E 1 A B 6 K k Q Q o o 9 g E o p 4 8 o 7 E 3 E - E w I w k x V N
2024-09-09 19:59:36 +03:00
id : 'Modeling' ,
context : ( { input } ) = > ( {
. . . modelingMachineDefaultContext ,
. . . input ,
} ) ,
states : {
idle : {
on : {
'Enter sketch' : [
{
target : 'animating to existing sketch' ,
guard : 'Selection is on face' ,
} ,
'Sketch no face' ,
] ,
Extrude : {
2025-01-31 13:34:38 -05:00
target : 'Applying extrude' ,
reenter : true ,
2024-09-09 19:59:36 +03:00
} ,
2024-09-17 08:29:52 -05:00
Revolve : {
2025-03-31 18:13:34 -04:00
target : 'Applying revolve' ,
reenter : true ,
2024-09-17 08:29:52 -05:00
} ,
2025-01-11 08:20:49 -05:00
Sweep : {
target : 'Applying sweep' ,
reenter : true ,
} ,
2024-12-04 17:24:16 -05:00
Loft : {
target : 'Applying loft' ,
2024-12-09 15:20:48 -05:00
reenter : true ,
} ,
Shell : {
target : 'Applying shell' ,
2024-12-04 17:24:16 -05:00
reenter : true ,
} ,
2024-09-09 19:59:36 +03:00
Fillet : {
2025-01-08 16:05:24 +01:00
target : 'Applying fillet' ,
reenter : true ,
2024-09-09 19:59:36 +03:00
} ,
2025-01-14 18:08:32 +01:00
Chamfer : {
target : 'Applying chamfer' ,
reenter : true ,
} ,
2025-03-19 11:58:53 -04:00
'event.parameter.create' : {
target : '#Modeling.parameter.creating' ,
} ,
2025-04-25 18:21:19 +02:00
2025-03-20 16:41:09 -04:00
'event.parameter.edit' : {
target : '#Modeling.parameter.editing' ,
} ,
2025-03-19 11:58:53 -04:00
2024-09-09 19:59:36 +03:00
Export : {
2025-03-18 20:25:51 -07:00
target : 'Exporting' ,
2024-09-09 19:59:36 +03:00
guard : 'Has exportable geometry' ,
} ,
Make : {
2025-03-18 20:25:51 -07:00
target : 'Making' ,
2024-09-09 19:59:36 +03:00
guard : 'Has exportable geometry' ,
} ,
'Delete selection' : {
2025-02-19 13:43:27 -05:00
target : 'Applying Delete selection' ,
2024-09-09 19:59:36 +03:00
guard : 'has valid selection for deletion' ,
2025-02-19 13:43:27 -05:00
reenter : true ,
2024-09-09 19:59:36 +03:00
} ,
'Text-to-CAD' : {
target : 'idle' ,
reenter : false ,
actions : [ 'Submit to Text-to-CAD API' ] ,
} ,
2024-11-26 11:36:14 -05:00
'Offset plane' : {
target : 'Applying offset plane' ,
reenter : true ,
} ,
2024-12-20 13:39:06 +11:00
2025-02-07 10:16:36 -05:00
Helix : {
target : 'Applying helix' ,
reenter : true ,
} ,
2024-12-20 13:39:06 +11:00
'Prompt-to-edit' : 'Applying Prompt-to-edit' ,
2025-02-22 08:09:54 -05:00
Appearance : {
target : 'Applying appearance' ,
reenter : true ,
} ,
2025-03-28 14:56:48 +11:00
2025-04-17 11:44:31 -04:00
Translate : {
target : 'Applying translate' ,
reenter : true ,
} ,
Rotate : {
target : 'Applying rotate' ,
reenter : true ,
} ,
2025-04-26 18:26:39 -04:00
Clone : {
target : 'Applying clone' ,
reenter : true ,
} ,
2025-03-28 14:56:48 +11:00
'Boolean Subtract' : 'Boolean subtracting' ,
'Boolean Union' : 'Boolean uniting' ,
'Boolean Intersect' : 'Boolean intersecting' ,
2024-09-09 19:59:36 +03:00
} ,
entry : 'reset client scene mouse handlers' ,
states : {
hidePlanes : {
on : {
2025-03-21 11:44:44 +01:00
'Artifact graph populated' : {
target : 'showPlanes' ,
guard : 'no kcl errors' ,
} ,
2024-09-09 19:59:36 +03:00
} ,
entry : 'hide default planes' ,
} ,
showPlanes : {
on : {
'Artifact graph emptied' : 'hidePlanes' ,
} ,
2025-05-08 03:54:40 +10:00
entry : [ 'show default planes' ] ,
2024-09-12 22:06:50 -04:00
description : ` We want to disable selections and hover highlights here, because users can't do anything with that information until they actually add something to the scene. The planes are just for orientation here. ` ,
exit : 'set selection filter to defaults' ,
2024-09-09 19:59:36 +03:00
} ,
} ,
initial : 'hidePlanes' ,
} ,
Sketch : {
states : {
SketchIdle : {
on : {
'Make segment vertical' : {
guard : 'Can make selection vertical' ,
target : 'Await constrain vertically' ,
} ,
'Make segment horizontal' : {
guard : 'Can make selection horizontal' ,
target : 'Await constrain horizontally' ,
} ,
'Constrain horizontal distance' : {
target : 'Await horizontal distance info' ,
guard : 'Can constrain horizontal distance' ,
} ,
'Constrain vertical distance' : {
target : 'Await vertical distance info' ,
guard : 'Can constrain vertical distance' ,
} ,
'Constrain ABS X' : {
target : 'Await ABS X info' ,
guard : 'Can constrain ABS X' ,
} ,
'Constrain ABS Y' : {
target : 'Await ABS Y info' ,
guard : 'Can constrain ABS Y' ,
} ,
'Constrain angle' : {
target : 'Await angle info' ,
guard : 'Can constrain angle' ,
} ,
'Constrain length' : {
2024-12-09 16:43:58 -05:00
target : 'Apply length constraint' ,
2024-09-09 19:59:36 +03:00
guard : 'Can constrain length' ,
} ,
'Constrain perpendicular distance' : {
target : 'Await perpendicular distance info' ,
guard : 'Can constrain perpendicular distance' ,
} ,
'Constrain horizontally align' : {
guard : 'Can constrain horizontally align' ,
target : 'Await constrain horizontally align' ,
} ,
'Constrain vertically align' : {
guard : 'Can constrain vertically align' ,
target : 'Await constrain vertically align' ,
} ,
'Constrain snap to X' : {
guard : 'Can constrain snap to X' ,
target : 'Await constrain snap to X' ,
} ,
'Constrain snap to Y' : {
guard : 'Can constrain snap to Y' ,
target : 'Await constrain snap to Y' ,
} ,
'Constrain equal length' : {
guard : 'Can constrain equal length' ,
target : 'Await constrain equal length' ,
} ,
'Constrain parallel' : {
target : 'Await constrain parallel' ,
2025-04-15 23:27:50 +10:00
guard : 'Can constrain parallel' ,
2024-09-09 19:59:36 +03:00
} ,
'Constrain remove constraints' : {
guard : 'Can constrain remove constraints' ,
target : 'Await constrain remove constraints' ,
} ,
'code edit during sketch' : 'clean slate' ,
'change tool' : {
target : 'Change Tool' ,
2025-02-15 00:57:04 +11:00
reenter : true ,
2024-09-09 19:59:36 +03:00
} ,
} ,
2025-04-15 23:27:50 +10:00
states : {
'set up segments' : {
invoke : {
src : 'setup-client-side-sketch-segments' ,
id : 'setup-client-side-sketch-segments3' ,
input : ( { context : { sketchDetails , selectionRanges } } ) = > ( {
sketchDetails ,
selectionRanges ,
} ) ,
onDone : [
{
target : 'scene drawn' ,
guard : 'is-error-free' ,
} ,
{
target : 'sketch-can-not-be-drawn' ,
reenter : true ,
} ,
] ,
2025-04-25 18:21:19 +02:00
onError : {
target : '#Modeling.idle' ,
reenter : true ,
} ,
2025-04-15 23:27:50 +10:00
} ,
} ,
'scene drawn' : { } ,
'sketch-can-not-be-drawn' : {
entry : 'show sketch error toast' ,
exit : 'remove sketch error toast' ,
} ,
} ,
initial : 'set up segments' ,
2024-09-09 19:59:36 +03:00
} ,
'Await horizontal distance info' : {
invoke : {
src : 'Get horizontal info' ,
id : 'get-horizontal-info' ,
input : ( { context : { selectionRanges , sketchDetails } } ) = > ( {
selectionRanges ,
sketchDetails ,
} ) ,
onDone : {
target : 'SketchIdle' ,
actions : 'Set selection' ,
} ,
onError : 'SketchIdle' ,
} ,
} ,
'Await vertical distance info' : {
invoke : {
src : 'Get vertical info' ,
id : 'get-vertical-info' ,
input : ( { context : { selectionRanges , sketchDetails } } ) = > ( {
selectionRanges ,
sketchDetails ,
} ) ,
onDone : {
target : 'SketchIdle' ,
actions : 'Set selection' ,
} ,
onError : 'SketchIdle' ,
} ,
} ,
'Await ABS X info' : {
invoke : {
src : 'Get ABS X info' ,
id : 'get-abs-x-info' ,
input : ( { context : { selectionRanges , sketchDetails } } ) = > ( {
selectionRanges ,
sketchDetails ,
} ) ,
onDone : {
target : 'SketchIdle' ,
actions : 'Set selection' ,
} ,
onError : 'SketchIdle' ,
} ,
} ,
'Await ABS Y info' : {
invoke : {
src : 'Get ABS Y info' ,
id : 'get-abs-y-info' ,
input : ( { context : { selectionRanges , sketchDetails } } ) = > ( {
selectionRanges ,
sketchDetails ,
} ) ,
onDone : {
target : 'SketchIdle' ,
actions : 'Set selection' ,
} ,
onError : 'SketchIdle' ,
} ,
} ,
'Await angle info' : {
invoke : {
src : 'Get angle info' ,
id : 'get-angle-info' ,
input : ( { context : { selectionRanges , sketchDetails } } ) = > ( {
selectionRanges ,
sketchDetails ,
} ) ,
onDone : {
target : 'SketchIdle' ,
actions : 'Set selection' ,
} ,
onError : 'SketchIdle' ,
} ,
} ,
2024-12-09 16:43:58 -05:00
'Apply length constraint' : {
2024-09-09 19:59:36 +03:00
invoke : {
2024-12-09 16:43:58 -05:00
src : 'astConstrainLength' ,
id : 'AST-constrain-length' ,
input : ( { context : { selectionRanges , sketchDetails } , event } ) = > {
const data =
event . type === 'Constrain length' ? event.data : undefined
return {
selectionRanges ,
sketchDetails ,
lengthValue : data?.length ,
}
} ,
2024-09-09 19:59:36 +03:00
onDone : {
target : 'SketchIdle' ,
actions : 'Set selection' ,
} ,
onError : 'SketchIdle' ,
} ,
} ,
'Await perpendicular distance info' : {
invoke : {
src : 'Get perpendicular distance info' ,
id : 'get-perpendicular-distance-info' ,
input : ( { context : { selectionRanges , sketchDetails } } ) = > ( {
selectionRanges ,
sketchDetails ,
} ) ,
onDone : {
target : 'SketchIdle' ,
actions : 'Set selection' ,
} ,
onError : 'SketchIdle' ,
} ,
} ,
'Line tool' : {
exit : [ ] ,
states : {
Init : {
entry : 'setup noPoints onClick listener' ,
on : {
'Add start point' : {
target : 'normal' ,
2025-02-15 00:57:04 +11:00
actions : 'set up draft line' ,
2024-09-09 19:59:36 +03:00
} ,
} ,
2025-03-18 11:14:12 +11:00
exit : 'remove draft entities' ,
2024-09-09 19:59:36 +03:00
} ,
2025-02-15 00:57:04 +11:00
normal : {
on : {
'Close sketch' : {
target : 'Finish profile' ,
reenter : true ,
} ,
} ,
} ,
'Finish profile' : {
invoke : {
src : 'setup-client-side-sketch-segments' ,
id : 'setup-client-side-sketch-segments7' ,
onDone : 'Init' ,
onError : 'Init' ,
input : ( { context : { sketchDetails , selectionRanges } } ) = > ( {
sketchDetails ,
selectionRanges ,
} ) ,
} ,
} ,
2024-09-09 19:59:36 +03:00
} ,
initial : 'Init' ,
on : {
'change tool' : {
target : 'Change Tool' ,
2025-02-15 00:57:04 +11:00
reenter : true ,
2024-09-09 19:59:36 +03:00
} ,
} ,
} ,
Init : {
always : [
{
target : 'SketchIdle' ,
guard : 'is editing existing sketch' ,
} ,
'Line tool' ,
] ,
} ,
'Tangential arc to' : {
on : {
'change tool' : {
target : 'Change Tool' ,
2025-02-15 00:57:04 +11:00
reenter : true ,
2024-09-09 19:59:36 +03:00
} ,
} ,
2025-02-15 00:57:04 +11:00
states : {
Init : {
on : {
'Continue existing profile' : {
target : 'normal' ,
actions : 'set up draft arc' ,
} ,
} ,
entry : 'setup noPoints onClick listener' ,
2025-03-18 11:14:12 +11:00
exit : 'remove draft entities' ,
2025-02-15 00:57:04 +11:00
} ,
normal : {
on : {
'Close sketch' : {
target : 'Finish profile' ,
reenter : true ,
} ,
} ,
} ,
'Finish profile' : {
invoke : {
src : 'setup-client-side-sketch-segments' ,
id : 'setup-client-side-sketch-segments6' ,
onDone : 'Init' ,
onError : 'Init' ,
input : ( { context : { sketchDetails , selectionRanges } } ) = > ( {
sketchDetails ,
selectionRanges ,
} ) ,
} ,
} ,
} ,
initial : 'Init' ,
2024-09-09 19:59:36 +03:00
} ,
'undo startSketchOn' : {
invoke : {
src : 'AST-undo-startSketchOn' ,
id : 'AST-undo-startSketchOn' ,
input : ( { context : { sketchDetails } } ) = > ( { sketchDetails } ) ,
onDone : {
target : '#Modeling.idle' ,
actions : 'enter modeling mode' ,
2025-04-25 18:21:19 +02:00
reenter : true ,
2024-09-09 19:59:36 +03:00
} ,
} ,
} ,
'Rectangle tool' : {
states : {
'Awaiting second corner' : {
on : {
2025-02-15 00:57:04 +11:00
'Finish rectangle' : {
target : 'Finished Rectangle' ,
actions : 'reset deleteIndex' ,
} ,
2024-09-09 19:59:36 +03:00
} ,
} ,
'Awaiting origin' : {
on : {
2025-03-18 11:14:12 +11:00
'click in scene' : {
2025-02-15 00:57:04 +11:00
target : 'adding draft rectangle' ,
reenter : true ,
2024-09-09 19:59:36 +03:00
} ,
} ,
2025-02-15 00:57:04 +11:00
entry : 'listen for rectangle origin' ,
2024-09-09 19:59:36 +03:00
} ,
'Finished Rectangle' : {
2025-02-15 00:57:04 +11:00
invoke : {
src : 'setup-client-side-sketch-segments' ,
id : 'setup-client-side-sketch-segments' ,
onDone : 'Awaiting origin' ,
input : ( { context : { sketchDetails , selectionRanges } } ) = > ( {
sketchDetails ,
selectionRanges ,
} ) ,
} ,
} ,
'adding draft rectangle' : {
invoke : {
src : 'set-up-draft-rectangle' ,
id : 'set-up-draft-rectangle' ,
onDone : {
target : 'Awaiting second corner' ,
actions : 'update sketchDetails' ,
} ,
onError : 'Awaiting origin' ,
input : ( { context : { sketchDetails } , event } ) = > {
2025-03-18 11:14:12 +11:00
if ( event . type !== 'click in scene' )
2025-02-15 00:57:04 +11:00
return {
sketchDetails ,
data : [ 0 , 0 ] ,
}
return {
sketchDetails ,
data : event.data ,
}
} ,
} ,
2024-09-09 19:59:36 +03:00
} ,
} ,
initial : 'Awaiting origin' ,
on : {
'change tool' : {
target : 'Change Tool' ,
2025-02-15 00:57:04 +11:00
reenter : true ,
2024-09-09 19:59:36 +03:00
} ,
} ,
} ,
2024-11-18 10:04:09 -05:00
'Center Rectangle tool' : {
states : {
'Awaiting corner' : {
on : {
2025-02-15 00:57:04 +11:00
'Finish center rectangle' : {
target : 'Finished Center Rectangle' ,
actions : 'reset deleteIndex' ,
} ,
2024-11-18 10:04:09 -05:00
} ,
} ,
'Awaiting origin' : {
on : {
'Add center rectangle origin' : {
2025-02-15 00:57:04 +11:00
target : 'add draft center rectangle' ,
reenter : true ,
2024-11-18 10:04:09 -05:00
} ,
} ,
2025-02-15 00:57:04 +11:00
entry : 'listen for center rectangle origin' ,
2024-11-18 10:04:09 -05:00
} ,
'Finished Center Rectangle' : {
2025-02-15 00:57:04 +11:00
invoke : {
src : 'setup-client-side-sketch-segments' ,
id : 'setup-client-side-sketch-segments2' ,
onDone : 'Awaiting origin' ,
input : ( { context : { sketchDetails , selectionRanges } } ) = > ( {
sketchDetails ,
selectionRanges ,
} ) ,
} ,
} ,
'add draft center rectangle' : {
invoke : {
src : 'set-up-draft-center-rectangle' ,
id : 'set-up-draft-center-rectangle' ,
onDone : {
target : 'Awaiting corner' ,
actions : 'update sketchDetails' ,
} ,
onError : 'Awaiting origin' ,
input : ( { context : { sketchDetails } , event } ) = > {
if ( event . type !== 'Add center rectangle origin' )
return {
sketchDetails ,
data : [ 0 , 0 ] ,
}
return {
sketchDetails ,
data : event.data ,
}
} ,
} ,
2024-11-18 10:04:09 -05:00
} ,
} ,
initial : 'Awaiting origin' ,
on : {
'change tool' : {
target : 'Change Tool' ,
2025-02-15 00:57:04 +11:00
reenter : true ,
2024-11-18 10:04:09 -05:00
} ,
} ,
} ,
2024-09-09 19:59:36 +03:00
'clean slate' : {
2025-04-15 23:27:50 +10:00
invoke : {
src : 'reeval-node-paths' ,
id : 'reeval-node-paths' ,
input : ( { context : { sketchDetails } } ) = > ( {
sketchDetails ,
} ) ,
onDone : {
target : 'SketchIdle' ,
actions : 'update sketchDetails' ,
} ,
onError : {
target : '#Modeling.idle' ,
actions : 'toastError' ,
2025-04-25 18:21:19 +02:00
reenter : true ,
2025-04-15 23:27:50 +10:00
} ,
} ,
2024-09-09 19:59:36 +03:00
} ,
2024-12-09 16:43:58 -05:00
'Converting to named value' : {
2024-09-09 19:59:36 +03:00
invoke : {
2024-12-09 16:43:58 -05:00
src : 'Apply named value constraint' ,
id : 'astConstrainNamedValue' ,
2024-09-09 19:59:36 +03:00
input : ( { context : { selectionRanges , sketchDetails } , event } ) = > {
2024-12-09 16:43:58 -05:00
if ( event . type !== 'Constrain with named value' ) {
2024-09-09 19:59:36 +03:00
return {
selectionRanges ,
sketchDetails ,
data : undefined ,
}
}
return {
selectionRanges ,
sketchDetails ,
data : event.data ,
}
} ,
onError : 'SketchIdle' ,
onDone : {
target : 'SketchIdle' ,
2024-11-16 16:49:44 -05:00
actions : 'Set selection' ,
2024-09-09 19:59:36 +03:00
} ,
} ,
} ,
'Await constrain remove constraints' : {
invoke : {
src : 'do-constrain-remove-constraint' ,
id : 'do-constrain-remove-constraint' ,
input : ( { context : { selectionRanges , sketchDetails } , event } ) = > {
return {
selectionRanges ,
sketchDetails ,
data :
event . type === 'Constrain remove constraints'
? event . data
: undefined ,
}
} ,
onDone : {
target : 'SketchIdle' ,
actions : 'Set selection' ,
} ,
} ,
} ,
'Await constrain horizontally' : {
invoke : {
src : 'do-constrain-horizontally' ,
id : 'do-constrain-horizontally' ,
input : ( { context : { selectionRanges , sketchDetails } } ) = > ( {
selectionRanges ,
sketchDetails ,
} ) ,
onDone : {
target : 'SketchIdle' ,
actions : 'Set selection' ,
} ,
} ,
} ,
'Await constrain vertically' : {
invoke : {
src : 'do-constrain-vertically' ,
id : 'do-constrain-vertically' ,
input : ( { context : { selectionRanges , sketchDetails } } ) = > ( {
selectionRanges ,
sketchDetails ,
} ) ,
onDone : {
target : 'SketchIdle' ,
actions : 'Set selection' ,
} ,
} ,
} ,
'Await constrain horizontally align' : {
invoke : {
src : 'do-constrain-horizontally-align' ,
id : 'do-constrain-horizontally-align' ,
input : ( { context } ) = > ( {
selectionRanges : context.selectionRanges ,
sketchDetails : context.sketchDetails ,
} ) ,
onDone : {
target : 'SketchIdle' ,
actions : 'Set selection' ,
} ,
} ,
} ,
'Await constrain vertically align' : {
invoke : {
src : 'do-constrain-vertically-align' ,
id : 'do-constrain-vertically-align' ,
input : ( { context } ) = > ( {
selectionRanges : context.selectionRanges ,
sketchDetails : context.sketchDetails ,
} ) ,
onDone : {
target : 'SketchIdle' ,
actions : 'Set selection' ,
} ,
} ,
} ,
'Await constrain snap to X' : {
invoke : {
src : 'do-constrain-snap-to-x' ,
id : 'do-constrain-snap-to-x' ,
input : ( { context } ) = > ( {
selectionRanges : context.selectionRanges ,
sketchDetails : context.sketchDetails ,
} ) ,
onDone : {
target : 'SketchIdle' ,
actions : 'Set selection' ,
} ,
} ,
} ,
'Await constrain snap to Y' : {
invoke : {
src : 'do-constrain-snap-to-y' ,
id : 'do-constrain-snap-to-y' ,
input : ( { context } ) = > ( {
selectionRanges : context.selectionRanges ,
sketchDetails : context.sketchDetails ,
} ) ,
onDone : {
target : 'SketchIdle' ,
actions : 'Set selection' ,
} ,
} ,
} ,
'Await constrain equal length' : {
invoke : {
src : 'do-constrain-equal-length' ,
id : 'do-constrain-equal-length' ,
input : ( { context } ) = > ( {
selectionRanges : context.selectionRanges ,
sketchDetails : context.sketchDetails ,
} ) ,
onDone : {
target : 'SketchIdle' ,
actions : 'Set selection' ,
} ,
} ,
} ,
'Await constrain parallel' : {
invoke : {
src : 'do-constrain-parallel' ,
id : 'do-constrain-parallel' ,
input : ( { context } ) = > ( {
selectionRanges : context.selectionRanges ,
sketchDetails : context.sketchDetails ,
} ) ,
onDone : {
target : 'SketchIdle' ,
actions : 'Set selection' ,
} ,
} ,
} ,
2025-02-15 00:57:04 +11:00
'Change Tool ifs' : {
2024-09-09 19:59:36 +03:00
always : [
{
target : 'SketchIdle' ,
guard : 'next is none' ,
} ,
{
target : 'Line tool' ,
guard : 'next is line' ,
} ,
{
target : 'Rectangle tool' ,
guard : 'next is rectangle' ,
} ,
{
target : 'Tangential arc to' ,
guard : 'next is tangential arc' ,
} ,
2024-09-23 22:42:51 +10:00
{
target : 'Circle tool' ,
guard : 'next is circle' ,
} ,
2024-11-18 10:04:09 -05:00
{
target : 'Center Rectangle tool' ,
guard : 'next is center rectangle' ,
} ,
2024-12-20 14:30:37 -05:00
{
2025-02-15 00:57:04 +11:00
target : 'Circle three point tool' ,
guard : 'next is circle three point neo' ,
2025-01-06 14:08:18 -05:00
reenter : true ,
2024-12-20 14:30:37 -05:00
} ,
2025-03-18 11:14:12 +11:00
{
target : 'Arc tool' ,
guard : 'next is arc' ,
reenter : true ,
} ,
{
target : 'Arc three point tool' ,
guard : 'next is arc three point' ,
reenter : true ,
} ,
2024-09-09 19:59:36 +03:00
] ,
2024-12-16 10:34:11 -05:00
} ,
2025-02-15 00:57:04 +11:00
2024-09-23 22:42:51 +10:00
'Circle tool' : {
on : {
2025-02-15 00:57:04 +11:00
'change tool' : {
target : 'Change Tool' ,
reenter : true ,
} ,
2024-09-23 22:42:51 +10:00
} ,
states : {
'Awaiting origin' : {
2025-03-18 11:14:12 +11:00
entry : 'listen for circle origin' ,
2024-09-23 22:42:51 +10:00
on : {
'Add circle origin' : {
2025-02-15 00:57:04 +11:00
target : 'adding draft circle' ,
reenter : true ,
2024-09-23 22:42:51 +10:00
} ,
} ,
} ,
'Awaiting Radius' : {
on : {
2025-02-15 00:57:04 +11:00
'Finish circle' : {
target : 'Finished Circle' ,
actions : 'reset deleteIndex' ,
} ,
2024-09-23 22:42:51 +10:00
} ,
} ,
'Finished Circle' : {
2025-02-15 00:57:04 +11:00
invoke : {
src : 'setup-client-side-sketch-segments' ,
id : 'setup-client-side-sketch-segments4' ,
onDone : 'Awaiting origin' ,
input : ( { context : { sketchDetails , selectionRanges } } ) = > ( {
sketchDetails ,
selectionRanges ,
} ) ,
} ,
} ,
'adding draft circle' : {
invoke : {
src : 'set-up-draft-circle' ,
id : 'set-up-draft-circle' ,
onDone : {
target : 'Awaiting Radius' ,
actions : 'update sketchDetails' ,
} ,
onError : 'Awaiting origin' ,
input : ( { context : { sketchDetails } , event } ) = > {
if ( event . type !== 'Add circle origin' )
return {
sketchDetails ,
data : [ 0 , 0 ] ,
}
return {
sketchDetails ,
data : event.data ,
}
} ,
} ,
2024-09-23 22:42:51 +10:00
} ,
} ,
initial : 'Awaiting origin' ,
} ,
2025-02-15 00:57:04 +11:00
'Change Tool' : {
states : {
'splitting sketch pipe' : {
invoke : {
src : 'split-sketch-pipe-if-needed' ,
id : 'split-sketch-pipe-if-needed' ,
onDone : {
target : 'setup sketch for tool' ,
actions : 'update sketchDetails' ,
} ,
onError : '#Modeling.Sketch.SketchIdle' ,
input : ( { context : { sketchDetails } } ) = > ( {
sketchDetails ,
} ) ,
} ,
} ,
'setup sketch for tool' : {
invoke : {
src : 'setup-client-side-sketch-segments' ,
id : 'setup-client-side-sketch-segments' ,
onDone : '#Modeling.Sketch.Change Tool ifs' ,
onError : '#Modeling.Sketch.SketchIdle' ,
input : ( { context : { sketchDetails , selectionRanges } } ) = > ( {
sketchDetails ,
selectionRanges ,
} ) ,
} ,
} ,
} ,
initial : 'splitting sketch pipe' ,
2025-04-17 10:10:27 +10:00
entry : [ 'assign tool in context' , 'reset selections' ] ,
2025-02-15 00:57:04 +11:00
} ,
'Circle three point tool' : {
states : {
'Awaiting first point' : {
on : {
'Add first point' : 'Awaiting second point' ,
} ,
entry : 'listen for circle first point' ,
} ,
'Awaiting second point' : {
on : {
'Add second point' : {
target : 'adding draft circle three point' ,
2025-03-18 11:14:12 +11:00
actions : 'remove draft entities' ,
2025-02-15 00:57:04 +11:00
} ,
} ,
entry : 'listen for circle second point' ,
} ,
'adding draft circle three point' : {
invoke : {
src : 'set-up-draft-circle-three-point' ,
id : 'set-up-draft-circle-three-point' ,
onDone : {
target : 'Awaiting third point' ,
actions : 'update sketchDetails' ,
} ,
input : ( { context : { sketchDetails } , event } ) = > {
if ( event . type !== 'Add second point' )
return {
sketchDetails ,
data : { p1 : [ 0 , 0 ] , p2 : [ 0 , 0 ] } ,
}
return {
sketchDetails ,
data : event.data ,
}
} ,
} ,
} ,
'Awaiting third point' : {
on : {
'Finish circle three point' : {
target : 'Finished circle three point' ,
actions : 'reset deleteIndex' ,
} ,
} ,
} ,
'Finished circle three point' : {
invoke : {
src : 'setup-client-side-sketch-segments' ,
id : 'setup-client-side-sketch-segments5' ,
onDone : 'Awaiting first point' ,
input : ( { context : { sketchDetails , selectionRanges } } ) = > ( {
sketchDetails ,
selectionRanges ,
} ) ,
} ,
2025-01-06 14:08:18 -05:00
} ,
} ,
2025-02-15 00:57:04 +11:00
initial : 'Awaiting first point' ,
2025-03-18 11:14:12 +11:00
exit : 'remove draft entities' ,
2025-02-15 00:57:04 +11:00
2024-12-20 14:30:37 -05:00
on : {
'change tool' : 'Change Tool' ,
} ,
} ,
2025-03-18 11:14:12 +11:00
'Arc tool' : {
states : {
'Awaiting start point' : {
on : {
'Add start point' : {
target : 'Awaiting for circle center' ,
actions : 'update sketchDetails arc' ,
} ,
} ,
entry : 'setup noPoints onClick listener' ,
exit : 'remove draft entities' ,
} ,
'Awaiting for circle center' : {
entry : [ 'listen for rectangle origin' ] ,
on : {
'click in scene' : 'Adding draft arc' ,
} ,
} ,
'Adding draft arc' : {
invoke : {
src : 'set-up-draft-arc' ,
id : 'set-up-draft-arc' ,
onDone : {
target : 'Awaiting endAngle' ,
actions : 'update sketchDetails' ,
} ,
input : ( { context : { sketchDetails } , event } ) = > {
if ( event . type !== 'click in scene' )
return {
sketchDetails ,
data : [ 0 , 0 ] ,
}
return {
sketchDetails ,
data : event.data ,
}
} ,
} ,
} ,
'Awaiting endAngle' : {
on : {
'Finish arc' : 'Finishing arc' ,
} ,
} ,
'Finishing arc' : {
invoke : {
src : 'setup-client-side-sketch-segments' ,
id : 'setup-client-side-sketch-segments8' ,
onDone : 'Awaiting start point' ,
input : ( { context : { sketchDetails , selectionRanges } } ) = > ( {
sketchDetails ,
selectionRanges ,
} ) ,
} ,
} ,
} ,
initial : 'Awaiting start point' ,
on : {
'change tool' : {
target : 'Change Tool' ,
reenter : true ,
} ,
} ,
} ,
'Arc three point tool' : {
states : {
'Awaiting start point' : {
on : {
'Add start point' : {
target : 'Awaiting for circle center' ,
actions : 'update sketchDetails arc' ,
} ,
} ,
entry : 'setup noPoints onClick listener' ,
exit : 'remove draft entities' ,
} ,
'Awaiting for circle center' : {
on : {
'click in scene' : {
target : 'Adding draft arc three point' ,
actions : 'remove draft entities' ,
} ,
} ,
entry : [ 'listen for rectangle origin' , 'add draft line' ] ,
} ,
'Adding draft arc three point' : {
invoke : {
src : 'set-up-draft-arc-three-point' ,
id : 'set-up-draft-arc-three-point' ,
onDone : {
target : 'Awaiting third point' ,
actions : 'update sketchDetails' ,
} ,
input : ( { context : { sketchDetails } , event } ) = > {
if ( event . type !== 'click in scene' )
return {
sketchDetails ,
data : [ 0 , 0 ] ,
}
return {
sketchDetails ,
data : event.data ,
}
} ,
} ,
} ,
'Awaiting third point' : {
on : {
'Finish arc' : {
target : 'Finishing arc' ,
actions : 'reset deleteIndex' ,
} ,
'Close sketch' : 'Finish profile' ,
} ,
} ,
'Finishing arc' : {
invoke : {
src : 'setup-client-side-sketch-segments' ,
id : 'setup-client-side-sketch-segments9' ,
onDone : {
target : 'Awaiting for circle center' ,
reenter : true ,
} ,
input : ( { context : { sketchDetails , selectionRanges } } ) = > ( {
sketchDetails ,
selectionRanges ,
} ) ,
} ,
} ,
'Finish profile' : {
invoke : {
src : 'setup-client-side-sketch-segments' ,
id : 'setup-client-side-sketch-segments10' ,
onDone : 'Awaiting start point' ,
input : ( { context : { sketchDetails , selectionRanges } } ) = > ( {
sketchDetails ,
selectionRanges ,
} ) ,
} ,
} ,
} ,
initial : 'Awaiting start point' ,
on : {
'change tool' : 'Change Tool' ,
} ,
exit : 'remove draft entities' ,
} ,
2024-09-09 19:59:36 +03:00
} ,
initial : 'Init' ,
on : {
2025-05-08 11:50:25 +02:00
Cancel : '.undo startSketchOn' ,
2024-09-09 19:59:36 +03:00
CancelSketch : '.SketchIdle' ,
'Delete segment' : {
reenter : false ,
2025-01-31 16:13:43 -05:00
actions : [ 'Delete segment' , 'Set sketchDetails' , 'reset selections' ] ,
2024-09-09 19:59:36 +03:00
} ,
'code edit during sketch' : '.clean slate' ,
2025-05-05 22:13:13 +10:00
'Constrain with named value' : {
target : '.Converting to named value' ,
guard : 'Can convert to named value' ,
} ,
2024-09-09 19:59:36 +03:00
} ,
exit : [
'sketch exit execute' ,
'tear down client sketch' ,
'remove sketch grid' ,
'engineToClient cam sync direction' ,
'Reset Segment Overlays' ,
'enable copilot' ,
] ,
2024-11-22 11:05:04 -05:00
entry : [ 'add axis n grid' , 'clientToEngine cam sync direction' ] ,
2024-09-09 19:59:36 +03:00
} ,
'Sketch no face' : {
entry : [
'disable copilot' ,
'show default planes' ,
'set selection filter to faces only' ,
'enter sketching mode' ,
] ,
exit : [ 'hide default planes' , 'set selection filter to defaults' ] ,
on : {
2025-05-07 08:25:12 +10:00
'Select sketch plane' : {
2024-09-09 19:59:36 +03:00
target : 'animating to plane' ,
actions : [ 'reset sketch metadata' ] ,
} ,
} ,
} ,
'animating to plane' : {
invoke : {
src : 'animate-to-face' ,
id : 'animate-to-face' ,
2024-09-26 18:25:05 +10:00
2024-09-09 19:59:36 +03:00
input : ( { event } ) = > {
2025-05-07 08:25:12 +10:00
if ( event . type !== 'Select sketch plane' ) return undefined
2024-09-09 19:59:36 +03:00
return event . data
} ,
2024-09-26 18:25:05 +10:00
2024-09-09 19:59:36 +03:00
onDone : {
target : 'Sketch' ,
actions : 'set new sketch metadata' ,
} ,
2024-09-26 18:25:05 +10:00
onError : 'Sketch no face' ,
2024-09-09 19:59:36 +03:00
} ,
} ,
'animating to existing sketch' : {
invoke : {
src : 'animate-to-sketch' ,
id : 'animate-to-sketch' ,
2025-02-15 00:57:04 +11:00
2024-09-09 19:59:36 +03:00
input : ( { context } ) = > ( {
selectionRanges : context.selectionRanges ,
sketchDetails : context.sketchDetails ,
} ) ,
2025-02-15 00:57:04 +11:00
2024-09-09 19:59:36 +03:00
onDone : {
target : 'Sketch' ,
actions : [
'disable copilot' ,
'set new sketch metadata' ,
'enter sketching mode' ,
] ,
} ,
2025-02-15 00:57:04 +11:00
onError : 'idle' ,
2024-09-09 19:59:36 +03:00
} ,
} ,
2024-11-26 11:36:14 -05:00
2025-01-31 13:34:38 -05:00
'Applying extrude' : {
invoke : {
src : 'extrudeAstMod' ,
id : 'extrudeAstMod' ,
input : ( { event } ) = > {
if ( event . type !== 'Extrude' ) return undefined
return event . data
} ,
onDone : [ 'idle' ] ,
onError : {
target : 'idle' ,
actions : 'toastError' ,
} ,
} ,
} ,
2025-03-31 18:13:34 -04:00
'Applying revolve' : {
invoke : {
src : 'revolveAstMod' ,
id : 'revolveAstMod' ,
input : ( { event } ) = > {
if ( event . type !== 'Revolve' ) return undefined
return event . data
} ,
onDone : [ 'idle' ] ,
onError : {
target : 'idle' ,
actions : 'toastError' ,
} ,
} ,
} ,
2024-11-26 11:36:14 -05:00
'Applying offset plane' : {
invoke : {
src : 'offsetPlaneAstMod' ,
id : 'offsetPlaneAstMod' ,
input : ( { event } ) = > {
if ( event . type !== 'Offset plane' ) return undefined
return event . data
} ,
onDone : [ 'idle' ] ,
onError : [ 'idle' ] ,
} ,
} ,
2024-12-04 17:24:16 -05:00
2025-02-07 10:16:36 -05:00
'Applying helix' : {
invoke : {
src : 'helixAstMod' ,
id : 'helixAstMod' ,
input : ( { event } ) = > {
if ( event . type !== 'Helix' ) return undefined
return event . data
} ,
onDone : [ 'idle' ] ,
onError : [ 'idle' ] ,
} ,
} ,
2025-01-11 08:20:49 -05:00
'Applying sweep' : {
invoke : {
src : 'sweepAstMod' ,
id : 'sweepAstMod' ,
input : ( { event } ) = > {
if ( event . type !== 'Sweep' ) return undefined
return event . data
} ,
onDone : [ 'idle' ] ,
onError : [ 'idle' ] ,
} ,
} ,
2024-12-04 17:24:16 -05:00
'Applying loft' : {
invoke : {
src : 'loftAstMod' ,
id : 'loftAstMod' ,
input : ( { event } ) = > {
if ( event . type !== 'Loft' ) return undefined
return event . data
} ,
onDone : [ 'idle' ] ,
onError : [ 'idle' ] ,
} ,
} ,
2024-12-09 15:20:48 -05:00
'Applying shell' : {
invoke : {
src : 'shellAstMod' ,
id : 'shellAstMod' ,
input : ( { event } ) = > {
if ( event . type !== 'Shell' ) return undefined
return event . data
} ,
onDone : [ 'idle' ] ,
onError : [ 'idle' ] ,
} ,
} ,
2024-12-20 13:39:06 +11:00
2025-01-08 16:05:24 +01:00
'Applying fillet' : {
invoke : {
src : 'filletAstMod' ,
id : 'filletAstMod' ,
input : ( { event } ) = > {
if ( event . type !== 'Fillet' ) return undefined
return event . data
} ,
onDone : [ 'idle' ] ,
onError : [ 'idle' ] ,
} ,
} ,
2025-01-14 18:08:32 +01:00
'Applying chamfer' : {
invoke : {
src : 'chamferAstMod' ,
id : 'chamferAstMod' ,
input : ( { event } ) = > {
if ( event . type !== 'Chamfer' ) return undefined
return event . data
} ,
onDone : [ 'idle' ] ,
onError : [ 'idle' ] ,
} ,
} ,
2025-03-19 11:58:53 -04:00
parameter : {
type : 'parallel' ,
2025-04-26 14:00:28 +10:00
2025-03-19 11:58:53 -04:00
states : {
creating : {
invoke : {
src : 'actor.parameter.create' ,
id : 'actor.parameter.create' ,
input : ( { event } ) = > {
if ( event . type !== 'event.parameter.create' ) return undefined
return event . data
} ,
onDone : [ '#Modeling.idle' ] ,
onError : [ '#Modeling.idle' ] ,
} ,
} ,
2025-03-20 16:41:09 -04:00
editing : {
invoke : {
src : 'actor.parameter.edit' ,
id : 'actor.parameter.edit' ,
input : ( { event } ) = > {
if ( event . type !== 'event.parameter.edit' ) return undefined
return event . data
} ,
onDone : [ '#Modeling.idle' ] ,
onError : [ '#Modeling.idle' ] ,
} ,
} ,
2025-03-19 11:58:53 -04:00
} ,
} ,
2024-12-20 13:39:06 +11:00
'Applying Prompt-to-edit' : {
invoke : {
src : 'submit-prompt-edit' ,
id : 'submit-prompt-edit' ,
input : ( { event } ) = > {
if ( event . type !== 'Prompt-to-edit' || ! event . data ) {
return {
prompt : '' ,
selection : { graphSelections : [ ] , otherSelections : [ ] } ,
}
}
return event . data
} ,
onDone : 'idle' ,
onError : 'idle' ,
} ,
} ,
2025-02-19 13:43:27 -05:00
'Applying Delete selection' : {
invoke : {
src : 'deleteSelectionAstMod' ,
id : 'deleteSelectionAstMod' ,
input : ( { event , context } ) = > {
return { selectionRanges : context.selectionRanges }
} ,
onDone : 'idle' ,
onError : {
target : 'idle' ,
reenter : true ,
actions : ( { event } ) = > {
if ( 'error' in event && err ( event . error ) ) {
toast . error ( event . error . message )
}
} ,
} ,
} ,
} ,
2025-02-22 08:09:54 -05:00
'Applying appearance' : {
invoke : {
src : 'appearanceAstMod' ,
id : 'appearanceAstMod' ,
input : ( { event } ) = > {
if ( event . type !== 'Appearance' ) return undefined
return event . data
} ,
onDone : [ 'idle' ] ,
onError : [ 'idle' ] ,
} ,
} ,
2025-04-17 11:44:31 -04:00
'Applying translate' : {
invoke : {
src : 'translateAstMod' ,
id : 'translateAstMod' ,
input : ( { event } ) = > {
if ( event . type !== 'Translate' ) return undefined
return event . data
} ,
onDone : [ 'idle' ] ,
onError : [ 'idle' ] ,
} ,
} ,
'Applying rotate' : {
invoke : {
src : 'rotateAstMod' ,
id : 'rotateAstMod' ,
input : ( { event } ) = > {
if ( event . type !== 'Rotate' ) return undefined
return event . data
} ,
onDone : [ 'idle' ] ,
onError : [ 'idle' ] ,
} ,
} ,
2025-03-18 20:25:51 -07:00
2025-04-26 18:26:39 -04:00
'Applying clone' : {
invoke : {
src : 'cloneAstMod' ,
id : 'cloneAstMod' ,
input : ( { event } ) = > {
if ( event . type !== 'Clone' ) return undefined
return event . data
} ,
onDone : [ 'idle' ] ,
onError : {
target : 'idle' ,
actions : 'toastError' ,
} ,
} ,
} ,
2025-03-18 20:25:51 -07:00
Exporting : {
invoke : {
src : 'exportFromEngine' ,
id : 'exportFromEngine' ,
input : ( { event } ) = > {
if ( event . type !== 'Export' ) return undefined
return event . data
} ,
onDone : [ 'idle' ] ,
onError : [ 'idle' ] ,
} ,
} ,
Making : {
invoke : {
src : 'makeFromEngine' ,
id : 'makeFromEngine' ,
input : ( { event , context } ) = > {
if ( event . type !== 'Make' || ! context . machineManager ) return undefined
return {
machineManager : context.machineManager ,
. . . event . data ,
}
} ,
onDone : [ 'idle' ] ,
onError : [ 'idle' ] ,
} ,
} ,
2025-03-28 14:56:48 +11:00
'Boolean subtracting' : {
invoke : {
src : 'boolSubtractAstMod' ,
id : 'boolSubtractAstMod' ,
input : ( { event } ) = >
event . type !== 'Boolean Subtract' ? undefined : event . data ,
onDone : 'idle' ,
onError : 'idle' ,
} ,
} ,
2025-04-25 18:21:19 +02:00
2025-03-28 14:56:48 +11:00
'Boolean uniting' : {
invoke : {
src : 'boolUnionAstMod' ,
id : 'boolUnionAstMod' ,
input : ( { event } ) = >
event . type !== 'Boolean Union' ? undefined : event . data ,
onDone : 'idle' ,
onError : 'idle' ,
} ,
} ,
2025-04-25 18:21:19 +02:00
2025-03-28 14:56:48 +11:00
'Boolean intersecting' : {
invoke : {
src : 'boolIntersectAstMod' ,
id : 'boolIntersectAstMod' ,
input : ( { event } ) = >
event . type !== 'Boolean Intersect' ? undefined : event . data ,
onDone : 'idle' ,
onError : 'idle' ,
} ,
} ,
2024-09-09 19:59:36 +03:00
} ,
initial : 'idle' ,
on : {
Cancel : {
target : '.idle' ,
// TODO what if we're existing extrude equipped, should these actions still be fired?
// maybe cancel needs to have a guard for if else logic?
actions : [
'reset sketch metadata' ,
'enable copilot' ,
'enter modeling mode' ,
] ,
} ,
'Set selection' : {
reenter : false ,
actions : 'Set selection' ,
} ,
'Set mouse state' : {
reenter : false ,
actions : 'Set mouse state' ,
} ,
'Set context' : {
reenter : false ,
actions : 'Set context' ,
} ,
'Set Segment Overlays' : {
reenter : false ,
actions : 'Set Segment Overlays' ,
} ,
2024-10-04 13:47:44 -07:00
'Center camera on selection' : {
reenter : false ,
actions : 'Center camera on selection' ,
} ,
2025-04-25 18:21:19 +02:00
'Toggle default plane visibility' : {
reenter : false ,
actions : 'Toggle default plane visibility' ,
} ,
2024-09-09 19:59:36 +03:00
} ,
} )
2024-07-05 13:40:16 +10:00
export function isEditingExistingSketch ( {
sketchDetails ,
} : {
sketchDetails : SketchDetails | null
} ) : boolean {
// should check that the variable declaration is a pipeExpression
2025-04-25 16:01:35 -05:00
// and that the pipeExpression contains a "startProfile" callExpression
2025-02-15 00:57:04 +11:00
if ( ! sketchDetails ? . sketchEntryNodePath ) return false
2024-07-05 13:40:16 +10:00
const variableDeclaration = getNodeFromPath < VariableDeclarator > (
kclManager . ast ,
2025-02-15 00:57:04 +11:00
sketchDetails . sketchEntryNodePath ,
2025-03-20 08:30:11 +11:00
'VariableDeclarator' ,
false ,
true // suppress noise because we know sketchEntryNodePath might not match up to the ast if the user changed the code
// and is dealt with in `re-eval nodePaths`
2024-07-05 13:40:16 +10:00
)
2025-02-15 00:57:04 +11:00
if ( variableDeclaration instanceof Error ) return false
2024-07-05 13:40:16 +10:00
if ( variableDeclaration . node . type !== 'VariableDeclarator' ) return false
2025-02-15 00:57:04 +11:00
const maybePipeExpression = variableDeclaration . node . init
if (
2025-05-02 16:08:12 -05:00
maybePipeExpression . type === 'CallExpressionKw' &&
2025-04-25 16:01:35 -05:00
( maybePipeExpression . callee . name . name === 'startProfile' ||
2025-03-24 20:58:55 +13:00
maybePipeExpression . callee . name . name === 'circle' ||
maybePipeExpression . callee . name . name === 'circleThreePoint' )
2024-07-05 13:40:16 +10:00
)
2025-02-15 00:57:04 +11:00
return true
if ( maybePipeExpression . type !== 'PipeExpression' ) return false
const hasStartProfileAt = maybePipeExpression . body . some (
2025-01-16 11:10:36 -05:00
( item ) = >
2025-04-25 16:01:35 -05:00
item . type === 'CallExpressionKw' &&
item . callee . name . name === 'startProfile'
2025-01-16 11:10:36 -05:00
)
2025-02-15 00:57:04 +11:00
const hasCircle =
maybePipeExpression . body . some (
2025-02-28 17:40:01 -08:00
( item ) = >
2025-03-24 20:58:55 +13:00
item . type === 'CallExpressionKw' && item . callee . name . name === 'circle'
2025-02-15 00:57:04 +11:00
) ||
maybePipeExpression . body . some (
( item ) = >
item . type === 'CallExpressionKw' &&
2025-03-24 20:58:55 +13:00
item . callee . name . name === 'circleThreePoint'
2025-02-15 00:57:04 +11:00
)
return ( hasStartProfileAt && maybePipeExpression . body . length > 1 ) || hasCircle
2025-01-16 11:10:36 -05:00
}
2025-04-25 18:21:19 +02:00
2024-09-23 22:42:51 +10:00
export function pipeHasCircle ( {
sketchDetails ,
} : {
sketchDetails : SketchDetails | null
} ) : boolean {
2025-02-15 00:57:04 +11:00
if ( ! sketchDetails ? . sketchEntryNodePath ) return false
2024-09-23 22:42:51 +10:00
const variableDeclaration = getNodeFromPath < VariableDeclarator > (
kclManager . ast ,
2025-02-15 00:57:04 +11:00
sketchDetails . sketchEntryNodePath ,
2024-09-23 22:42:51 +10:00
'VariableDeclarator'
)
if ( err ( variableDeclaration ) ) return false
if ( variableDeclaration . node . type !== 'VariableDeclarator' ) return false
const pipeExpression = variableDeclaration . node . init
if ( pipeExpression . type !== 'PipeExpression' ) return false
const hasCircle = pipeExpression . body . some (
2025-03-24 20:58:55 +13:00
( item ) = >
item . type === 'CallExpressionKw' && item . callee . name . name === 'circle'
2024-09-23 22:42:51 +10:00
)
return hasCircle
2024-07-05 13:40:16 +10:00
}