854 lines
30 KiB
TypeScript
854 lines
30 KiB
TypeScript
![]() |
import { PathToNode } from 'lang/wasm'
|
||
|
import { engineCommandManager } from 'lang/std/engineConnection'
|
||
|
import { isReducedMotion } from 'lang/util'
|
||
|
import { Axis, Selection, SelectionRangeTypeMap, Selections } from 'useStore'
|
||
|
import { assign, createMachine } from 'xstate'
|
||
|
import { v4 as uuidv4 } from 'uuid'
|
||
|
import { isCursorInSketchCommandRange } from 'hooks/useAppMode'
|
||
|
import {
|
||
|
doesPipeHaveCallExp,
|
||
|
getNodePathFromSourceRange,
|
||
|
hasExtrudeSketchGroup,
|
||
|
} from 'lang/queryAst'
|
||
|
import { kclManager } from 'lang/KclSinglton'
|
||
|
import {
|
||
|
horzVertInfo,
|
||
|
applyConstraintHorzVert,
|
||
|
} from 'components/Toolbar/HorzVert'
|
||
|
import {
|
||
|
applyConstraintHorzVertAlign,
|
||
|
horzVertDistanceInfo,
|
||
|
} from 'components/Toolbar/SetHorzVertDistance'
|
||
|
import { angleBetweenInfo } from 'components/Toolbar/SetAngleBetween'
|
||
|
import { setAngleLengthInfo } from 'components/Toolbar/setAngleLength'
|
||
|
import {
|
||
|
applyConstraintEqualLength,
|
||
|
setEqualLengthInfo,
|
||
|
} from 'components/Toolbar/EqualLength'
|
||
|
import { extrudeSketch } from 'lang/modifyAst'
|
||
|
import { getNodeFromPath } from '../lang/queryAst'
|
||
|
import { CallExpression, PipeExpression } from '../lang/wasm'
|
||
|
|
||
|
export const MODELING_PERSIST_KEY = 'MODELING_PERSIST_KEY'
|
||
|
|
||
|
export type SetSelections =
|
||
|
| {
|
||
|
selectionType: 'singleCodeCursor'
|
||
|
selection?: Selection
|
||
|
}
|
||
|
| {
|
||
|
selectionType: 'otherSelection'
|
||
|
selection: Axis
|
||
|
}
|
||
|
| {
|
||
|
selectionType: 'completeSelection'
|
||
|
selection: Selections
|
||
|
}
|
||
|
| {
|
||
|
selectionType: 'mirrorCodeMirrorSelections'
|
||
|
selection: Selections
|
||
|
}
|
||
|
|
||
|
export const modelingMachine = createMachine(
|
||
|
{
|
||
|
/** @xstate-layout N4IgpgJg5mDOIC5QFkD2EwBsCWA7KAxAMICGuAxlgNoAMAuoqAA6qzYAu2qujIAHogDMAVgDsAOmEiAjLMGCAHIIAsAJlHKANCACeiaQDZBEsctGrVATmmiaNQQF8H2tBhz5x2CJjAEAymDsAASwWGDknNy0DEggLGyRPLECCMrCCuL20qrCRpYagvnaegjZBtLiBqLploIGCtZVyk4u6Fh4UJ7evgAicGERQSx47NG88RxcSaApdcKSNKIKi8ppwsrLwsX60oriqgaWBgbKImpWqi0gru0eXj4EfaE+g5AwY7ETibyzx5lLu1sygM61ERV0+ho0mU4gURlOwihlis2SuN3cnXuvX6L2CJD42FgH2YrEm3B+QnM4mqdhoyPUILqBm2CAaFTS5houQUCMczmubQxXQeAVxQ1QI2JcVJ32SiGUXPEhjBtWklgaokMzIhqVUNHEG0WgjVqkEUN2aMFHWFvlF4WCbzAUq+UwpqRoGQ2pwacPW4JKJxhgYU0kWu3Wymklrc1qx-gGeIJRPo4xlrrlqUEqkqdMKOSRNEOLPWGTVhkswlU0O5fNaMbu3XjYoAZiRKM60+SMwqMgY7Go6mbalCWaIjLCjtJ0n36tUFNHbpjGwBRXDsMAAJxCAGtAuQABYdhLpmY7SPiSzAyOXwGFUQs01BtXVEEV9VSZr89Gxldrzc7vdD2kGISWPLtTwQepBGpEN8lDGhVBDLYdTUfVZzvYFQ3MS4vytBsHieBMglbdsU0+Ttpn4Sk0KzBR1EKdING1EozQqYxajyNQ6MOBchTjO1BhITBMCPMlKJSSNoIsEEllQrlhGQko9RhZEDFUL1vWEUMcLrRcbUeHF7SCISRLI0CxLdRR2ROJQwQQ2RmMQENJD1OxjR5aE+1rAV6yXB4wD4dgNwAVwwIIRjANdRNlCDlGRJU6kfapK0vdYWSnBQJEsfJMtODYrM-XS+MbAKgtCsBwr-KLgNTMDxPlawJ0DKtXNkLl0o2NjNULPMPQ2HSfL0vxd3YA9iDIShMGGwDopPKjSg0fUaDURFFoVbJ7x1S9oOSmQcl2CtRF461ptG-dxFOg8AElGwE4JhiiszpTqt1pB9C8NA8plynVFlyn1EMzVfdTrAU46PEu87IZukUiNCKAAFtItGJ6XXA+a3vVD6vV2Y41IQlkzAMakLCnD0K0jK9wc6SGLpG67G0IsUHpRkDnosjM3oUi91SsRYjmUywWRUDJRFEY0PXzdQrGpunALls6YexZ4jPhpHHrZtH6tKHkJHkLJ1AUGpsofQxxBEWpxayCXFFl2noZXABHYLsCYIJ2FQVBTM1ijLK5Njp20wtPMEUc+yVRC4vgpZlqjXDfIVg9E-3JWCGXZ3XaCBHUAANwqj2vdm9HZg9NjpPUZbIykFkKYNTD1nsGycjt+modb1OAmCFWIimIvtbVMwczF4wtXqRyEDHfVsiNwtahyTU46Kk7W+T1PkBIXcQjARHkaCPON04cghL716kKVdYjErEWFEy9LoXZKsQT7I3jWEFv5Ydh5183tXd-3VANzYAAF7cHYMfVGvtOZggyOkewqkjBqQrOlOBlQqjyCrEoacR145DRXp-XwRBuCwCCiQPAQR-6AJAWuISQQICEjARQJ0ECXpQOqJULm+QVArFvjqcsMIDg2UwRsdIhVBpCntu3RshDcDEI3KQ3Ae9NyHxoXQ4hE0mE+xYRBGwlh9QpQOp5awdR0piAqOLLS5g0HmlEd+CGeDJEPGkbI+Rxl8A+BPpzOEKk+wOT6nYHk6VNgGkOLkUJOQ1jvzOqvKRRCSFkJ8Pgdgh5mEc20UbDIN4GjHAKopfQjQLzLAVCIrkdE344PEfYwCqcnFxIURQ4BoCTI6GMjgKAuAPHaKWMTJQCFzBZIOEbdK6kYSRg0EsRCdhLyWEiUnfBxBYlyLIfvZRwlmlCWwG0jpGNljQQVMCKohZzBiHSiIHasg1DIhEFpOEMy25VJiTI2pQQwDOxoQkqASStkpDejYA0voqg5HKFOLQvC0gwK0sOPs1g3K3PEAAGTwBVAAKp7TAacM5u2znnd2qKvlCEUDA8ZCk1TpE8qOJQSouQ9SnMIfIGhYUItwMi1F4gAAKEo1xBAAIIQAwBAAgPKIDiklCkmK80zTAnNg0dIUg1JqFWMWKESoThQkyn6I2UgGWIqCCir2F1t7q2CIKyAAreXCo1rVVJ4rEIVEOJlLyZpCz1EJqaA0Bx1JPx+dJLVTKdUsoCDvTlxr+WEIRkwHw64gjuA0ZasVsxgQSBnsPMFyJcmpBvkqXKozlpWDij65leqA2Gu5byk16cXZuyZQAdxxYXUVc1ZgV32G9MeCEjhHGFm9JU2Uq5yGlTYvCNMV6MoLZgcQV1cAcAIHihA6k1T-CuZqXIVc-r2EyPUMQmokJ2AMPmv1eqJ1TqoDVciWj5qmmypkS5cClAghBSUSsxNCglJSosRYu7ynL3liO-dY6AByqAgjspGLAU1QqWYzoZJYC8G0wSvuRMWdSSoim6N0a-TKsKuVVtIcEepVCwGYFofQ9RlVmyoAIBAbgYBPC4BzqgXc4gYDsAALR4caZgZjeAyOQaWPMRCyJNTGmBccdKS7YRmmMG5fs0zP12PllhnD5CAENOoYR1RDDKCkfI5uDcADxDhpIOwMjG4EaMcCKx5T+GhKcdwNx+txdECG3mIdaWjQjaXnSlUDIqroTmHDOsTD2GOCKIPtgI+aniOMK0xRqjNG6MMaY8x5ZYXrNcdQDx3RkhzDytonCYwJjGqIUyvIWwiJnyBcU8l8LRG1FRbSwQHTemDNGYAaZxLVXUu2fS-Z7W6hMtiHUupXLRhNolGBTtG+4tjBwjpLUCrwWyBQB8NFyjTK4v0eo4lxbPgbN2c0ValIfXSynGylBKcVZAmVgvB6ZayxR4enm3iNxFV6uNY3PpzAhnjNtfM9tsAu3uv7bjY5lUkhgTqnbR6qcgTdhXtkDYeHxojiPajZFD5+4VuxbwPFzb5n3lJIBxl4m6hKwKQ0Ju1Q6V1QVGWh6E4Fh8wbBR-jjHr2Ny6fe8177ZmWMs8Jz1t0fXibbSrPkBUsc01qj2Cqxu8gn5KFuUEXAgGSK2iIhgVswVMD3U+0ymd2Q4qSArG9a8PJgShxQlOGDUI6hG2nuYbytjOgkCC5wfAW9cRTHjF3BMvcBec2DtSXRY4eTHEjND3hy7qR9g2Oqe1l55yyc6AecI24Oge-tF7-Xoz9jLo5AcXpJi1J-NWOaOosdZYp-IGn933dEjTpPeZYHpRKaVGNBXDQ6pwcdSkOwlQtLr3LBwvyZXGB4CxCd7GhtQg6SSBkHIRQKh1D3sQMx5zyxzRSCkLog4i8xE-h8FPhzqRi-b8OYcTU8ILdjcHBHXRdI3ohlOI7wdycj-90ONmHNq09lQnUCyZydzRQTqLSY4KcWFfBd-V6S5fYJkG3eQQoKoGuPUXPKQBAkMB3AdBOWmH9XVTAKAjMaSEwS5WlaoLMCwFfWdM0SoHtK+RCDBGWJPZOeFbVPA8QHoKjAgiCYwag3IcWG1VVBkRDaCZ9f6WlKwBXJgnA1gllYDINUtCALg89NDc2ThXKBAk4UbRASsIMFDG+EEdYCwPdNgotXeYNJQxtcOODCmfZIcRDXQrkOPAw84YwllQ9dgCwxzV+I3U0UgqQBnSg65fYC9FQFQU4OkD9JeOTKJXAllADIDDldgcfJvafWdE4YmU4H6U0aeOEewyoFDOkLIRiWFNAbFPAzwhAErLKQFMwBNOoaQGuagy8coVaeeKoGTKIodeTV3JTShdjGrDTF7LrCoi9JaGwARDBAfTzfrXIIrUJKEEEFHDrCLWrTTNLEYhUHaQcKEHNbIZaExDYWA0AjiCwI4RPTo5ghTBbZ7LTDY35PUIwIcZaEQJQQJRYTIEWBEewOoCsZnNHJJW409A7RzcPD6d8dBCFXIKnEQTINtZKaoPUAaJ3ZOJXFXNsMACos46kEQVVawCsQsSnFCd4woHkCItqKsWWF3HDdPOvOaLWN0XKC8A5dzG1PgkxTKHmJYAofKJ-SvfcVPGk33dGekzmBn
|
||
|
id: 'Modeling',
|
||
|
|
||
|
tsTypes: {} as import('./modelingMachine.typegen').Typegen0,
|
||
|
predictableActionArguments: true,
|
||
|
preserveActionOrder: true,
|
||
|
|
||
|
context: {
|
||
|
guiMode: 'default',
|
||
|
selection: [] as string[],
|
||
|
selectionRanges: {
|
||
|
otherSelections: [],
|
||
|
codeBasedSelections: [],
|
||
|
} as Selections,
|
||
|
selectionRangeTypeMap: {} as SelectionRangeTypeMap,
|
||
|
sketchPathToNode: null as PathToNode | null, // maybe too specific, and we should have a generic pathToNode, but being specific seems less risky when I'm not sure
|
||
|
sketchEnginePathId: '' as string,
|
||
|
sketchPlaneId: '' as string,
|
||
|
},
|
||
|
|
||
|
schema: {
|
||
|
events: {} as
|
||
|
| { type: 'Deselect all' }
|
||
|
| { type: 'Deselect edge'; data: Selection & { type: 'edge' } }
|
||
|
| { type: 'Deselect axis'; data: Axis }
|
||
|
| {
|
||
|
type: 'Deselect segment'
|
||
|
data: Selection & { type: 'line' | 'arc' }
|
||
|
}
|
||
|
| { type: 'Deselect face'; data: Selection & { type: 'face' } }
|
||
|
| {
|
||
|
type: 'Deselect point'
|
||
|
data: Selection & { type: 'point' | 'line-end' | 'line-mid' }
|
||
|
}
|
||
|
| { type: 'Enter sketch' }
|
||
|
| { type: 'Select all'; data: Selection & { type: 'all ' } }
|
||
|
| { type: 'Select edge'; data: Selection & { type: 'edge' } }
|
||
|
| { type: 'Select axis'; data: Axis }
|
||
|
| { type: 'Select segment'; data: Selection & { type: 'line' | 'arc' } }
|
||
|
| { type: 'Select face'; data: Selection & { type: 'face' } }
|
||
|
| { type: 'Select default plane'; data: { planeId: string } }
|
||
|
| { type: 'Set selection'; data: SetSelections }
|
||
|
| {
|
||
|
type: 'Select point'
|
||
|
data: Selection & { type: 'point' | 'line-end' | 'line-mid' }
|
||
|
}
|
||
|
| { type: 'Sketch no face' }
|
||
|
| { type: 'Toggle gui mode' }
|
||
|
| { type: 'Cancel' }
|
||
|
| { type: 'CancelSketch' }
|
||
|
| {
|
||
|
type: 'Add point'
|
||
|
data: {
|
||
|
coords: { x: number; y: number }[]
|
||
|
axis: 'xy' | 'xz' | 'yz' | '-xy' | '-xz' | '-yz' | null
|
||
|
}
|
||
|
}
|
||
|
| { type: 'Equip tool' }
|
||
|
| { type: 'Equip move tool' }
|
||
|
| { type: 'Set radius' }
|
||
|
| { type: 'Complete line' }
|
||
|
| { type: 'Set distance' }
|
||
|
| { type: 'Equip new tool' }
|
||
|
| { type: 'update_code'; data: string }
|
||
|
| { type: 'Make segment horizontal' }
|
||
|
| { type: 'Make segment vertical' }
|
||
|
| { type: 'Constrain horizontal distance' }
|
||
|
| { type: 'Constrain vertical distance' }
|
||
|
| { type: 'Constrain angle' }
|
||
|
| { type: 'Constrain horizontally align' }
|
||
|
| { type: 'Constrain vertically align' }
|
||
|
| { type: 'Constrain length' }
|
||
|
| { type: 'Constrain equal length' }
|
||
|
| { type: 'extrude intent' },
|
||
|
// ,
|
||
|
},
|
||
|
|
||
|
states: {
|
||
|
idle: {
|
||
|
on: {
|
||
|
'Set selection': {
|
||
|
target: 'idle',
|
||
|
internal: true,
|
||
|
actions: 'Set selection',
|
||
|
},
|
||
|
|
||
|
'Deselect point': {
|
||
|
target: 'idle',
|
||
|
internal: true,
|
||
|
actions: [
|
||
|
'Remove from code-based selection',
|
||
|
'Update code selection cursors',
|
||
|
// 'Engine: remove highlight',
|
||
|
],
|
||
|
cond: 'Selection contains point',
|
||
|
},
|
||
|
|
||
|
'Deselect edge': {
|
||
|
target: 'idle',
|
||
|
internal: true,
|
||
|
actions: [
|
||
|
'Remove from code-based selection',
|
||
|
'Update code selection cursors',
|
||
|
// 'Engine: remove highlight',
|
||
|
],
|
||
|
cond: 'Selection contains edge',
|
||
|
},
|
||
|
|
||
|
'Deselect axis': {
|
||
|
target: 'idle',
|
||
|
internal: true,
|
||
|
actions: [
|
||
|
'Remove from other selection',
|
||
|
'Update code selection cursors',
|
||
|
// 'Engine: remove highlight',
|
||
|
],
|
||
|
cond: 'Selection contains axis',
|
||
|
},
|
||
|
|
||
|
'Select point': {
|
||
|
target: 'idle',
|
||
|
internal: true,
|
||
|
actions: [
|
||
|
'Add to code-based selection',
|
||
|
'Update code selection cursors',
|
||
|
// 'Engine: add highlight',
|
||
|
],
|
||
|
},
|
||
|
|
||
|
'Select edge': {
|
||
|
target: 'idle',
|
||
|
internal: true,
|
||
|
actions: [
|
||
|
'Add to code-based selection',
|
||
|
'Update code selection cursors',
|
||
|
// 'Engine: add highlight',
|
||
|
],
|
||
|
},
|
||
|
|
||
|
'Select axis': {
|
||
|
target: 'idle',
|
||
|
internal: true,
|
||
|
actions: [
|
||
|
'Add to other selection',
|
||
|
// 'Engine: add highlight',
|
||
|
],
|
||
|
},
|
||
|
|
||
|
'Select face': {
|
||
|
target: 'idle',
|
||
|
internal: true,
|
||
|
actions: [
|
||
|
'Add to code-based selection',
|
||
|
'Update code selection cursors',
|
||
|
// 'Engine: add highlight',
|
||
|
],
|
||
|
},
|
||
|
|
||
|
'Enter sketch': [
|
||
|
{
|
||
|
target: 'Sketch',
|
||
|
cond: 'Selection is one face',
|
||
|
actions: [
|
||
|
'set sketch metadata',
|
||
|
'sketch mode enabled',
|
||
|
'edit mode enter',
|
||
|
],
|
||
|
},
|
||
|
'Sketch no face',
|
||
|
],
|
||
|
|
||
|
'Deselect face': {
|
||
|
target: 'idle',
|
||
|
internal: true,
|
||
|
actions: [
|
||
|
'Remove from code-based selection',
|
||
|
'Update code selection cursors',
|
||
|
// 'Engine: remove highlight',
|
||
|
],
|
||
|
cond: 'Selection contains face',
|
||
|
},
|
||
|
|
||
|
'Select all': {
|
||
|
target: 'idle',
|
||
|
internal: true,
|
||
|
actions: 'Add to code-based selection',
|
||
|
},
|
||
|
|
||
|
'Deselect all': {
|
||
|
target: 'idle',
|
||
|
internal: true,
|
||
|
actions: [
|
||
|
'Clear selection',
|
||
|
'Update code selection cursors',
|
||
|
// 'Engine: remove highlight',
|
||
|
],
|
||
|
cond: 'Selection is not empty',
|
||
|
},
|
||
|
|
||
|
'extrude intent': [
|
||
|
{
|
||
|
target: 'awaiting selection',
|
||
|
cond: 'has no selection',
|
||
|
},
|
||
|
{
|
||
|
target: 'idle',
|
||
|
cond: 'has valid extrude selection',
|
||
|
internal: true,
|
||
|
actions: 'AST extrude',
|
||
|
},
|
||
|
],
|
||
|
},
|
||
|
},
|
||
|
|
||
|
Sketch: {
|
||
|
states: {
|
||
|
SketchIdle: {
|
||
|
on: {
|
||
|
'Select point': {
|
||
|
target: 'SketchIdle',
|
||
|
internal: true,
|
||
|
actions: [
|
||
|
'Update code selection cursors',
|
||
|
'Add to code-based selection',
|
||
|
],
|
||
|
},
|
||
|
|
||
|
'Select segment': {
|
||
|
target: 'SketchIdle',
|
||
|
internal: true,
|
||
|
actions: [
|
||
|
'Update code selection cursors',
|
||
|
'Add to code-based selection',
|
||
|
],
|
||
|
},
|
||
|
|
||
|
'Deselect point': {
|
||
|
target: 'SketchIdle',
|
||
|
internal: true,
|
||
|
cond: 'Selection contains point',
|
||
|
actions: [
|
||
|
'Update code selection cursors',
|
||
|
'Add to code-based selection',
|
||
|
],
|
||
|
},
|
||
|
|
||
|
'Deselect segment': {
|
||
|
target: 'SketchIdle',
|
||
|
internal: true,
|
||
|
cond: 'Selection contains line',
|
||
|
actions: [
|
||
|
'Update code selection cursors',
|
||
|
'Add to code-based selection',
|
||
|
],
|
||
|
},
|
||
|
|
||
|
'Equip tool': {
|
||
|
target: 'Line Tool',
|
||
|
actions: 'set tool line',
|
||
|
},
|
||
|
|
||
|
'Equip move tool': 'Move Tool',
|
||
|
|
||
|
'Set selection': {
|
||
|
target: 'SketchIdle',
|
||
|
internal: true,
|
||
|
actions: 'Set selection',
|
||
|
},
|
||
|
|
||
|
'Make segment vertical': {
|
||
|
cond: 'Can make selection vertical',
|
||
|
target: 'SketchIdle',
|
||
|
internal: true,
|
||
|
actions: ['Make selection vertical'],
|
||
|
},
|
||
|
|
||
|
'Make segment horizontal': {
|
||
|
target: 'SketchIdle',
|
||
|
internal: true,
|
||
|
cond: 'Can make selection horizontal',
|
||
|
actions: ['Make selection horizontal'],
|
||
|
},
|
||
|
|
||
|
'Constrain horizontal distance': {
|
||
|
target: 'Await horizontal distance info',
|
||
|
cond: 'Can constrain horizontal distance',
|
||
|
},
|
||
|
|
||
|
'Constrain vertical distance': {
|
||
|
target: 'Await vertical distance info',
|
||
|
cond: 'Can constrain vertical distance',
|
||
|
},
|
||
|
|
||
|
'Constrain angle': {
|
||
|
target: 'Await angle info',
|
||
|
cond: 'Can constrain angle',
|
||
|
},
|
||
|
|
||
|
'Constrain length': {
|
||
|
target: 'Await length info',
|
||
|
cond: 'Can constrain length',
|
||
|
},
|
||
|
|
||
|
'Constrain horizontally align': {
|
||
|
cond: 'Can constrain horizontally align',
|
||
|
target: 'SketchIdle',
|
||
|
internal: true,
|
||
|
actions: ['Constrain horizontally align'],
|
||
|
},
|
||
|
|
||
|
'Constrain vertically align': {
|
||
|
cond: 'Can constrain vertically align',
|
||
|
target: 'SketchIdle',
|
||
|
internal: true,
|
||
|
actions: ['Constrain vertically align'],
|
||
|
},
|
||
|
|
||
|
'Constrain equal length': {
|
||
|
cond: 'Can constrain equal length',
|
||
|
target: 'SketchIdle',
|
||
|
internal: true,
|
||
|
actions: ['Constrain equal length'],
|
||
|
},
|
||
|
},
|
||
|
|
||
|
entry: 'equip select',
|
||
|
},
|
||
|
|
||
|
'Line Tool': {
|
||
|
states: {
|
||
|
Done: {
|
||
|
type: 'final',
|
||
|
},
|
||
|
|
||
|
'Point Added': {
|
||
|
on: {
|
||
|
'Add point': {
|
||
|
target: 'Segment Added',
|
||
|
actions: ['AST start new sketch'],
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
|
||
|
'Segment Added': {
|
||
|
on: {
|
||
|
'Add point': {
|
||
|
target: 'Segment Added',
|
||
|
internal: true,
|
||
|
actions: ['AST add line segment'],
|
||
|
},
|
||
|
|
||
|
'Complete line': {
|
||
|
target: 'Done',
|
||
|
actions: ['Modify AST', 'Update code selection cursors'],
|
||
|
},
|
||
|
|
||
|
'Equip new tool': {
|
||
|
target: 'Segment Added',
|
||
|
internal: true,
|
||
|
actions: 'set tool',
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
|
||
|
Init: {
|
||
|
always: [
|
||
|
{
|
||
|
target: 'Segment Added',
|
||
|
cond: 'is editing existing sketch',
|
||
|
},
|
||
|
'No Points',
|
||
|
],
|
||
|
},
|
||
|
|
||
|
'No Points': {
|
||
|
on: {
|
||
|
'Add point': 'Point Added',
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
|
||
|
// invoke: [
|
||
|
// {
|
||
|
// src: 'createLine',
|
||
|
// id: 'Create line',
|
||
|
// onDone: 'SketchIdle',
|
||
|
// },
|
||
|
// ],
|
||
|
initial: 'Init',
|
||
|
|
||
|
on: {
|
||
|
'Equip move tool': 'Move Tool',
|
||
|
},
|
||
|
},
|
||
|
|
||
|
'Move Tool': {
|
||
|
entry: 'set tool move',
|
||
|
},
|
||
|
|
||
|
'Await horizontal distance info': {
|
||
|
invoke: {
|
||
|
src: 'Get horizontal info',
|
||
|
id: 'get-horizontal-info',
|
||
|
onDone: {
|
||
|
target: 'SketchIdle',
|
||
|
actions: 'Set selection',
|
||
|
},
|
||
|
onError: 'SketchIdle',
|
||
|
},
|
||
|
},
|
||
|
|
||
|
'Await vertical distance info': {
|
||
|
invoke: {
|
||
|
src: 'Get vertical info',
|
||
|
id: 'get-vertical-info',
|
||
|
onDone: {
|
||
|
target: 'SketchIdle',
|
||
|
actions: 'Set selection',
|
||
|
},
|
||
|
onError: 'SketchIdle',
|
||
|
},
|
||
|
},
|
||
|
|
||
|
'Await angle info': {
|
||
|
invoke: {
|
||
|
src: 'Get angle info',
|
||
|
id: 'get-angle-info',
|
||
|
onDone: {
|
||
|
target: 'SketchIdle',
|
||
|
actions: 'Set selection',
|
||
|
},
|
||
|
onError: 'SketchIdle',
|
||
|
},
|
||
|
},
|
||
|
|
||
|
'Await length info': {
|
||
|
invoke: {
|
||
|
src: 'Get length info',
|
||
|
id: 'get-length-info',
|
||
|
onDone: {
|
||
|
target: 'SketchIdle',
|
||
|
actions: 'Set selection',
|
||
|
},
|
||
|
onError: 'SketchIdle',
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
|
||
|
initial: 'SketchIdle',
|
||
|
|
||
|
on: {
|
||
|
CancelSketch: '.SketchIdle',
|
||
|
},
|
||
|
|
||
|
exit: 'sketch exit execute',
|
||
|
},
|
||
|
|
||
|
'Sketch no face': {
|
||
|
entry: 'show default planes',
|
||
|
|
||
|
exit: 'hide default planes',
|
||
|
on: {
|
||
|
'Select default plane': {
|
||
|
target: 'Sketch',
|
||
|
actions: [
|
||
|
'reset sketch metadata',
|
||
|
'set default plane id',
|
||
|
'sketch mode enabled',
|
||
|
'create path',
|
||
|
],
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
|
||
|
'awaiting selection': {
|
||
|
on: {
|
||
|
'Set selection': {
|
||
|
target: 'checking selection',
|
||
|
actions: 'Set selection',
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
|
||
|
'checking selection': {
|
||
|
always: [
|
||
|
{
|
||
|
target: 'idle',
|
||
|
cond: 'has valid extrude selection',
|
||
|
actions: 'AST extrude',
|
||
|
},
|
||
|
{
|
||
|
target: 'idle',
|
||
|
actions: 'toast extrude failed',
|
||
|
},
|
||
|
],
|
||
|
},
|
||
|
},
|
||
|
|
||
|
initial: 'idle',
|
||
|
|
||
|
on: {
|
||
|
Cancel: {
|
||
|
target: 'idle',
|
||
|
// TODO what if we're existing extrude equiped, should these actions still be fired?
|
||
|
// mabye cancel needs to have a guard for if else logic?
|
||
|
actions: [
|
||
|
'edit_mode_exit',
|
||
|
'default_camera_disable_sketch_mode',
|
||
|
'reset sketch metadata',
|
||
|
],
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
guards: {
|
||
|
'is editing existing sketch': ({ sketchPathToNode }) =>
|
||
|
!!sketchPathToNode,
|
||
|
'Can make selection horizontal': ({ selectionRanges }) =>
|
||
|
horzVertInfo(selectionRanges, 'horizontal').enabled,
|
||
|
'Can make selection vertical': ({ selectionRanges }) =>
|
||
|
horzVertInfo(selectionRanges, 'vertical').enabled,
|
||
|
'Can constrain horizontal distance': ({ selectionRanges }) =>
|
||
|
horzVertDistanceInfo({ selectionRanges, constraint: 'setHorzDistance' })
|
||
|
.enabled,
|
||
|
'Can constrain vertical distance': ({ selectionRanges }) =>
|
||
|
horzVertDistanceInfo({ selectionRanges, constraint: 'setVertDistance' })
|
||
|
.enabled,
|
||
|
'Can constrain angle': ({ selectionRanges }) =>
|
||
|
angleBetweenInfo({ selectionRanges }).enabled,
|
||
|
'Can constrain length': ({ selectionRanges }) =>
|
||
|
setAngleLengthInfo({ selectionRanges }).enabled,
|
||
|
'Can constrain horizontally align': ({ selectionRanges }) =>
|
||
|
horzVertDistanceInfo({ selectionRanges, constraint: 'setHorzDistance' })
|
||
|
.enabled,
|
||
|
'Can constrain vertically align': ({ selectionRanges }) =>
|
||
|
horzVertDistanceInfo({ selectionRanges, constraint: 'setHorzDistance' })
|
||
|
.enabled,
|
||
|
'Can constrain equal length': ({ selectionRanges }) =>
|
||
|
setEqualLengthInfo({ selectionRanges }).enabled,
|
||
|
'has no selection': ({ selectionRanges }) => {
|
||
|
if (selectionRanges?.codeBasedSelections?.length < 1) return true
|
||
|
const selection = selectionRanges?.codeBasedSelections?.[0] || {}
|
||
|
|
||
|
return (
|
||
|
selectionRanges.codeBasedSelections.length === 1 &&
|
||
|
!hasExtrudeSketchGroup({
|
||
|
ast: kclManager.ast,
|
||
|
programMemory: kclManager.programMemory,
|
||
|
selection,
|
||
|
})
|
||
|
)
|
||
|
},
|
||
|
'has valid extrude selection': ({ selectionRanges }) => {
|
||
|
if (selectionRanges.codeBasedSelections.length !== 1) return false
|
||
|
const isSketchPipe = isCursorInSketchCommandRange(
|
||
|
engineCommandManager.artifactMap,
|
||
|
selectionRanges
|
||
|
)
|
||
|
const common = {
|
||
|
selection: selectionRanges.codeBasedSelections[0],
|
||
|
ast: kclManager.ast,
|
||
|
}
|
||
|
const hasClose = doesPipeHaveCallExp({ calleeName: 'close', ...common })
|
||
|
const hasExtrude = doesPipeHaveCallExp({
|
||
|
calleeName: 'extrude',
|
||
|
...common,
|
||
|
})
|
||
|
return !!isSketchPipe && hasClose && !hasExtrude
|
||
|
},
|
||
|
},
|
||
|
actions: {
|
||
|
'Add to code-based selection': assign({
|
||
|
selectionRanges: ({ selectionRanges }, event) => ({
|
||
|
...selectionRanges,
|
||
|
codeBasedSelections: [
|
||
|
...selectionRanges.codeBasedSelections,
|
||
|
event.data,
|
||
|
],
|
||
|
}),
|
||
|
}),
|
||
|
'Add to other selection': assign({
|
||
|
selectionRanges: ({ selectionRanges }, event) => ({
|
||
|
...selectionRanges,
|
||
|
otherSelections: [...selectionRanges.otherSelections, event.data],
|
||
|
}),
|
||
|
}),
|
||
|
'Remove from code-based selection': assign({
|
||
|
selectionRanges: ({ selectionRanges }, event) => ({
|
||
|
...selectionRanges,
|
||
|
codeBasedSelections: [
|
||
|
...selectionRanges.codeBasedSelections,
|
||
|
event.data,
|
||
|
],
|
||
|
}),
|
||
|
}),
|
||
|
'Remove from other selection': assign({
|
||
|
selectionRanges: ({ selectionRanges }, event) => ({
|
||
|
...selectionRanges,
|
||
|
otherSelections: [...selectionRanges.otherSelections, event.data],
|
||
|
}),
|
||
|
}),
|
||
|
'Clear selection': assign({
|
||
|
selectionRanges: () => ({
|
||
|
otherSelections: [],
|
||
|
codeBasedSelections: [],
|
||
|
}),
|
||
|
}),
|
||
|
'sketch mode enabled': ({ sketchPlaneId }) => {
|
||
|
engineCommandManager.sendSceneCommand({
|
||
|
type: 'modeling_cmd_req',
|
||
|
cmd_id: uuidv4(),
|
||
|
cmd: {
|
||
|
type: 'sketch_mode_enable',
|
||
|
plane_id: sketchPlaneId,
|
||
|
ortho: true,
|
||
|
animated: !isReducedMotion(),
|
||
|
},
|
||
|
})
|
||
|
},
|
||
|
'edit mode enter': ({ selectionRanges }) => {
|
||
|
const pathId = isCursorInSketchCommandRange(
|
||
|
engineCommandManager.artifactMap,
|
||
|
selectionRanges
|
||
|
)
|
||
|
pathId &&
|
||
|
engineCommandManager.sendSceneCommand({
|
||
|
type: 'modeling_cmd_req',
|
||
|
cmd_id: uuidv4(),
|
||
|
cmd: {
|
||
|
type: 'edit_mode_enter',
|
||
|
target: pathId,
|
||
|
},
|
||
|
})
|
||
|
},
|
||
|
'hide default planes': ({}) => {
|
||
|
kclManager.hidePlanes()
|
||
|
},
|
||
|
edit_mode_exit: () =>
|
||
|
engineCommandManager.sendSceneCommand({
|
||
|
type: 'modeling_cmd_req',
|
||
|
cmd_id: uuidv4(),
|
||
|
cmd: { type: 'edit_mode_exit' },
|
||
|
}),
|
||
|
default_camera_disable_sketch_mode: () =>
|
||
|
engineCommandManager.sendSceneCommand({
|
||
|
type: 'modeling_cmd_req',
|
||
|
cmd_id: uuidv4(),
|
||
|
cmd: { type: 'default_camera_disable_sketch_mode' },
|
||
|
}),
|
||
|
'reset sketch metadata': assign({
|
||
|
sketchPathToNode: null,
|
||
|
sketchEnginePathId: '',
|
||
|
sketchPlaneId: '',
|
||
|
}),
|
||
|
'set sketch metadata': assign(({ selectionRanges }) => {
|
||
|
const sourceRange = selectionRanges.codeBasedSelections[0].range
|
||
|
const sketchPathToNode = getNodePathFromSourceRange(
|
||
|
kclManager.ast,
|
||
|
sourceRange
|
||
|
)
|
||
|
const pipeExpression = getNodeFromPath<PipeExpression>(
|
||
|
kclManager.ast,
|
||
|
sketchPathToNode,
|
||
|
'PipeExpression'
|
||
|
).node
|
||
|
if (pipeExpression.type !== 'PipeExpression') return {}
|
||
|
const sketchCallExpression = pipeExpression.body.find(
|
||
|
(e) =>
|
||
|
e.type === 'CallExpression' && e.callee.name === 'startSketchOn'
|
||
|
) as CallExpression
|
||
|
if (!sketchCallExpression) return {}
|
||
|
const firstArg = sketchCallExpression.arguments[0]
|
||
|
let planeId = ''
|
||
|
if (firstArg.type === 'Literal' && firstArg.value) {
|
||
|
const planeStrCleaned = firstArg.value
|
||
|
.toString()
|
||
|
.toLowerCase()
|
||
|
.replace('-', '')
|
||
|
if (
|
||
|
planeStrCleaned === 'xy' ||
|
||
|
planeStrCleaned === 'xz' ||
|
||
|
planeStrCleaned === 'yz'
|
||
|
) {
|
||
|
planeId = kclManager.getPlaneId(planeStrCleaned)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
console.log('planeId', planeId)
|
||
|
|
||
|
const sketchEnginePathId =
|
||
|
isCursorInSketchCommandRange(
|
||
|
engineCommandManager.artifactMap,
|
||
|
selectionRanges
|
||
|
) || ''
|
||
|
return {
|
||
|
sketchPathToNode,
|
||
|
sketchEnginePathId,
|
||
|
sketchPlaneId: planeId,
|
||
|
}
|
||
|
}),
|
||
|
'set tool line': () =>
|
||
|
engineCommandManager.sendSceneCommand({
|
||
|
type: 'modeling_cmd_req',
|
||
|
cmd_id: uuidv4(),
|
||
|
cmd: {
|
||
|
type: 'set_tool',
|
||
|
tool: 'sketch_line',
|
||
|
},
|
||
|
}),
|
||
|
'equip select': () =>
|
||
|
engineCommandManager.sendSceneCommand({
|
||
|
type: 'modeling_cmd_req',
|
||
|
cmd_id: uuidv4(),
|
||
|
cmd: {
|
||
|
type: 'set_tool',
|
||
|
tool: 'select',
|
||
|
},
|
||
|
}),
|
||
|
'set tool move': () =>
|
||
|
engineCommandManager.sendSceneCommand({
|
||
|
type: 'modeling_cmd_req',
|
||
|
cmd_id: uuidv4(),
|
||
|
cmd: {
|
||
|
type: 'set_tool',
|
||
|
tool: 'move',
|
||
|
},
|
||
|
}),
|
||
|
'Make selection horizontal': ({ selectionRanges }) => {
|
||
|
const { modifiedAst, pathToNodeMap } = applyConstraintHorzVert(
|
||
|
selectionRanges,
|
||
|
'horizontal',
|
||
|
kclManager.ast,
|
||
|
kclManager.programMemory
|
||
|
)
|
||
|
kclManager.updateAst(modifiedAst, true, {
|
||
|
// TODO re implement cursor shit
|
||
|
// callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
||
|
})
|
||
|
},
|
||
|
'Make selection vertical': ({ selectionRanges }) => {
|
||
|
const { modifiedAst, pathToNodeMap } = applyConstraintHorzVert(
|
||
|
selectionRanges,
|
||
|
'vertical',
|
||
|
kclManager.ast,
|
||
|
kclManager.programMemory
|
||
|
)
|
||
|
kclManager.updateAst(modifiedAst, true, {
|
||
|
// TODO re implement cursor shit
|
||
|
// callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
||
|
})
|
||
|
},
|
||
|
'Constrain horizontally align': ({ selectionRanges }) => {
|
||
|
const { modifiedAst, pathToNodeMap } = applyConstraintHorzVertAlign({
|
||
|
selectionRanges,
|
||
|
constraint: 'setVertDistance',
|
||
|
})
|
||
|
kclManager.updateAst(modifiedAst, true, {
|
||
|
// TODO re implement cursor shit
|
||
|
// callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
||
|
})
|
||
|
},
|
||
|
'Constrain vertically align': ({ selectionRanges }) => {
|
||
|
const { modifiedAst, pathToNodeMap } = applyConstraintHorzVertAlign({
|
||
|
selectionRanges,
|
||
|
constraint: 'setHorzDistance',
|
||
|
})
|
||
|
kclManager.updateAst(modifiedAst, true, {
|
||
|
// TODO re implement cursor shit
|
||
|
// callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
||
|
})
|
||
|
},
|
||
|
'Constrain equal length': ({ selectionRanges }) => {
|
||
|
const { modifiedAst, pathToNodeMap } = applyConstraintEqualLength({
|
||
|
selectionRanges,
|
||
|
})
|
||
|
kclManager.updateAst(modifiedAst, true, {
|
||
|
// TODO re implement cursor shit
|
||
|
// callBack: updateCursors(setCursor, selectionRanges, pathToNodeMap),
|
||
|
})
|
||
|
},
|
||
|
'AST extrude': ({ selectionRanges }) => {
|
||
|
const pathToNode = getNodePathFromSourceRange(
|
||
|
kclManager.ast,
|
||
|
selectionRanges.codeBasedSelections[0].range
|
||
|
)
|
||
|
const { modifiedAst, pathToExtrudeArg } = extrudeSketch(
|
||
|
kclManager.ast,
|
||
|
pathToNode
|
||
|
)
|
||
|
// TODO not handling focusPath correctly I think
|
||
|
kclManager.updateAst(modifiedAst, true, {
|
||
|
focusPath: pathToExtrudeArg,
|
||
|
})
|
||
|
},
|
||
|
'set default plane id': assign({
|
||
|
sketchPlaneId: (_, { data }) => data.planeId,
|
||
|
}),
|
||
|
},
|
||
|
}
|
||
|
)
|