Compare commits

..

2 Commits

46 changed files with 2418 additions and 5102 deletions

View File

@ -1733,7 +1733,7 @@ profile003 = startProfile(sketch001, at = [206.63, -56.73])
await page.waitForTimeout(600)
})
const codeFromTangentialArc = ` |> tangentialArc(end = [-10.82, 144.95])`
const codeFromTangentialArc = ` |> tangentialArc(endAbsolute = [39.49, 88.22])`
await test.step('check that tangential tool does not snap to other profile starts', async () => {
await toolbar.selectTangentialArc()
await page.waitForTimeout(1000)
@ -1755,7 +1755,7 @@ profile003 = startProfile(sketch001, at = [206.63, -56.73])
// check pixel is now gray at tanArcLocation to verify code has executed
await scene.expectPixelColor([26, 26, 26], tanArcLocation, 15)
await editor.expectEditor.not.toContain(
`tangentialArc(end = [-10.82, 144.95])`
`tangentialArc(endAbsolute = [39.49, 88.22])`
)
})
@ -1955,7 +1955,7 @@ profile003 = startProfile(sketch001, at = [206.63, -56.73])
await endArcStartLine()
await editor.expectEditor.toContain(
`|> tangentialArc(end = [2.98, -7.52])`
`|> tangentialArc(endAbsolute = [16.61, 4.14])`
)
// Add a three-point arc segment
@ -3181,7 +3181,7 @@ test.describe('Redirecting to home page and back to the original file should cle
sketch001 = startSketchOn(XZ)
profile001 = startProfile(sketch001, at = [0, 0])
|> line(end = [191.39, 191.39])
|> tangentialArc(end = [95.69, -95.7], tag = $seg01)
|> tangentialArc(endAbsolute = [287.08, 95.69], tag = $seg01)
|> angledLine(angle = tangentToEnd(seg01), length = 135.34)
|> arc(interiorAbsolute = [191.39, -95.69], endAbsolute = [287.08, -95.69], tag = $seg02)
|> angledLine(angle = tangentToEnd(seg02) + turns::HALF_TURN, length = 270.67)

View File

@ -375,22 +375,22 @@ test.describe(
await expect(u.codeLocator).toHaveText(code)
await toolbar.selectTangentialArc()
await page.waitForTimeout(200)
await page.waitForTimeout(100)
// click to continue profile
await page.mouse.click(813, 392)
await page.waitForTimeout(300)
await page.waitForTimeout(100)
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
code += `
|> tangentialArc(end = [184.31, 184.31])`
|> tangentialArc(endAbsolute = [551.2, -62.01])`
await expect(u.codeLocator).toHaveText(code)
// click tangential arc tool again to unequip it
// it will be available directly in the toolbar since it was last equipped
await toolbar.tangentialArcBtn.click()
await page.waitForTimeout(1000)
await page.waitForTimeout(100)
// screen shot should show the sketch
await expect(page).toHaveScreenshot({
@ -472,20 +472,20 @@ test.describe(
await expect(u.codeLocator).toHaveText(code)
await toolbar.selectTangentialArc()
await page.waitForTimeout(200)
await page.waitForTimeout(100)
// click to continue profile
await page.mouse.click(813, 392)
await page.waitForTimeout(300)
await page.waitForTimeout(100)
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
code += `
|> tangentialArc(end = [184.31, 184.31])`
|> tangentialArc(endAbsolute = [551.2, -62.01])`
await expect(u.codeLocator).toHaveText(code)
await toolbar.tangentialArcBtn.click()
await page.waitForTimeout(1000)
await page.waitForTimeout(100)
// screen shot should show the sketch
await expect(page).toHaveScreenshot({

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

@ -61,7 +61,7 @@ test.describe('Test network related behaviors', () => {
})
// Expect the network to be down
await expect(networkToggle).toContainText('Problem')
await expect(networkToggle).toContainText('Network health (Offline)')
// Click the network widget
await networkWidget.click()
@ -156,7 +156,8 @@ test.describe('Test network related behaviors', () => {
// Expect the network to be down
await networkToggle.hover()
await expect(networkToggle).toContainText('Problem')
await expect(networkToggle).toContainText('Network health (Offline)')
// Ensure we are not in sketch mode
await expect(

View File

@ -364,11 +364,6 @@ export async function getUtils(page: Page, test_?: typeof test) {
)
}
// Chrome devtools protocol session only works in Chromium
const browserType = page.context().browser()?.browserType().name()
const cdpSession =
browserType !== 'chromium' ? null : await page.context().newCDPSession(page)
const util = {
waitForAuthSkipAppStart: () => waitForAuthAndLsp(page),
waitForPageLoad: () => waitForPageLoad(page),
@ -489,15 +484,9 @@ export async function getUtils(page: Page, test_?: typeof test) {
emulateNetworkConditions: async (
networkOptions: Protocol.Network.emulateNetworkConditionsParameters
) => {
if (cdpSession === null) {
// Use a fail safe if we can't simulate disconnect (on Safari)
return page.evaluate('window.engineCommandManager.tearDown()')
}
return cdpSession?.send(
'Network.emulateNetworkConditions',
networkOptions
)
return networkOptions.offline
? page.evaluate('window.engineCommandManager.offline()')
: page.evaluate('window.engineCommandManager.online()')
},
toNormalizedCode(text: string) {

View File

@ -7,17 +7,13 @@
// Import parameters
import pipeInnerDiameter, pipeOuterDiameter, pipeLength from "parameters.kcl"
// Create a function to make the pipe. Export
export fn pipe() {
// Create the pipe base
pipeBase = startSketchOn(XZ)
// Create the pipe base
pipeBase = startSketchOn(XZ)
|> circle(%, center = [0, 0], radius = pipeOuterDiameter / 2)
|> extrude(%, length = pipeLength)
// Extrude a hole through the length of the pipe
pipe = startSketchOn(pipeBase, face = END)
// Extrude a hole through the length of the pipe
startSketchOn(pipeBase, face = END)
|> circle(center = [0, 0], radius = pipeInnerDiameter / 2)
|> extrude(%, length = -pipeLength)
|> appearance(color = "#a24ed0")
return pipe
}

View File

@ -7,10 +7,8 @@
// Import parameters
import pipeDiameter, mountingHoleDiameter, mountingHolePlacementDiameter, flangeDiameter, flangeTotalThickness, flangeBackHeight, flangeFrontHeight, flangeBaseThickness, flangeBackDiameter, flangeFrontDiameter from "parameters.kcl"
// Create a function to create the flange. We must create a function since we are using multiple flanges.
export fn flange() {
// Sketch the mounting hole pattern
mountingHoles = startSketchOn(XY)
// Sketch the mounting hole pattern
mountingHoles = startSketchOn(XY)
|> circle(%, center = [0, mountingHolePlacementDiameter / 2], radius = mountingHoleDiameter / 2)
|> patternCircular2d(
%,
@ -20,25 +18,22 @@ export fn flange() {
rotateDuplicates = false,
)
// Create the flange base
flangeBase = startSketchOn(XY)
// Create the flange base
flangeBase = startSketchOn(XY)
|> circle(%, center = [0, 0], radius = flangeDiameter / 2)
|> subtract2d(tool = mountingHoles)
|> extrude(%, length = flangeBaseThickness)
// Create both the raised portions on the front and back of the flange base
flangeBack = startSketchOn(flangeBase, face = START)
// Create both the raised portions on the front and back of the flange base
flangeBack = startSketchOn(flangeBase, face = START)
|> circle(%, center = [0, 0], radius = flangeBackDiameter / 2)
|> extrude(%, length = flangeBackHeight)
flangeFront = startSketchOn(flangeBase, face = END)
flangeFront = startSketchOn(flangeBase, face = END)
|> circle(%, center = [0, 0], radius = flangeFrontDiameter / 2)
|> extrude(%, length = flangeFrontHeight)
// Create the circular cut in the center for the pipe
pipeCut = startSketchOn(flangeFront, face = END)
// Create the circular cut in the center for the pipe
startSketchOn(flangeFront, face = END)
|> circle(%, center = [0, 0], radius = pipeDiameter / 2)
|> extrude(%, length = -flangeTotalThickness)
|> appearance(%, color = "#bab0b0")
return pipeCut
}

View File

@ -7,16 +7,14 @@
// Import parameters
import boltDiameter, boltLength, boltHeadLength, boltHeadDiameter, boltHexDrive, boltHexFlatLength, boltThreadLength from "parameters.kcl"
// Create a function to make a the bolt
export fn bolt() {
// Create the head of the cap screw
boltHead = startSketchOn(XZ)
// Create the head of the cap screw
boltHead = startSketchOn(XZ)
|> circle(center = [0, 0], radius = boltHeadDiameter / 2, tag = $topEdge)
|> extrude(length = -boltHeadLength)
|> fillet(radius = 0.020, tags = [topEdge, getOppositeEdge(topEdge)])
// Define the sketch of the hex pattern on the screw head and extrude into the head
hexPatternSketch = startSketchOn(boltHead, face = START)
// Define the sketch of the hex pattern on the screw head and extrude into the head
hexPatternSketch = startSketchOn(boltHead, face = START)
|> startProfile(at = [
boltHexDrive / 2,
boltHexFlatLength / 2
@ -29,11 +27,8 @@ export fn bolt() {
|> close()
|> extrude(length = -boltHeadLength * 0.75)
// create the body of the bolt
boltBody = startSketchOn(boltHead, face = END)
// create the body of the bolt
startSketchOn(boltHead, face = END)
|> circle(center = [0, 0], radius = boltDiameter / 2, tag = $filletEdge)
|> extrude(length = boltLength)
|> appearance(color = "#4dd043", metalness = 90, roughness = 90)
return boltBody
}

View File

@ -7,10 +7,8 @@
// Import parameters
import hexNutDiameter, hexNutFlatToFlat, hexNutThickness, hexNutFlatLength from "parameters.kcl"
// Create a function to make the hex nut. Must be a function since multiple hex nuts are used
export fn hexNut() {
// Create the base of the hex nut
hexNutBase = startSketchOn(XY)
// Create the base of the hex nut
hexNutBase = startSketchOn(XY)
|> startProfile(at = [
hexNutFlatToFlat / 2,
hexNutFlatLength / 2
@ -23,11 +21,8 @@ export fn hexNut() {
|> close()
|> extrude(length = hexNutThickness)
// Create the hole in the center of the hex nut
hexNut = startSketchOn(hexNutBase, face = END)
// Create the hole in the center of the hex nut
startSketchOn(hexNutBase, face = END)
|> circle(center = [0, 0], radius = hexNutDiameter / 2)
|> extrude(%, length = -hexNutThickness)
|> appearance(%, color = "#4edfd5")
return hexNut
}

View File

@ -7,18 +7,13 @@
// Import parameters
import washerInnerDia, washerOuterDia, washerThickness from "parameters.kcl"
// Create a function to make the washer. Must be a function since multiple washers are used.
export fn washer() {
// Create the base of the washer
washerBase = startSketchOn(XY)
// Create the base of the washer
washerBase = startSketchOn(XY)
|> circle(center = [0, 0], radius = washerOuterDia / 2)
|> extrude(length = washerThickness)
// Extrude a hole through the washer
washer = startSketchOn(washerBase, face = END)
// Extrude a hole through the washer
washer = startSketchOn(washerBase, face = END)
|> circle(center = [0, 0], radius = washerInnerDia / 2)
|> extrude(%, length = -washerThickness)
|> appearance(%, color = "#ee4f4f")
return washer
}

View File

@ -9,16 +9,19 @@ import * from "parameters.kcl"
// Import parts
import "9472k188-gasket.kcl" as gasket
import flange from "68095k348-flange.kcl"
import washer from "98017a257-washer.kcl"
import bolt from "91251a404-bolt.kcl"
import hexNut from "95479a127-hex-nut.kcl"
import pipe from "1120t74-pipe.kcl"
import "68095k348-flange.kcl" as flange
import "98017a257-washer.kcl" as washer
import "91251a404-bolt.kcl" as bolt
import "95479a127-hex-nut.kcl" as hexNut
import "1120t74-pipe.kcl" as pipe
// Place flanges
flange()
flange()
|> rotate(axis = [0, 1, 0], angle = 180)
// Place flange clone
rotate(
clone(flange),
roll = 180,
pitch = 0,
yaw = 0,
)
|> translate(x = 0, y = 0, z = flangeBackHeight * 2 + gasketThickness)
// Place gasket between the flanges
@ -26,7 +29,7 @@ gasket
|> translate(x = 0, y = 0, z = -flangeBackHeight - gasketThickness)
// Place eight washers (four front, four back)
washer()
washer
|> translate(x = mountingHolePlacementDiameter / 2, y = 0, z = flangeBaseThickness)
|> patternCircular3d(
%,
@ -44,7 +47,8 @@ washer()
)
// Place four bolts
bolt()
bolt
|> translate(x = mountingHolePlacementDiameter / 2, y = 0, z = flangeBaseThickness + washerThickness)
|> rotate(roll = 90, pitch = 0, yaw = 0)
|> patternCircular3d(
@ -57,7 +61,7 @@ bolt()
)
// Place four hex nuts
hexNut()
hexNut
|> translate(x = mountingHolePlacementDiameter / 2, y = 0, z = -(flangeBackHeight * 2 + gasketThickness + flangeBaseThickness + washerThickness + hexNutThickness))
|> patternCircular3d(
%,
@ -69,9 +73,8 @@ hexNut()
)
// Place both pieces of pipe
pipe()
|> rotate(
%,
rotate(
pipe,
roll = -90,
pitch = 0,
yaw = 0,
@ -83,16 +86,13 @@ pipe()
z = flangeBaseThickness + flangeFrontHeight - 0.5,
global = true,
)
pipe()
|> rotate(
%,
roll = 90,
rotate(
clone(pipe),
roll = 180,
pitch = 0,
yaw = 0,
)
|> translate(
%,
x = 0,
y = 0,
z = -(flangeBackHeight * 2 + gasketThickness + flangeBaseThickness + flangeFrontHeight - 0.5),

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 75 KiB

View File

@ -652,19 +652,19 @@ flowchart LR
84 --- 144
84 --- 237
86 --- 168
86 x--> 188
86 x--> 189
86 --- 212
86 --- 256
88 --- 169
88 x--> 188
88 x--> 189
88 --- 213
88 --- 257
90 --- 167
90 x--> 188
90 x--> 189
90 --- 214
90 --- 258
92 --- 170
92 x--> 188
92 x--> 189
92 --- 215
92 --- 259
119 --- 133
@ -946,10 +946,10 @@ flowchart LR
218 <--x 186
219 <--x 186
194 <--x 187
212 <--x 189
213 <--x 189
214 <--x 189
215 <--x 189
212 <--x 188
213 <--x 188
214 <--x 188
215 <--x 188
220 <--x 268
223 <--x 267
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

View File

@ -87,20 +87,20 @@ flowchart LR
27 --- 4
8 --- 20
8 x--> 25
8 --- 29
8 --- 35
8 --- 30
8 --- 36
9 --- 21
9 x--> 25
9 --- 33
9 --- 39
10 --- 22
10 x--> 25
10 --- 30
10 --- 36
10 --- 31
10 --- 37
11 --- 23
11 x--> 25
11 --- 31
11 --- 37
11 --- 29
11 --- 35
12 --- 24
12 x--> 25
12 --- 32
@ -132,18 +132,18 @@ flowchart LR
18 --- 34
19 --- 28
19 --- 34
20 --- 29
20 --- 35
36 <--x 20
20 --- 30
20 --- 36
37 <--x 20
21 --- 33
35 <--x 21
21 --- 39
22 --- 30
22 --- 36
37 <--x 22
23 --- 31
23 --- 37
38 <--x 23
22 --- 31
22 --- 37
38 <--x 22
23 --- 29
23 --- 35
36 <--x 23
24 --- 32
24 --- 38
39 <--x 24

View File

@ -366,114 +366,114 @@ flowchart LR
11 ---- 74
26 --- 75
26 x--> 108
26 --- 130
26 --- 162
26 --- 114
26 --- 146
27 --- 76
27 x--> 108
27 --- 116
27 --- 148
27 --- 139
27 --- 171
28 --- 77
28 x--> 108
28 --- 139
28 --- 171
28 --- 135
28 --- 167
29 --- 78
29 x--> 108
29 --- 135
29 --- 167
29 --- 133
29 --- 165
30 --- 79
30 x--> 108
30 --- 128
30 --- 160
30 --- 121
30 --- 153
31 --- 80
31 x--> 108
31 --- 123
31 --- 155
31 --- 115
31 --- 147
32 --- 81
32 x--> 108
32 --- 127
32 --- 159
32 --- 129
32 --- 161
33 --- 82
33 x--> 108
33 --- 132
33 --- 164
33 --- 131
33 --- 163
34 --- 83
34 x--> 108
34 --- 133
34 --- 165
34 --- 138
34 --- 170
35 --- 84
35 x--> 108
35 --- 121
35 --- 153
35 --- 119
35 --- 151
36 --- 85
36 x--> 108
36 --- 137
36 --- 169
36 --- 125
36 --- 157
37 --- 86
37 x--> 108
37 --- 115
37 --- 147
37 --- 117
37 --- 149
38 --- 87
38 x--> 108
38 --- 120
38 --- 152
38 --- 122
38 --- 154
39 --- 88
39 x--> 108
39 --- 124
39 --- 156
39 --- 113
39 --- 145
40 --- 89
40 x--> 108
40 --- 134
40 --- 166
40 --- 124
40 --- 156
41 --- 90
41 x--> 108
41 --- 136
41 --- 168
41 --- 134
41 --- 166
42 --- 91
42 x--> 108
42 --- 122
42 --- 154
42 --- 132
42 --- 164
43 --- 92
43 x--> 108
43 --- 114
43 --- 146
43 --- 120
43 --- 152
44 --- 93
44 x--> 108
44 --- 113
44 --- 145
44 --- 118
44 --- 150
45 --- 94
45 x--> 108
45 --- 131
45 --- 163
45 --- 137
45 --- 169
46 --- 95
46 x--> 108
46 --- 117
46 --- 149
46 --- 130
46 --- 162
47 --- 96
47 x--> 108
47 --- 125
47 --- 157
47 --- 126
47 --- 158
48 --- 97
48 x--> 108
48 --- 119
48 --- 151
48 --- 128
48 --- 160
49 --- 98
49 x--> 108
49 --- 126
49 --- 158
49 --- 127
49 --- 159
50 --- 99
50 x--> 108
50 --- 118
50 --- 150
50 --- 136
50 --- 168
51 --- 100
51 x--> 108
51 --- 129
51 --- 161
51 --- 116
51 --- 148
52 --- 101
52 x--> 108
52 --- 138
52 --- 170
52 --- 123
52 --- 155
61 --- 102
61 x--> 110
61 x--> 109
61 --- 140
61 --- 172
63 --- 103
@ -594,87 +594,87 @@ flowchart LR
74 --- 174
74 --- 175
74 --- 176
75 --- 130
75 --- 162
163 <--x 75
76 --- 116
76 --- 148
149 <--x 76
77 --- 139
145 <--x 77
77 --- 171
78 --- 135
78 --- 167
168 <--x 78
79 --- 128
79 --- 160
161 <--x 79
80 --- 123
80 --- 155
156 <--x 80
81 --- 127
81 --- 159
160 <--x 81
82 --- 132
82 --- 164
165 <--x 82
83 --- 133
83 --- 165
166 <--x 83
84 --- 121
84 --- 153
154 <--x 84
85 --- 137
85 --- 169
170 <--x 85
86 --- 115
86 --- 147
148 <--x 86
87 --- 120
87 --- 152
153 <--x 87
88 --- 124
88 --- 156
157 <--x 88
89 --- 134
89 --- 166
167 <--x 89
90 --- 136
90 --- 168
169 <--x 90
91 --- 122
91 --- 154
155 <--x 91
92 --- 114
92 --- 146
147 <--x 92
93 --- 113
93 --- 145
146 <--x 93
94 --- 131
94 --- 163
164 <--x 94
95 --- 117
95 --- 149
150 <--x 95
96 --- 125
96 --- 157
158 <--x 96
97 --- 119
97 --- 151
152 <--x 97
98 --- 126
98 --- 158
159 <--x 98
99 --- 118
99 --- 150
151 <--x 99
100 --- 129
100 --- 161
162 <--x 100
101 --- 138
101 --- 170
171 <--x 101
75 --- 114
75 --- 146
147 <--x 75
76 --- 139
145 <--x 76
76 --- 171
77 --- 135
77 --- 167
168 <--x 77
78 --- 133
78 --- 165
166 <--x 78
79 --- 121
79 --- 153
154 <--x 79
80 --- 115
80 --- 147
148 <--x 80
81 --- 129
81 --- 161
162 <--x 81
82 --- 131
82 --- 163
164 <--x 82
83 --- 138
83 --- 170
171 <--x 83
84 --- 119
84 --- 151
152 <--x 84
85 --- 125
85 --- 157
158 <--x 85
86 --- 117
86 --- 149
150 <--x 86
87 --- 122
87 --- 154
155 <--x 87
88 --- 113
88 --- 145
146 <--x 88
89 --- 124
89 --- 156
157 <--x 89
90 --- 134
90 --- 166
167 <--x 90
91 --- 132
91 --- 164
165 <--x 91
92 --- 120
92 --- 152
153 <--x 92
93 --- 118
93 --- 150
151 <--x 93
94 --- 137
94 --- 169
170 <--x 94
95 --- 130
95 --- 162
163 <--x 95
96 --- 126
96 --- 158
159 <--x 96
97 --- 128
97 --- 160
161 <--x 97
98 --- 127
98 --- 159
160 <--x 98
99 --- 136
99 --- 168
169 <--x 99
100 --- 116
100 --- 148
149 <--x 100
101 --- 123
101 --- 155
156 <--x 101
102 --- 140
102 --- 172
103 --- 144
@ -689,7 +689,7 @@ flowchart LR
106 --- 141
106 --- 173
174 <--x 106
140 <--x 109
140 <--x 110
141 <--x 111
142 <--x 111
143 <--x 111

View File

@ -582,48 +582,48 @@ flowchart LR
46 --- 226
72 --- 112
72 x--> 151
72 --- 167
72 --- 201
72 --- 163
72 --- 197
73 --- 113
73 x--> 151
73 --- 169
73 --- 203
73 --- 164
73 --- 198
74 --- 114
74 x--> 151
74 --- 168
74 --- 202
75 --- 115
75 x--> 151
75 --- 166
75 --- 200
75 --- 165
75 --- 199
76 --- 116
76 x--> 151
76 --- 165
76 --- 199
76 --- 162
76 --- 196
77 --- 117
77 x--> 151
77 --- 161
77 --- 195
78 --- 118
78 x--> 151
78 --- 162
78 --- 196
78 --- 167
78 --- 201
79 --- 119
79 x--> 151
79 --- 163
79 --- 197
79 --- 170
79 --- 204
80 --- 120
80 x--> 151
80 --- 170
80 --- 204
80 --- 160
80 --- 194
81 --- 121
81 x--> 151
81 --- 160
81 --- 194
81 --- 169
81 --- 203
82 --- 122
82 x--> 151
82 --- 164
82 --- 198
82 --- 166
82 --- 200
84 --- 123
84 x--> 145
84 --- 171
@ -756,39 +756,39 @@ flowchart LR
109 --- 107
111 --- 159
111 --- 193
112 --- 167
112 --- 201
202 <--x 112
113 --- 169
113 --- 203
204 <--x 113
112 --- 163
112 --- 197
198 <--x 112
113 --- 164
113 --- 198
199 <--x 113
114 --- 168
114 --- 202
203 <--x 114
115 --- 166
115 --- 200
201 <--x 115
116 --- 165
116 --- 199
200 <--x 116
115 --- 165
115 --- 199
200 <--x 115
116 --- 162
116 --- 196
197 <--x 116
117 --- 161
117 --- 195
196 <--x 117
118 --- 162
118 --- 196
197 <--x 118
119 --- 163
119 --- 197
198 <--x 119
120 --- 170
194 <--x 120
120 --- 204
121 --- 160
121 --- 194
195 <--x 121
122 --- 164
122 --- 198
199 <--x 122
118 --- 167
118 --- 201
202 <--x 118
119 --- 170
194 <--x 119
119 --- 204
120 --- 160
120 --- 194
195 <--x 120
121 --- 169
121 --- 203
204 <--x 121
122 --- 166
122 --- 200
201 <--x 122
123 --- 171
123 --- 205
124 --- 186

View File

@ -177,72 +177,72 @@ flowchart LR
2 ---- 34
17 --- 35
17 x--> 52
17 --- 58
17 --- 75
17 --- 67
17 --- 84
18 --- 36
18 x--> 52
18 --- 66
18 --- 83
18 --- 65
18 --- 82
19 --- 37
19 x--> 52
19 --- 60
19 --- 77
19 --- 55
19 --- 72
20 --- 38
20 x--> 52
20 --- 61
20 --- 78
21 --- 39
21 x--> 52
21 --- 67
21 --- 84
21 --- 69
21 --- 86
22 --- 40
22 x--> 52
22 --- 63
22 --- 80
23 --- 41
23 x--> 52
23 --- 56
23 --- 73
23 --- 60
23 --- 77
24 --- 42
24 x--> 52
24 --- 54
24 --- 71
25 --- 43
25 x--> 52
25 --- 70
25 --- 87
25 --- 66
25 --- 83
26 --- 44
26 x--> 52
26 --- 68
26 --- 85
26 --- 57
26 --- 74
27 --- 45
27 x--> 52
27 --- 65
27 --- 82
27 --- 64
27 --- 81
28 --- 46
28 x--> 52
28 --- 62
28 --- 79
28 --- 58
28 --- 75
29 --- 47
29 x--> 52
29 --- 57
29 --- 74
29 --- 59
29 --- 76
30 --- 48
30 x--> 52
30 --- 55
30 --- 72
30 --- 56
30 --- 73
31 --- 49
31 x--> 52
31 --- 64
31 --- 81
31 --- 62
31 --- 79
32 --- 50
32 x--> 52
32 --- 69
32 --- 86
32 --- 70
32 --- 87
33 --- 51
33 x--> 52
33 --- 59
33 --- 76
33 --- 68
33 --- 85
34 --- 35
34 --- 36
34 --- 37
@ -296,57 +296,57 @@ flowchart LR
34 --- 85
34 --- 86
34 --- 87
35 --- 58
74 <--x 35
35 --- 75
36 --- 66
82 <--x 36
36 --- 83
37 --- 60
76 <--x 37
37 --- 77
35 --- 67
83 <--x 35
35 --- 84
36 --- 65
81 <--x 36
36 --- 82
37 --- 55
71 <--x 37
37 --- 72
38 --- 61
77 <--x 38
38 --- 78
39 --- 67
83 <--x 39
39 --- 84
39 --- 69
85 <--x 39
39 --- 86
40 --- 63
79 <--x 40
40 --- 80
41 --- 56
72 <--x 41
41 --- 73
41 --- 60
76 <--x 41
41 --- 77
42 --- 54
42 --- 71
87 <--x 42
43 --- 70
86 <--x 43
43 --- 87
44 --- 68
84 <--x 44
44 --- 85
45 --- 65
81 <--x 45
45 --- 82
46 --- 62
78 <--x 46
46 --- 79
47 --- 57
73 <--x 47
47 --- 74
48 --- 55
71 <--x 48
48 --- 72
49 --- 64
80 <--x 49
49 --- 81
50 --- 69
85 <--x 50
50 --- 86
51 --- 59
75 <--x 51
51 --- 76
43 --- 66
82 <--x 43
43 --- 83
44 --- 57
73 <--x 44
44 --- 74
45 --- 64
80 <--x 45
45 --- 81
46 --- 58
74 <--x 46
46 --- 75
47 --- 59
75 <--x 47
47 --- 76
48 --- 56
72 <--x 48
48 --- 73
49 --- 62
78 <--x 49
49 --- 79
50 --- 70
86 <--x 50
50 --- 87
51 --- 68
84 <--x 51
51 --- 85
54 <--x 53
55 <--x 53
56 <--x 53

View File

@ -59,23 +59,14 @@ description: Result of parsing pipe-flange-assembly.kcl
"filename": "68095k348-flange.kcl"
},
"selector": {
"type": "List",
"items": [
{
"alias": null,
"commentStart": 0,
"end": 0,
"name": {
"type": "None",
"alias": {
"commentStart": 0,
"end": 0,
"name": "flange",
"start": 0,
"type": "Identifier"
},
"start": 0,
"type": "ImportItem"
}
]
},
"start": 0,
"type": "ImportStatement",
@ -89,23 +80,14 @@ description: Result of parsing pipe-flange-assembly.kcl
"filename": "98017a257-washer.kcl"
},
"selector": {
"type": "List",
"items": [
{
"alias": null,
"commentStart": 0,
"end": 0,
"name": {
"type": "None",
"alias": {
"commentStart": 0,
"end": 0,
"name": "washer",
"start": 0,
"type": "Identifier"
},
"start": 0,
"type": "ImportItem"
}
]
},
"start": 0,
"type": "ImportStatement",
@ -119,23 +101,14 @@ description: Result of parsing pipe-flange-assembly.kcl
"filename": "91251a404-bolt.kcl"
},
"selector": {
"type": "List",
"items": [
{
"alias": null,
"commentStart": 0,
"end": 0,
"name": {
"type": "None",
"alias": {
"commentStart": 0,
"end": 0,
"name": "bolt",
"start": 0,
"type": "Identifier"
},
"start": 0,
"type": "ImportItem"
}
]
},
"start": 0,
"type": "ImportStatement",
@ -149,23 +122,14 @@ description: Result of parsing pipe-flange-assembly.kcl
"filename": "95479a127-hex-nut.kcl"
},
"selector": {
"type": "List",
"items": [
{
"alias": null,
"commentStart": 0,
"end": 0,
"name": {
"type": "None",
"alias": {
"commentStart": 0,
"end": 0,
"name": "hexNut",
"start": 0,
"type": "Identifier"
},
"start": 0,
"type": "ImportItem"
}
]
},
"start": 0,
"type": "ImportStatement",
@ -179,91 +143,24 @@ description: Result of parsing pipe-flange-assembly.kcl
"filename": "1120t74-pipe.kcl"
},
"selector": {
"type": "List",
"items": [
{
"alias": null,
"commentStart": 0,
"end": 0,
"name": {
"type": "None",
"alias": {
"commentStart": 0,
"end": 0,
"name": "pipe",
"start": 0,
"type": "Identifier"
},
"start": 0,
"type": "ImportItem"
}
]
},
"start": 0,
"type": "ImportStatement",
"type": "ImportStatement"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "flange",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
},
"preComments": [
"",
"",
"// Place flanges"
],
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"body": [
{
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "flange",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
},
{
"arguments": [
{
@ -271,62 +168,7 @@ description: Result of parsing pipe-flange-assembly.kcl
"label": {
"commentStart": 0,
"end": 0,
"name": "axis",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"elements": [
{
"commentStart": 0,
"end": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"raw": "1",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 1.0,
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
}
],
"end": 0,
"start": 0,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
},
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "angle",
"name": "roll",
"start": 0,
"type": "Identifier"
},
@ -342,6 +184,50 @@ description: Result of parsing pipe-flange-assembly.kcl
"suffix": "None"
}
}
},
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "pitch",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
}
},
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "yaw",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
}
}
],
"callee": {
@ -364,7 +250,44 @@ description: Result of parsing pipe-flange-assembly.kcl
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
"unlabeled": {
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "clone",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "flange",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
}
},
{
"arguments": [
@ -510,7 +433,7 @@ description: Result of parsing pipe-flange-assembly.kcl
"end": 0,
"nonCodeMeta": {
"nonCodeNodes": {
"2": [
"1": [
{
"commentStart": 0,
"end": 0,
@ -530,6 +453,11 @@ description: Result of parsing pipe-flange-assembly.kcl
"type": "PipeExpression",
"type": "PipeExpression"
},
"preComments": [
"",
"",
"// Place flange clone"
],
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
@ -717,7 +645,6 @@ description: Result of parsing pipe-flange-assembly.kcl
"expression": {
"body": [
{
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
@ -730,15 +657,9 @@ description: Result of parsing pipe-flange-assembly.kcl
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
},
{
"arguments": [
{
@ -1351,7 +1272,6 @@ description: Result of parsing pipe-flange-assembly.kcl
"expression": {
"body": [
{
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
@ -1364,15 +1284,9 @@ description: Result of parsing pipe-flange-assembly.kcl
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
},
{
"arguments": [
{
@ -1846,7 +1760,6 @@ description: Result of parsing pipe-flange-assembly.kcl
"expression": {
"body": [
{
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
@ -1859,15 +1772,9 @@ description: Result of parsing pipe-flange-assembly.kcl
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
},
{
"arguments": [
{
@ -2349,29 +2256,6 @@ description: Result of parsing pipe-flange-assembly.kcl
"end": 0,
"expression": {
"body": [
{
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "pipe",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
},
{
"arguments": [
{
@ -2470,11 +2354,20 @@ description: Result of parsing pipe-flange-assembly.kcl
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "pipe",
"start": 0,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
},
{
@ -2657,29 +2550,6 @@ description: Result of parsing pipe-flange-assembly.kcl
"end": 0,
"expression": {
"body": [
{
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "pipe",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
},
{
"arguments": [
{
@ -2694,12 +2564,12 @@ description: Result of parsing pipe-flange-assembly.kcl
"arg": {
"commentStart": 0,
"end": 0,
"raw": "90",
"raw": "180",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 90.0,
"value": 180.0,
"suffix": "None"
}
}
@ -2770,11 +2640,42 @@ description: Result of parsing pipe-flange-assembly.kcl
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "clone",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "pipe",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
}
},
{
@ -3009,13 +2910,7 @@ description: Result of parsing pipe-flange-assembly.kcl
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"commentStart": 0,
"end": 0,
"start": 0,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
"unlabeled": null
}
],
"commentStart": 0,
@ -3111,7 +3006,7 @@ description: Result of parsing pipe-flange-assembly.kcl
],
"nonCodeMeta": {
"nonCodeNodes": {
"13": [
"9": [
{
"commentStart": 0,
"end": 0,

File diff suppressed because it is too large Load Diff

View File

@ -3,13 +3,29 @@ source: kcl-lib/src/simulation_tests.rs
description: Variables in memory after executing pipe-flange-assembly.kcl
---
{
"__mod_bolt": {
"type": "Module",
"value": 5
},
"__mod_flange": {
"type": "Module",
"value": 3
},
"__mod_gasket": {
"type": "Module",
"value": 2
},
"bolt": {
"type": "Function",
"value": null
"__mod_hexNut": {
"type": "Module",
"value": 6
},
"__mod_pipe": {
"type": "Module",
"value": 7
},
"__mod_washer": {
"type": "Module",
"value": 4
},
"boltDiameter": {
"type": "Number",
@ -107,10 +123,6 @@ description: Variables in memory after executing pipe-flange-assembly.kcl
"type": "TagIdentifier",
"value": "filletEdge"
},
"flange": {
"type": "Function",
"value": null
},
"flangeBackDiameter": {
"type": "Number",
"value": 3.62,
@ -241,10 +253,6 @@ description: Variables in memory after executing pipe-flange-assembly.kcl
}
}
},
"hexNut": {
"type": "Function",
"value": null
},
"hexNutDiameter": {
"type": "Number",
"value": 0.625,
@ -323,10 +331,6 @@ description: Variables in memory after executing pipe-flange-assembly.kcl
}
}
},
"pipe": {
"type": "Function",
"value": null
},
"pipeDiameter": {
"type": "Number",
"value": 2.44,
@ -379,10 +383,6 @@ description: Variables in memory after executing pipe-flange-assembly.kcl
}
}
},
"washer": {
"type": "Function",
"value": null
},
"washerInnerDia": {
"type": "Number",
"value": 0.64,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 75 KiB

View File

@ -112,8 +112,8 @@ flowchart LR
8 --- 20
8 ---- 25
12 <--x 32
12 --- 33
12 <--x 34
12 <--x 33
12 --- 34
13 --- 31
13 x--> 35
13 --- 39

View File

@ -889,7 +889,7 @@ flowchart LR
99 --- 265
99 --- 314
106 --- 181
106 x--> 213
106 x--> 212
106 --- 260
106 --- 309
126 --- 194
@ -1227,7 +1227,7 @@ flowchart LR
251 <--x 209
252 <--x 209
253 <--x 209
260 <--x 212
260 <--x 213
261 <--x 214
262 <--x 214
263 <--x 214

View File

@ -114,6 +114,9 @@ export function App() {
// by the Projects view.
billingActor.send({ type: BillingTransition.Update, apiToken: authToken })
// Tell engineStream to wait for dependencies to start streaming.
engineStreamActor.send({ type: EngineStreamTransition.WaitForDependencies })
// When leaving the modeling scene, cut the engine stream.
return () => {
// When leaving the modeling scene, cut the engine stream.

View File

@ -1107,11 +1107,7 @@ export class SceneEntities {
(lastSegment.type === 'TangentialArc' && segmentName !== 'line') ||
segmentName === 'tangentialArc'
) {
if (snappedPoint[0] === 0 || snappedPoint[1] === 0) {
resolvedFunctionName = 'tangentialArcTo'
} else {
resolvedFunctionName = 'tangentialArc'
}
} else if (snappedToTangent) {
// Generate tag for previous arc segment and use it for the angle of angledLine:
// |> tangentialArc(endAbsolute = [5, -10], tag = $arc001)
@ -3588,7 +3584,8 @@ export class SceneEntities {
})
if (!resp) {
return Promise.reject('no response')
console.warn('No response')
return {} as Models['GetSketchModePlane_type']
}
if (isArray(resp)) {

View File

@ -1,4 +1,3 @@
import { isPlaywright } from '@src/lib/isPlaywright'
import { useAppState } from '@src/AppState'
import { ClientSideScene } from '@src/clientSideScene/ClientSideSceneComp'
import { ViewControlContextMenu } from '@src/components/ViewControlMenu'
@ -52,8 +51,10 @@ export const EngineStream = (props: {
const last = useRef<number>(Date.now())
const [firstPlay, setFirstPlay] = useState(true)
const [isRestartRequestStarting, setIsRestartRequestStarting] =
useState(false)
const [goRestart, setGoRestart] = useState(false)
const [timeoutId, setTimeoutId] = useState<
ReturnType<typeof setTimeout> | undefined
>(undefined)
const [attemptTimes, setAttemptTimes] = useState<[number, number]>([
0,
TIME_1_SECOND,
@ -85,18 +86,21 @@ export const EngineStream = (props: {
const streamIdleMode = settings.app.streamIdleMode.current
useEffect(() => {
// Will cause a useEffect loop if not checked for.
if (engineStreamState.context.videoRef.current !== null) return
engineStreamActor.send({
type: EngineStreamTransition.SetVideoRef,
videoRef: { current: videoRef.current },
})
}, [videoRef.current])
}, [videoRef.current, engineStreamState])
useEffect(() => {
if (engineStreamState.context.canvasRef.current !== null) return
engineStreamActor.send({
type: EngineStreamTransition.SetCanvasRef,
canvasRef: { current: canvasRef.current },
})
}, [canvasRef.current])
}, [canvasRef.current, engineStreamState])
useEffect(() => {
engineStreamActor.send({
@ -131,24 +135,6 @@ export const EngineStream = (props: {
})
}
useEffect(() => {
// Only try to start the stream if we're stopped or think we're done
// waiting for dependencies.
if (
!(
engineStreamState.value === EngineStreamState.WaitingForDependencies ||
engineStreamState.value === EngineStreamState.Stopped
)
)
return
// Don't bother trying to connect if the auth token is empty.
// We have the checks in the machine but this can cause a hot loop.
if (!engineStreamState.context.authToken) return
startOrReconfigureEngine()
}, [engineStreamState, setAppState])
// I would inline this but it needs to be a function for removeEventListener.
const play = () => {
engineStreamActor.send({
@ -174,12 +160,13 @@ export const EngineStream = (props: {
console.log('scene is ready, execute kcl')
const kmp = kclManager.executeCode().catch(trap)
if (!firstPlay) return
setFirstPlay(false)
// Reset the restart timeouts
setAttemptTimes([0, TIME_1_SECOND])
console.log(firstPlay)
if (!firstPlay) return
setFirstPlay(false)
console.log('firstPlay true, zoom to fit')
kmp
.then(async () => {
@ -211,51 +198,76 @@ export const EngineStream = (props: {
// We do a back-off restart, using a fibonacci sequence, since it
// has a nice retry time curve (somewhat quick then exponential)
const attemptRestartIfNecessary = () => {
if (isRestartRequestStarting) return
setIsRestartRequestStarting(true)
// Timeout already set.
if (timeoutId) return
setTimeoutId(
setTimeout(() => {
engineStreamState.context.videoRef.current?.pause()
engineCommandManager.tearDown()
startOrReconfigureEngine()
setFirstPlay(false)
setIsRestartRequestStarting(false)
setFirstPlay(true)
setTimeoutId(undefined)
setGoRestart(false)
}, attemptTimes[0] + attemptTimes[1])
)
setAttemptTimes([attemptTimes[1], attemptTimes[0] + attemptTimes[1]])
}
// Poll that we're connected. If not, send a reset signal.
// Do not restart if we're in idle mode.
const connectionCheckIntervalId = setInterval(() => {
// SKIP DURING TESTS BECAUSE IT WILL MESS WITH REUSING THE
// ELECTRON INSTANCE.
if (isPlaywright()) {
const onOffline = () => {
if (
!(
EngineConnectionStateType.Disconnected ===
engineCommandManager.engineConnection?.state.type ||
EngineConnectionStateType.Disconnecting ===
engineCommandManager.engineConnection?.state.type
)
) {
return
}
// Don't try try to restart if we're already connected!
const hasEngineConnectionInst = engineCommandManager.engineConnection
const isDisconnected =
engineCommandManager.engineConnection?.state.type ===
EngineConnectionStateType.Disconnected
const inIdleMode = engineStreamState.value === EngineStreamState.Paused
if ((hasEngineConnectionInst && !isDisconnected) || inIdleMode) return
engineStreamActor.send({ type: EngineStreamTransition.Stop })
attemptRestartIfNecessary()
}, TIME_1_SECOND)
}
if (
!goRestart &&
engineStreamState.value === EngineStreamState.WaitingForDependencies
) {
setGoRestart(true)
}
if (goRestart && !timeoutId) {
attemptRestartIfNecessary()
}
engineCommandManager.addEventListener(
EngineCommandManagerEvents.EngineRestartRequest,
attemptRestartIfNecessary
)
return () => {
clearInterval(connectionCheckIntervalId)
engineCommandManager.addEventListener(
EngineCommandManagerEvents.Offline,
onOffline
)
return () => {
engineCommandManager.removeEventListener(
EngineCommandManagerEvents.EngineRestartRequest,
attemptRestartIfNecessary
)
engineCommandManager.removeEventListener(
EngineCommandManagerEvents.Offline,
onOffline
)
}
}, [engineStreamState, attemptTimes, isRestartRequestStarting])
}, [
engineStreamState,
attemptTimes,
goRestart,
timeoutId,
engineCommandManager.engineConnection?.state.type,
])
useEffect(() => {
// If engineStreamMachine is already reconfiguring, bail.
@ -269,7 +281,7 @@ export const EngineStream = (props: {
const canvas = engineStreamState.context.canvasRef?.current
if (!canvas) return
new ResizeObserver(() => {
const observer = new ResizeObserver(() => {
// Prevents:
// `Uncaught ResizeObserver loop completed with undelivered notifications`
window.requestAnimationFrame(() => {
@ -280,13 +292,19 @@ export const EngineStream = (props: {
if (
(Math.abs(video.width - window.innerWidth) > 4 ||
Math.abs(video.height - window.innerHeight) > 4) &&
!engineStreamState.matches(EngineStreamState.WaitingToPlay)
engineStreamState.matches(EngineStreamState.Playing)
) {
timeoutStart.current = Date.now()
startOrReconfigureEngine()
}
})
}).observe(document.body)
})
observer.observe(document.body)
return () => {
observer.disconnect()
}
}, [engineStreamState.value])
/**
@ -345,8 +363,21 @@ export const EngineStream = (props: {
timeoutStart.current = null
} else if (timeoutStart.current) {
const elapsed = Date.now() - timeoutStart.current
if (elapsed >= IDLE_TIME_MS) {
// Don't pause if we're already disconnected.
if (
// It's unnecessary to once again setup an event listener for
// offline/online to capture this state, when this state already
// exists on the window.navigator object. In hindsight it makes
// me (lee) regret we set React state variables such as
// isInternetConnected in other files when we could check this
// object instead.
engineCommandManager.engineConnection?.state.type ===
EngineConnectionStateType.ConnectionEstablished &&
elapsed >= IDLE_TIME_MS &&
engineStreamState.value === EngineStreamState.Playing
) {
timeoutStart.current = null
console.log('PAUSING')
engineStreamActor.send({ type: EngineStreamTransition.Pause })
}
}
@ -357,7 +388,7 @@ export const EngineStream = (props: {
return () => {
window.cancelAnimationFrame(frameId)
}
}, [modelingMachineState])
}, [modelingMachineState, engineStreamState.value])
useEffect(() => {
if (!streamIdleMode) return
@ -370,9 +401,18 @@ export const EngineStream = (props: {
return
}
if (engineStreamState.value === EngineStreamState.Paused) {
startOrReconfigureEngine()
}
engineStreamActor.send({
type: EngineStreamTransition.Resume,
modelingMachineActorSend,
settings: settingsEngine,
setAppState,
onMediaStream(mediaStream: MediaStream) {
engineStreamActor.send({
type: EngineStreamTransition.SetMediaStream,
mediaStream,
})
},
})
timeoutStart.current = Date.now()
}
@ -471,7 +511,11 @@ export const EngineStream = (props: {
}
sendSelectEventToEngine(e)
.then(({ entity_id }) => {
.then((result) => {
if (!result) {
return
}
const { entity_id } = result
if (!entity_id) {
// No entity selected. This is benign
return

View File

@ -21,7 +21,6 @@ import {
filterOperations,
getOperationIcon,
getOperationLabel,
getOperationVariableName,
stdLibMap,
} from '@src/lib/operations'
import { editorManager, kclManager, rustContext } from '@src/lib/singletons'
@ -237,7 +236,6 @@ const VisibilityToggle = (props: VisibilityToggleProps) => {
const OperationItemWrapper = ({
icon,
name,
variableName,
visibilityToggle,
menuItems,
errors,
@ -248,7 +246,6 @@ const OperationItemWrapper = ({
}: React.HTMLAttributes<HTMLButtonElement> & {
icon: CustomIconName
name: string
variableName?: string
visibilityToggle?: VisibilityToggleProps
customSuffix?: JSX.Element
menuItems?: ComponentProps<typeof ContextMenu>['items']
@ -267,15 +264,8 @@ const OperationItemWrapper = ({
className={`reset flex-1 flex items-center gap-2 text-left text-base ${selectable ? 'border-transparent dark:border-transparent' : 'border-none cursor-default'} ${className}`}
>
<CustomIcon name={icon} className="w-5 h-5 block" />
<div className="flex items-baseline align-baseline">
<div className="mr-2">
{name}
{variableName && (
<span className="ml-2 opacity-50 text-[11px] font-semibold">
{variableName}
</span>
)}
</div>
<div className="flex items-baseline">
<div className="mr-2">{name}</div>
{customSuffix && customSuffix}
</div>
</button>
@ -300,11 +290,6 @@ const OperationItem = (props: {
}) => {
const kclContext = useKclContext()
const name = getOperationLabel(props.item)
const variableName = useMemo(() => {
return getOperationVariableName(props.item, kclContext.ast)
}, [props.item, kclContext.ast])
const errors = useMemo(() => {
return kclContext.diagnostics.filter(
(diag) =>
@ -514,7 +499,6 @@ const OperationItem = (props: {
<OperationItemWrapper
icon={getOperationIcon(props.item)}
name={name}
variableName={variableName}
menuItems={menuItems}
onClick={selectOperation}
onDoubleClick={enterEditFlow}

View File

@ -78,18 +78,19 @@ export function useNetworkStatus() {
}, [hasIssues, internetConnected, ping])
useEffect(() => {
const onlineCallback = () => {
setInternetConnected(true)
}
const offlineCallback = () => {
setInternetConnected(false)
setSteps(structuredClone(initialConnectingTypeGroupState))
}
window.addEventListener('online', onlineCallback)
window.addEventListener('offline', offlineCallback)
engineCommandManager.addEventListener(
EngineCommandManagerEvents.Offline,
offlineCallback
)
return () => {
window.removeEventListener('online', onlineCallback)
window.removeEventListener('offline', offlineCallback)
engineCommandManager.removeEventListener(
EngineCommandManagerEvents.Offline,
offlineCallback
)
}
}, [])
@ -139,6 +140,8 @@ export function useNetworkStatus() {
if (
engineConnectionState.type === EngineConnectionStateType.Connecting
) {
setInternetConnected(true)
const groups = Object.values(nextSteps)
for (let group of groups) {
for (let step of group) {
@ -168,6 +171,10 @@ export function useNetworkStatus() {
if (engineConnectionState.value.type === DisconnectingType.Error) {
setError(engineConnectionState.value.value)
} else if (
engineConnectionState.value.type === DisconnectingType.Quit
) {
return structuredClone(initialConnectingTypeGroupState)
}
}
}

View File

@ -24,7 +24,6 @@ export type ToolTip =
| 'yLineTo'
| 'angledLineThatIntersects'
| 'tangentialArc'
| 'tangentialArcTo'
| 'circle'
| 'circleThreePoint'
| 'arcTo'
@ -45,7 +44,6 @@ export const toolTips: Array<ToolTip> = [
'yLineTo',
'angledLineThatIntersects',
'tangentialArc',
'tangentialArcTo',
'circleThreePoint',
'arc',
'arcTo',

View File

@ -1,4 +1,3 @@
import { TEST } from '@src/env'
import type { Models } from '@kittycad/lib'
import { VITE_KC_API_WS_MODELING_URL, VITE_KC_DEV_TOKEN } from '@src/env'
import { jsAppSettings } from '@src/lib/settings/settingsUtils'
@ -83,6 +82,9 @@ export enum ConnectionError {
TooManyConnections,
Outage,
// Observed to happen on a local network outage.
PeerConnectionRemoteDisconnected,
// An unknown error is the most severe because it has not been classified
// or encountered before.
Unknown,
@ -107,6 +109,8 @@ export const CONNECTION_ERROR_TEXT: Record<ConnectionError, string> = {
'There are too many open engine connections associated with your account.',
[ConnectionError.Outage]:
'We seem to be experiencing an outage. Please visit [status.zoo.dev](https://status.zoo.dev) for updates.',
[ConnectionError.PeerConnectionRemoteDisconnected]:
'The remote end has disconnected.',
[ConnectionError.Unknown]:
'An unexpected error occurred. Please report this to us.',
}
@ -226,6 +230,9 @@ export enum EngineConnectionEvents {
Opened = 'opened', // (engineConnection: EngineConnection) => void
Closed = 'closed', // (engineConnection: EngineConnection) => void
NewTrack = 'new-track', // (track: NewTrackArgs) => void
// A general offline state.
Offline = 'offline',
}
function toRTCSessionDescriptionInit(
@ -674,9 +681,20 @@ class EngineConnection extends EventTarget {
// The remote end broke up with us! :(
case 'disconnected':
this.state = {
type: EngineConnectionStateType.Disconnecting,
value: {
type: DisconnectingType.Error,
value: {
error: ConnectionError.PeerConnectionRemoteDisconnected,
context: event,
},
},
}
this.dispatchEvent(
new CustomEvent(EngineConnectionEvents.RestartRequest, {})
new CustomEvent(EngineConnectionEvents.Offline, {})
)
this.disconnectAll()
break
case 'closed':
this.pc?.removeEventListener('icecandidate', this.onIceCandidate)
@ -847,7 +865,6 @@ class EngineConnection extends EventTarget {
'message',
this.onDataChannelMessage
)
this.disconnectAll()
}
this.unreliableDataChannel?.addEventListener(
@ -866,7 +883,6 @@ class EngineConnection extends EventTarget {
},
},
}
this.disconnectAll()
}
this.unreliableDataChannel?.addEventListener(
'error',
@ -956,6 +972,9 @@ class EngineConnection extends EventTarget {
this.onNetworkStatusReady
)
this.dispatchEvent(
new CustomEvent(EngineConnectionEvents.Offline, {})
)
this.disconnectAll()
}
this.websocket.addEventListener('close', this.onWebSocketClose)
@ -974,8 +993,6 @@ class EngineConnection extends EventTarget {
},
}
}
this.disconnectAll()
}
this.websocket.addEventListener('error', this.onWebSocketError)
@ -1331,6 +1348,9 @@ export enum EngineCommandManagerEvents {
// the whole scene is ready (settings loaded)
SceneReady = 'scene-ready',
// we're offline
Offline = 'offline',
}
/**
@ -1380,6 +1400,7 @@ export class EngineCommandManager extends EventTarget {
* This is compared to the {@link outSequence} number to determine if we should ignore
* any out-of-order late responses in the unreliable channel.
*/
keepForcefulOffline = false
inSequence = 1
engineConnection?: EngineConnection
commandLogs: CommandLog[] = []
@ -1453,13 +1474,8 @@ export class EngineCommandManager extends EventTarget {
)
}
private onOffline = () => {
console.log('Browser reported network is offline')
if (TEST) {
console.warn('DURING TESTS ENGINECONNECTION.ONOFFLINE WILL DO NOTHING.')
return
}
this.onEngineConnectionRestartRequest()
private onEngineOffline = () => {
this.dispatchEvent(new CustomEvent(EngineCommandManagerEvents.Offline, {}))
}
idleMode: boolean = false
@ -1494,6 +1510,11 @@ export class EngineCommandManager extends EventTarget {
if (settings) {
this.settings = settings
}
if (this.keepForcefulOffline) {
return
}
if (width === 0 || height === 0) {
return
}
@ -1509,8 +1530,6 @@ export class EngineCommandManager extends EventTarget {
return
}
window.addEventListener('offline', this.onOffline)
let additionalSettings = this.settings.enableSSAO ? '&post_effect=ssao' : ''
additionalSettings +=
'&show_grid=' + (this.settings.showScaleGrid ? 'true' : 'false')
@ -1537,6 +1556,11 @@ export class EngineCommandManager extends EventTarget {
this.onEngineConnectionRestartRequest as EventListener
)
this.engineConnection.addEventListener(
EngineConnectionEvents.Offline,
this.onEngineOffline as EventListener
)
// eslint-disable-next-line @typescript-eslint/no-misused-promises
this.onEngineConnectionOpened = async () => {
console.log('onEngineConnectionOpened')
@ -1552,9 +1576,9 @@ export class EngineCommandManager extends EventTarget {
// Let's restart.
console.warn("shit's gone south")
console.warn(e)
this.engineConnection?.dispatchEvent(
new CustomEvent(EngineConnectionEvents.RestartRequest, {})
)
// this.engineConnection?.dispatchEvent(
// new CustomEvent(EngineConnectionEvents.RestartRequest, {})
// )
return
}
@ -1597,23 +1621,7 @@ export class EngineCommandManager extends EventTarget {
console.log('camControlsCameraChange')
this._camControlsCameraChange()
// We should eventually only have 1 restoral call.
if (this.idleMode) {
await this.sceneInfra?.camControls.restoreRemoteCameraStateAndTriggerSync()
} else {
// NOTE: This code is old. It uses the old hack to restore camera.
console.log('call default_camera_get_settings')
// eslint-disable-next-line @typescript-eslint/no-floating-promises
await this.sendSceneCommand({
// CameraControls subscribes to default_camera_get_settings response events
// firing this at connection ensure the camera's are synced initially
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
})
}
setIsStreamReady(true)
@ -1877,8 +1885,6 @@ export class EngineCommandManager extends EventTarget {
tearDown(opts?: { idleMode: boolean }) {
this.idleMode = opts?.idleMode ?? false
window.removeEventListener('offline', this.onOffline)
if (this.engineConnection) {
for (const [cmdId, pending] of Object.entries(this.pendingCommands)) {
pending.reject([
@ -1928,7 +1934,26 @@ export class EngineCommandManager extends EventTarget {
this.engineCommandManager.engineConnection = null
}
this.engineConnection = undefined
// It is possible all connections never even started, but we still want
// to signal to the whole application we are "offline".
this.dispatchEvent(new CustomEvent(EngineCommandManagerEvents.Offline, {}))
}
offline() {
this.keepForcefulOffline = true
this.tearDown()
console.log('offline')
}
online() {
this.keepForcefulOffline = false
this.dispatchEvent(
new CustomEvent(EngineCommandManagerEvents.EngineRestartRequest, {})
)
console.log('online')
}
async startNewSession() {
this.responseMap = {}
}

View File

@ -1018,61 +1018,217 @@ export const yLine: SketchLineHelperKw = {
export const tangentialArc: SketchLineHelperKw = {
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
return tangentialArcHelpers.add({
node,
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR()
const { to } = segmentInput
const _node = { ...node }
const getNode = getNodeFromPathCurry(_node, pathToNode)
const _node1 = getNode<PipeExpression | CallExpressionKw>('PipeExpression')
if (err(_node1)) return _node1
const { node: pipe } = _node1
const _node2 = getNodeFromPath<VariableDeclarator>(
_node,
pathToNode,
segmentInput,
replaceExistingCallback,
isAbsolute: false,
})
},
updateArgs: ({ node, pathToNode, input }) => {
return tangentialArcHelpers.update({
node,
pathToNode,
input,
isAbsolute: false,
})
},
getTag: getTagKwArg(),
addTag: addTagKw(),
getConstraintInfo: (callExp: CallExpressionKw, code, pathToNode) => {
return tangentialArcHelpers.getConstraintInfo({
callExp,
code,
pathToNode,
isAbsolute: false,
})
},
}
'VariableDeclarator'
)
if (err(_node2)) return _node2
const { node: varDec } = _node2
export const tangentialArcTo: SketchLineHelperKw = {
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
return tangentialArcHelpers.add({
node,
const toX = createLiteral(roundOff(to[0], 2))
const toY = createLiteral(roundOff(to[1], 2))
if (replaceExistingCallback && pipe.type !== 'CallExpressionKw') {
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
const result = replaceExistingCallback([
{
type: 'labeledArgArrayItem',
key: ARG_END_ABSOLUTE,
index: 0,
argType: 'xAbsolute',
expr: toX,
},
{
type: 'labeledArgArrayItem',
key: ARG_END_ABSOLUTE,
index: 1,
argType: 'yAbsolute',
expr: toY,
},
])
if (err(result)) return result
const { callExp, valueUsedInTransform } = result
pipe.body[callIndex] = callExp
return {
modifiedAst: _node,
pathToNode,
segmentInput,
replaceExistingCallback,
isAbsolute: true,
})
valueUsedInTransform,
}
}
const newLine = createCallExpressionStdLibKw(
'tangentialArc',
null, // Assumes this is being called in a pipeline, so the first arg is optional and if not given, will become pipeline substitution.
[createLabeledArg(ARG_END_ABSOLUTE, createArrayExpression([toX, toY]))]
)
if (pipe.type === 'PipeExpression') {
pipe.body = [...pipe.body, newLine]
return {
modifiedAst: _node,
pathToNode: [
...pathToNode.slice(
0,
pathToNode.findIndex(([_, type]) => type === 'PipeExpression') + 1
),
['body', 'PipeExpression'],
[pipe.body.length - 1, 'CallExpressionKw'],
],
}
} else {
varDec.init = createPipeExpression([varDec.init, newLine])
}
return {
modifiedAst: _node,
pathToNode,
}
},
updateArgs: ({ node, pathToNode, input }) => {
return tangentialArcHelpers.update({
node,
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR()
const { to } = input
const _node = { ...node }
const nodeMeta = getNodeFromPath<CallExpressionKw>(_node, pathToNode)
if (err(nodeMeta)) return nodeMeta
const { node: callExpression } = nodeMeta
if (callExpression.type !== 'CallExpressionKw') {
return new Error(
`Expected CallExpressionKw, but found ${callExpression.type}`
)
}
for (const arg of callExpression.arguments) {
if (arg.label?.name !== ARG_END_ABSOLUTE && arg.label?.name !== ARG_TAG) {
console.debug(
'Trying to edit unsupported tangentialArc keyword arguments; skipping'
)
return {
modifiedAst: _node,
pathToNode,
input,
isAbsolute: true,
})
}
}
}
const toArrExp = createArrayExpression([
createLiteral(roundOff(to[0], 2)),
createLiteral(roundOff(to[1], 2)),
])
mutateKwArg(ARG_END_ABSOLUTE, callExpression, toArrExp)
return {
modifiedAst: _node,
pathToNode,
}
},
getTag: getTagKwArg(),
addTag: addTagKw(),
getConstraintInfo: (callExp: CallExpressionKw, code, pathToNode) => {
return tangentialArcHelpers.getConstraintInfo({
callExp,
code,
pathToNode,
isAbsolute: true,
if (callExp.type !== 'CallExpressionKw') return []
if (callExp.callee.name.name !== 'tangentialArc') return []
const callee = callExp.callee
const pathToCallee: PathToNode = [
...pathToNode,
['callee', 'CallExpressionKw'],
]
const endAbsoluteArg = findKwArgWithIndex(ARG_END_ABSOLUTE, callExp)
const constraints: ConstrainInfo[] = [
constrainInfo(
'tangentialWithPrevious',
true,
callee.name.name,
'tangentialArc',
undefined,
topLevelRange(callee.start, callee.end),
pathToCallee
),
]
if (endAbsoluteArg) {
const { expr, argIndex } = endAbsoluteArg
const pathToArgs: PathToNode = [
...pathToNode,
['arguments', 'CallExpressionKw'],
]
const pathToArg: PathToNode = [
...pathToArgs,
[argIndex, ARG_INDEX_FIELD],
['arg', LABELED_ARG_FIELD],
]
if (expr.type !== 'ArrayExpression' || expr.elements.length < 2) {
constraints.push({
stdLibFnName: 'tangentialArc',
type: 'xAbsolute',
isConstrained: isNotLiteralArrayOrStatic(expr),
sourceRange: topLevelRange(expr.start, expr.end),
pathToNode: pathToArg,
value: code.slice(expr.start, expr.end),
argPosition: {
type: 'labeledArgArrayItem',
index: 0,
key: ARG_END_ABSOLUTE,
},
})
constraints.push({
stdLibFnName: 'tangentialArc',
type: 'yAbsolute',
isConstrained: isNotLiteralArrayOrStatic(expr),
sourceRange: topLevelRange(expr.start, expr.end),
pathToNode: pathToArg,
value: code.slice(expr.start, expr.end),
argPosition: {
type: 'labeledArgArrayItem',
index: 1,
key: ARG_END_ABSOLUTE,
},
})
return constraints
}
const pathToX: PathToNode = [
...pathToArg,
['elements', 'ArrayExpression'],
[0, 'index'],
]
const pathToY: PathToNode = [
...pathToArg,
['elements', 'ArrayExpression'],
[1, 'index'],
]
const exprX = expr.elements[0]
const exprY = expr.elements[1]
constraints.push({
stdLibFnName: 'tangentialArc',
type: 'xAbsolute',
isConstrained: isNotLiteralArrayOrStatic(exprX),
sourceRange: topLevelRange(exprX.start, exprX.end),
pathToNode: pathToX,
value: code.slice(exprX.start, exprX.end),
argPosition: {
type: 'labeledArgArrayItem',
index: 0,
key: ARG_END_ABSOLUTE,
},
})
constraints.push({
stdLibFnName: 'tangentialArc',
type: 'yAbsolute',
isConstrained: isNotLiteralArrayOrStatic(exprY),
sourceRange: topLevelRange(exprY.start, exprY.end),
pathToNode: pathToY,
value: code.slice(exprY.start, exprY.end),
argPosition: {
type: 'labeledArgArrayItem',
index: 1,
key: ARG_END_ABSOLUTE,
},
})
}
return constraints
},
}
@ -3155,7 +3311,6 @@ export const sketchLineHelperMapKw: { [key: string]: SketchLineHelperKw } = {
angledLineToX,
angledLineToY,
tangentialArc,
tangentialArcTo,
startProfile,
} as const
@ -3231,7 +3386,6 @@ export function fnNameToToolTipFromSegment(
fnName: string
): ToolTip | Error {
switch (fnName) {
case 'arcTo':
case 'arc': {
return seg.type === 'ArcThreePoint' ? 'arcTo' : 'arc'
}
@ -3249,7 +3403,6 @@ export function fnNameToToolTipFromSegment(
case 'circleThreePoint':
case 'circle':
case 'tangentialArc':
case 'tangentialArcTo':
case 'angledLine':
case 'startProfile':
case 'arcTo':
@ -3273,7 +3426,8 @@ export function fnNameToTooltip(
argLabels: string[],
fnName: string
): ToolTip | Error {
const isAbsolute = argLabels.some((label) => label === ARG_END_ABSOLUTE)
const isAbsolute =
argLabels.findIndex((label) => label === ARG_END_ABSOLUTE) >= 0
switch (fnName) {
case 'arc': {
const isArc = argLabels.some((label) =>
@ -3287,11 +3441,10 @@ export function fnNameToTooltip(
return isAbsolute ? 'xLineTo' : 'xLine'
case 'yLine':
return isAbsolute ? 'yLineTo' : 'yLine'
case 'tangentialArc':
return isAbsolute ? 'tangentialArcTo' : 'tangentialArc'
case 'angledLineThatIntersects':
case 'circleThreePoint':
case 'circle':
case 'tangentialArc':
case 'startProfile':
return fnName
case 'angledLine': {
@ -3332,7 +3485,6 @@ export function tooltipToFnName(tooltip: ToolTip): string | Error {
case 'xLine':
case 'yLine':
case 'line':
case 'tangentialArc':
return tooltip
case 'lineTo':
return 'line'
@ -3340,8 +3492,6 @@ export function tooltipToFnName(tooltip: ToolTip): string | Error {
return 'xLine'
case 'yLineTo':
return 'yLine'
case 'tangentialArcTo':
return 'tangentialArc'
case 'angledLine':
case 'angledLineToX':
case 'angledLineToY':
@ -3949,6 +4099,7 @@ export function isAbsoluteLine(lineCall: CallExpressionKw): boolean | Error {
const name = lineCall?.callee?.name.name
switch (name) {
case 'line':
case 'tangentialArc':
if (findKwArg(ARG_END, lineCall) !== undefined) {
return false
}
@ -3969,8 +4120,6 @@ export function isAbsoluteLine(lineCall: CallExpressionKw): boolean | Error {
return new Error(
`${name} call has neither ${ARG_END} nor ${ARG_END_ABSOLUTE} params`
)
case 'tangentialArc':
return findKwArg(ARG_END_ABSOLUTE, lineCall) !== undefined
case 'angledLineThatIntersects':
case 'arc':
case 'circle':
@ -4002,7 +4151,6 @@ export function getArgForEnd(lineCall: CallExpressionKw):
switch (name) {
case 'circle':
return getCircle(lineCall)
case 'tangentialArc':
case 'line': {
const arg = findKwArgAny(DETERMINING_ARGS, lineCall)
if (arg === undefined) {
@ -4059,264 +4207,3 @@ export function getArgForEnd(lineCall: CallExpressionKw):
function removeDeterminingArgs(callExp: CallExpressionKw) {
removeKwArgs(DETERMINING_ARGS, callExp)
}
const tangentialArcHelpers = {
add: ({
node,
pathToNode,
segmentInput,
replaceExistingCallback,
isAbsolute = false,
}: {
node: Node<Program>
pathToNode: PathToNode
segmentInput: SegmentInputs
replaceExistingCallback?: (
rawArgs: RawArgs
) => CreatedSketchExprResult | Error
isAbsolute?: boolean
}) => {
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR()
const { to, from } = segmentInput
const _node = { ...node }
const getNode = getNodeFromPathCurry(_node, pathToNode)
const _node1 = getNode<PipeExpression | CallExpressionKw>('PipeExpression')
if (err(_node1)) return _node1
const { node: pipe } = _node1
const _node2 = getNodeFromPath<VariableDeclarator>(
_node,
pathToNode,
'VariableDeclarator'
)
if (err(_node2)) return _node2
const { node: varDec } = _node2
const toX = createLiteral(roundOff(isAbsolute ? to[0] : to[0] - from[0], 2))
const toY = createLiteral(roundOff(isAbsolute ? to[1] : to[1] - from[1], 2))
const argLabel = isAbsolute ? ARG_END_ABSOLUTE : ARG_END
const xArgType = isAbsolute ? 'xAbsolute' : 'xRelative'
const yArgType = isAbsolute ? 'yAbsolute' : 'yRelative'
if (replaceExistingCallback && pipe.type !== 'CallExpressionKw') {
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
const result = replaceExistingCallback([
{
type: 'labeledArgArrayItem',
key: argLabel,
index: 0,
argType: xArgType,
expr: toX,
},
{
type: 'labeledArgArrayItem',
key: argLabel,
index: 1,
argType: yArgType,
expr: toY,
},
])
if (err(result)) return result
const { callExp, valueUsedInTransform } = result
pipe.body[callIndex] = callExp
return {
modifiedAst: _node,
pathToNode,
valueUsedInTransform,
}
}
const newLine = createCallExpressionStdLibKw(
'tangentialArc',
null, // Assumes this is being called in a pipeline, so the first arg is optional and if not given, will become pipeline substitution.
[createLabeledArg(argLabel, createArrayExpression([toX, toY]))]
)
if (pipe.type === 'PipeExpression') {
pipe.body = [...pipe.body, newLine]
return {
modifiedAst: _node,
pathToNode: [
...pathToNode.slice(
0,
pathToNode.findIndex(([_, type]) => type === 'PipeExpression') + 1
),
['body', 'PipeExpression'],
[pipe.body.length - 1, 'CallExpressionKw'],
] as PathToNode,
}
} else {
varDec.init = createPipeExpression([varDec.init, newLine])
}
return {
modifiedAst: _node,
pathToNode,
}
},
update: ({
node,
pathToNode,
input,
isAbsolute = false,
}: {
node: Node<Program>
pathToNode: PathToNode
input: SegmentInputs
isAbsolute?: boolean
}) => {
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR()
const { to, from } = input
const _node = { ...node }
const nodeMeta = getNodeFromPath<CallExpressionKw>(_node, pathToNode)
if (err(nodeMeta)) return nodeMeta
const { node: callExpression } = nodeMeta
if (callExpression.type !== 'CallExpressionKw') {
return new Error(
`Expected CallExpressionKw, but found ${callExpression.type}`
)
}
const argLabel = isAbsolute ? ARG_END_ABSOLUTE : ARG_END
const functionName = isAbsolute ? 'tangentialArcTo' : 'tangentialArc'
for (const arg of callExpression.arguments) {
if (arg.label?.name !== argLabel && arg.label?.name !== ARG_TAG) {
console.debug(
`Trying to edit unsupported ${functionName} keyword arguments; skipping`
)
return {
modifiedAst: _node,
pathToNode,
}
}
}
const toArrExp = createArrayExpression([
createLiteral(roundOff(isAbsolute ? to[0] : to[0] - from[0], 2)),
createLiteral(roundOff(isAbsolute ? to[1] : to[1] - from[1], 2)),
])
mutateKwArg(argLabel, callExpression, toArrExp)
return {
modifiedAst: _node,
pathToNode,
}
},
getConstraintInfo: ({
callExp,
code,
pathToNode,
isAbsolute = false,
}: {
callExp: CallExpressionKw
code: string
pathToNode: PathToNode
isAbsolute?: boolean
}): ConstrainInfo[] => {
if (callExp.type !== 'CallExpressionKw') return []
if (callExp.callee.name.name !== 'tangentialArc') return []
const callee = callExp.callee
const pathToCallee: PathToNode = [
...pathToNode,
['callee', 'CallExpressionKw'],
]
const argLabel = isAbsolute ? ARG_END_ABSOLUTE : ARG_END
const xConstraintType = isAbsolute ? 'xAbsolute' : 'xRelative'
const yConstraintType = isAbsolute ? 'yAbsolute' : 'yRelative'
const endArg = findKwArgWithIndex(argLabel, callExp)
const constraints: ConstrainInfo[] = [
constrainInfo(
'tangentialWithPrevious',
true,
callee.name.name,
'tangentialArc',
undefined,
topLevelRange(callee.start, callee.end),
pathToCallee
),
]
if (endArg) {
const { expr, argIndex } = endArg
const pathToArgs: PathToNode = [
...pathToNode,
['arguments', 'CallExpressionKw'],
]
const pathToArg: PathToNode = [
...pathToArgs,
[argIndex, ARG_INDEX_FIELD],
['arg', LABELED_ARG_FIELD],
]
if (expr.type !== 'ArrayExpression' || expr.elements.length < 2) {
constraints.push({
stdLibFnName: 'tangentialArc',
type: xConstraintType,
isConstrained: isNotLiteralArrayOrStatic(expr),
sourceRange: topLevelRange(expr.start, expr.end),
pathToNode: pathToArg,
value: code.slice(expr.start, expr.end),
argPosition: {
type: 'labeledArgArrayItem',
index: 0,
key: argLabel,
},
})
constraints.push({
stdLibFnName: 'tangentialArc',
type: yConstraintType,
isConstrained: isNotLiteralArrayOrStatic(expr),
sourceRange: topLevelRange(expr.start, expr.end),
pathToNode: pathToArg,
value: code.slice(expr.start, expr.end),
argPosition: {
type: 'labeledArgArrayItem',
index: 1,
key: argLabel,
},
})
return constraints
}
const pathToX: PathToNode = [
...pathToArg,
['elements', 'ArrayExpression'],
[0, 'index'],
]
const pathToY: PathToNode = [
...pathToArg,
['elements', 'ArrayExpression'],
[1, 'index'],
]
const exprX = expr.elements[0]
const exprY = expr.elements[1]
constraints.push({
stdLibFnName: 'tangentialArc',
type: xConstraintType,
isConstrained: isNotLiteralArrayOrStatic(exprX),
sourceRange: topLevelRange(exprX.start, exprX.end),
pathToNode: pathToX,
value: code.slice(exprX.start, exprX.end),
argPosition: {
type: 'labeledArgArrayItem',
index: 0,
key: argLabel,
},
})
constraints.push({
stdLibFnName: 'tangentialArc',
type: yConstraintType,
isConstrained: isNotLiteralArrayOrStatic(exprY),
sourceRange: topLevelRange(exprY.start, exprY.end),
pathToNode: pathToY,
value: code.slice(exprY.start, exprY.end),
argPosition: {
type: 'labeledArgArrayItem',
index: 1,
key: argLabel,
},
})
}
return constraints
},
}

View File

@ -1502,14 +1502,16 @@ export function removeSingleConstraint({
const literal = rawArg?.overrideExpr ?? rawArg?.expr
return (arg.index === inputToReplace.index && literal) || argExpr
})
// It's a kw call.
const isAbsolute = inputs.some((input) => input.argType === 'xAbsolute')
const isAbsolute = callExp.node.callee.name.name == 'lineTo'
if (isAbsolute) {
const args = [
createLabeledArg(
isAbsolute ? ARG_END_ABSOLUTE : ARG_END,
createArrayExpression(values)
),
createLabeledArg(ARG_END_ABSOLUTE, createArrayExpression(values)),
]
return createStdlibCallExpressionKw('line', args, tag)
} else {
const args = [
createLabeledArg(ARG_END, createArrayExpression(values)),
]
return createStdlibCallExpressionKw(
callExp.node.callee.name.name as ToolTip,
@ -1517,6 +1519,7 @@ export function removeSingleConstraint({
tag
)
}
}
if (
inputToReplace.type === 'arrayInObject' ||
inputToReplace.type === 'objectProperty'

View File

@ -1,12 +1,7 @@
import type { Operation } from '@rust/kcl-lib/bindings/Operation'
import { topLevelRange } from '@src/lang/util'
import {
assertParse,
defaultSourceRange,
type SourceRange,
} from '@src/lang/wasm'
import { filterOperations, getOperationVariableName } from '@src/lib/operations'
import { defaultSourceRange } from '@src/lang/wasm'
import { filterOperations } from '@src/lib/operations'
function stdlib(name: string): Operation {
return {
@ -167,75 +162,3 @@ describe('operations filtering', () => {
])
})
})
function rangeOfText(fullCode: string, target: string): SourceRange {
const start = fullCode.indexOf(target)
if (start === -1) {
throw new Error(`Could not find \`${target}\` in: ${fullCode}`)
}
return topLevelRange(start, start + target.length)
}
describe('variable name of operations', () => {
it('finds the variable name with simple assignment', async () => {
const op = stdlib('stdLibFn')
if (op.type !== 'StdLibCall') {
throw new Error('Expected operation to be a StdLibCall')
}
const code = `myVar = stdLibFn()`
// Make the source range match the code.
op.sourceRange = rangeOfText(code, 'stdLibFn()')
const program = assertParse(code)
const variableName = getOperationVariableName(op, program)
expect(variableName).toBe('myVar')
})
it('finds the variable name inside a function with simple assignment', async () => {
const op = stdlib('stdLibFn')
if (op.type !== 'StdLibCall') {
throw new Error('Expected operation to be a StdLibCall')
}
const code = `fn myFunc() {
myVar = stdLibFn()
return 0
}
`
// Make the source range match the code.
op.sourceRange = rangeOfText(code, 'stdLibFn()')
const program = assertParse(code)
const variableName = getOperationVariableName(op, program)
expect(variableName).toBe('myVar')
})
it("finds the variable name when it's the last in a pipeline", async () => {
const op = stdlib('stdLibFn')
if (op.type !== 'StdLibCall') {
throw new Error('Expected operation to be a StdLibCall')
}
const code = `myVar = foo()
|> stdLibFn()
`
// Make the source range match the code.
op.sourceRange = rangeOfText(code, 'stdLibFn()')
const program = assertParse(code)
const variableName = getOperationVariableName(op, program)
expect(variableName).toBe('myVar')
})
it("finds nothing when it's not the last in a pipeline", async () => {
const op = stdlib('stdLibFn')
if (op.type !== 'StdLibCall') {
throw new Error('Expected operation to be a StdLibCall')
}
const code = `myVar = foo()
|> stdLibFn()
|> bar()
`
// Make the source range match the code.
op.sourceRange = rangeOfText(code, 'stdLibFn()')
const program = assertParse(code)
const variableName = getOperationVariableName(op, program)
expect(variableName).toBeUndefined()
})
})

View File

@ -15,13 +15,7 @@ import {
getSweepEdgeCodeRef,
getWallCodeRef,
} from '@src/lang/std/artifactGraph'
import {
type CallExpressionKw,
type PipeExpression,
type Program,
sourceRangeFromRust,
type VariableDeclaration,
} from '@src/lang/wasm'
import { type PipeExpression, sourceRangeFromRust } from '@src/lang/wasm'
import type {
HelixModes,
ModelingCommandSchema,
@ -1080,71 +1074,6 @@ export function getOperationIcon(op: Operation): CustomIconName {
}
}
/**
* If the result of the operation is assigned to a variable, returns the
* variable name.
*/
export function getOperationVariableName(
op: Operation,
program: Program
): string | undefined {
if (
op.type !== 'StdLibCall' &&
!(op.type === 'GroupBegin' && op.group.type === 'FunctionCall')
) {
return undefined
}
// Find the AST node.
const range = sourceRangeFromRust(op.sourceRange)
const pathToNode = getNodePathFromSourceRange(program, range)
if (pathToNode.length === 0) {
return undefined
}
const call = getNodeFromPath<CallExpressionKw>(
program,
pathToNode,
'CallExpressionKw'
)
if (err(call) || call.node.type !== 'CallExpressionKw') {
return undefined
}
// Find the var name from the variable declaration.
const varDec = getNodeFromPath<VariableDeclaration>(
program,
pathToNode,
'VariableDeclaration'
)
if (err(varDec)) {
return undefined
}
if (varDec.node.type !== 'VariableDeclaration') {
// There's no variable declaration for this call.
return undefined
}
const varName = varDec.node.declaration.id.name
// If the operation is a simple assignment, we can use the variable name.
if (varDec.node.declaration.init === call.node) {
return varName
}
// If the AST node is in a pipe expression, we can only use the variable
// name if it's the last operation in the pipe.
const pipe = getNodeFromPath<PipeExpression>(
program,
pathToNode,
'PipeExpression'
)
if (err(pipe)) {
return undefined
}
if (
pipe.node.type === 'PipeExpression' &&
pipe.node.body[pipe.node.body.length - 1] === call.node
) {
return varName
}
return undefined
}
/**
* Apply all filters to a list of operations.
*/

View File

@ -703,7 +703,8 @@ export async function sendSelectEventToEngine(
cmd_id: uuidv4(),
})
if (!res) {
return Promise.reject('no response')
console.warn('No response')
return undefined
}
if (isArray(res)) {

View File

@ -70,7 +70,6 @@ export async function holdOntoVideoFrameInCanvas(
video: HTMLVideoElement,
canvas: HTMLCanvasElement
) {
video.pause()
canvas.width = video.videoWidth
canvas.height = video.videoHeight
canvas.style.width = video.videoWidth + 'px'
@ -220,12 +219,15 @@ export const engineStreamMachine = setup({
if (context.videoRef.current && context.canvasRef.current) {
await context.videoRef.current.pause()
// It's possible we've already frozen the frame due to a disconnect.
if (context.videoRef.current.style.display !== 'none') {
await holdOntoVideoFrameInCanvas(
context.videoRef.current,
context.canvasRef.current
)
context.videoRef.current.style.display = 'none'
}
}
await rootContext.sceneInfra.camControls.saveRemoteCameraState()
@ -365,9 +367,12 @@ export const engineStreamMachine = setup({
}),
},
on: {
[EngineStreamTransition.StartOrReconfigureEngine]: {
[EngineStreamTransition.Resume]: {
target: EngineStreamState.Resuming,
},
[EngineStreamTransition.Stop]: {
target: EngineStreamState.Stopped,
},
},
},
[EngineStreamState.Stopped]: {
@ -398,12 +403,23 @@ export const engineStreamMachine = setup({
rootContext: args.self.system.get('root').getSnapshot().context,
event: args.event,
}),
// Usually only fails if there was a disconnection mid-way.
onError: [
{
target: EngineStreamState.WaitingForDependencies,
reenter: true,
},
],
},
on: {
// The stream can be paused as it's resuming.
[EngineStreamTransition.Pause]: {
target: EngineStreamState.Paused,
},
// The stream can be stopped as it's resuming.
[EngineStreamTransition.Stop]: {
target: EngineStreamState.Stopped,
},
[EngineStreamTransition.SetMediaStream]: {
target: EngineStreamState.Playing,
actions: [