Compare commits

...

50 Commits

Author SHA1 Message Date
c26709c06a Merge remote-tracking branch 'origin' into kurt-circle 2024-09-10 21:03:22 +10:00
fa580d4035 clean up by grouping segment labels 2024-09-10 17:30:21 +10:00
93d3df4877 more renaming 2024-09-10 17:23:11 +10:00
9fafc90c57 remove surplus | 'radius's 2024-09-10 13:23:21 +10:00
04e82bf728 comments and naming tweaks 2024-09-10 13:15:11 +10:00
0abe4c4082 some xstate v5 stuff that got missed from merge with main 2024-09-10 11:51:03 +10:00
266c601fd4 Merge remote-tracking branch 'origin' into kurt-circle 2024-09-10 11:34:42 +10:00
28a63194c7 more clean up 2024-09-10 01:49:07 +10:00
a6aff874bb more types clean up 2024-09-10 01:43:34 +10:00
25080e9895 clean up create call back double function stuff 2024-09-10 01:08:57 +10:00
99ffc82ffa change types that that effected a lot of places 2024-09-09 22:58:36 +10:00
4219a2c31d more minor clean up renaming 2024-09-09 22:38:02 +10:00
bb265ca833 few more minor renames 2024-09-09 22:10:02 +10:00
63dea715bc some renames 2024-09-09 22:04:43 +10:00
15871e6245 cargo fmt 2024-09-09 20:32:57 +10:00
7ab9b3ee46 Merge remote-tracking branch 'origin' into kurt-circle 2024-09-09 20:29:47 +10:00
e794c5b0e9 Update some circle function calls and twenty twenty stuff 2024-09-09 20:28:24 +10:00
a1dd884013 stdlib docs update 2024-09-09 19:34:12 +10:00
994f71bce5 make clippy happy 2024-09-09 19:30:51 +10:00
b55652f9b7 fix circle line thickness and handles scale correctly on zoom 2024-09-09 17:10:40 +10:00
9d6a09766c A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest) 2024-09-09 06:55:27 +00:00
840e75e5a1 fix dragging circle circumference error 2024-09-09 16:37:41 +10:00
466511ac46 A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest) 2024-09-09 16:35:54 +10:00
85abcde086 make sure behaviour is sane betwen center and radius clicks 2024-09-09 16:28:22 +10:00
48d347b4de add e2e tests 2024-09-09 16:18:39 +10:00
b3c4aec8db try increas playwright timeout 2024-09-09 14:36:00 +10:00
a59de4efa3 hopefully fix e2e tests 2024-09-09 13:43:52 +10:00
57a460f57a update samples 2024-09-09 13:23:34 +10:00
59284ffa50 rely a little less on arbitary indexs 2024-09-09 12:45:59 +10:00
5592185371 Merge remote-tracking branch 'origin' into kurt-circle 2024-09-09 12:00:32 +10:00
998b194712 fix unit tests 2024-09-09 11:52:23 +10:00
6753a9e9f8 make lint happy 2024-09-07 15:40:59 +10:00
abb8b95b88 Merge remote-tracking branch 'origin' into kurt-circle 2024-09-07 09:09:33 +10:00
94b0510521 big refactor of var values for astMods 2024-09-07 09:04:07 +10:00
72e522dd51 change circle args 2024-09-04 18:55:05 +10:00
706af591c5 Merge remote-tracking branch 'origin' into kurt-circle 2024-09-02 20:35:39 +10:00
a1f8ac4548 Merge remote-tracking branch 'origin' into kurt-circle 2024-08-29 15:05:33 +10:00
0d5a8aea93 change start end angle for draft circle so that it animates from the oposite side to the handl 2024-08-29 15:05:22 +10:00
3e4316b614 Merge remote-tracking branch 'origin' into kurt-circle 2024-08-28 20:07:37 +10:00
c6577a5ae6 remove error when circle has second click 2024-08-28 20:03:36 +10:00
379960561d fix weird tear down issue 2024-08-28 19:57:26 +10:00
8d912fa62a make sure cursor is consistent with other tool tips 2024-08-28 19:41:16 +10:00
bfd92a626b remove extra segment handle 2024-08-28 19:40:59 +10:00
44e07ca6e5 fix draft arc not being complete 2024-08-28 19:23:05 +10:00
22c854815a can use circle tool tip 2024-08-28 19:07:26 +10:00
3409aa5cfd Merge remote-tracking branch 'origin' into kurt-circle 2024-08-27 21:26:59 +10:00
c56c446e15 add circle segment to searches 2024-08-24 21:59:35 +10:00
1988888699 update is editing existing sketch 2024-08-24 21:43:39 +10:00
acfa95f728 basic circle edit working 2024-08-24 21:09:21 +10:00
b7c71c01cf xstate start 2024-08-24 20:36:07 +10:00
81 changed files with 3375 additions and 1222 deletions

View File

@ -262,7 +262,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-14]
timeout-minutes: 40
timeout-minutes: 60
runs-on: ${{ matrix.os }}
needs: check-rust-changes
steps:

View File

@ -270,6 +270,26 @@ const extrusion = extrude(5, sketch001)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -479,6 +499,26 @@ const extrusion = extrude(5, sketch001)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],

View File

@ -274,6 +274,26 @@ const extrusion = extrude(5, sketch001)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -483,6 +503,26 @@ const extrusion = extrude(5, sketch001)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],

View File

@ -189,6 +189,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -398,6 +418,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -609,6 +649,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -818,6 +878,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],

View File

@ -188,6 +188,26 @@ const extrusion = extrude(10, sketch001)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -397,6 +417,26 @@ const extrusion = extrude(10, sketch001)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -608,6 +648,26 @@ const extrusion = extrude(10, sketch001)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -817,6 +877,26 @@ const extrusion = extrude(10, sketch001)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],

View File

@ -190,6 +190,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -399,6 +419,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -610,6 +650,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -819,6 +879,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],

View File

@ -282,6 +282,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -491,6 +511,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -702,6 +742,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -911,6 +971,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],

View File

@ -187,6 +187,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -396,6 +416,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -607,6 +647,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -816,6 +876,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],

View File

@ -187,6 +187,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -396,6 +416,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -607,6 +647,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -816,6 +876,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],

View File

@ -200,6 +200,26 @@ const exampleSketch = startSketchOn('XZ')
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -409,6 +429,26 @@ const exampleSketch = startSketchOn('XZ')
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -620,6 +660,26 @@ const exampleSketch = startSketchOn('XZ')
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -829,6 +889,26 @@ const exampleSketch = startSketchOn('XZ')
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],

View File

@ -193,6 +193,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -402,6 +422,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -613,6 +653,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -822,6 +882,26 @@ const example = extrude(10, exampleSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],

View File

@ -415,6 +415,26 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],
@ -819,6 +839,26 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
to: [number, number],
type: "TangentialArc",
} |
{
// arc's direction
ccw: bool,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// the arc's radius
radius: number,
// The tag of the path.
tag: {
digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number],
end: number,
start: number,
value: string,
},
// The to point.
to: [number, number],
type: "Circle",
} |
{
// The from point.
from: [number, number],

View File

@ -558,7 +558,7 @@ test.describe('Editor tests', () => {
await page.keyboard.press('ArrowDown')
await page.keyboard.press('Enter')
await page.keyboard.type(`const extrusion = startSketchOn('XY')
|> circle([0, 0], dia/2, %)
|> circle({ center: [0, 0], radius: dia/2 }, %)
|> hole(squareHole(length, width, height), %)
|> extrude(height, %)`)

View File

@ -149,14 +149,16 @@ test.describe('Sketch tests', () => {
await page.getByRole('button', { name: 'line Line', exact: true }).click()
await page.waitForTimeout(100)
await expect(async () => {
await page.mouse.click(700, 200)
await expect.poll(u.normalisedEditorCode)
await expect.poll(u.normalisedEditorCode, { timeout: 1000 })
.toBe(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([12.34, -12.34], %)
|> line([-12.34, 12.34], %)
`)
}).toPass({ timeout: 40_000, intervals: [1_000] })
})
test('Can exit selection of face', async ({ page }) => {
// Load the app with the code panes
@ -344,6 +346,92 @@ test.describe('Sketch tests', () => {
})
})
test('Can edit a circle center and radius by dragging its handles', async ({
page,
}) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('XZ')
|> circle({ center: [4.61, -5.01], radius: 8 }, %)`
)
})
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await page.waitForTimeout(100)
await u.openAndClearDebugPanel()
await u.sendCustomCmd({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
vantage: { x: 0, y: -1250, z: 580 },
center: { x: 0, y: 0, z: 0 },
up: { x: 0, y: 0, z: 1 },
},
})
await page.waitForTimeout(100)
await u.sendCustomCmd({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
})
await page.waitForTimeout(100)
const startPX = [667, 325]
const dragPX = 40
await page
.getByText('circle({ center: [4.61, -5.01], radius: 8 }, %)')
.click()
await expect(
page.getByRole('button', { name: 'Edit Sketch' })
).toBeVisible()
await page.getByRole('button', { name: 'Edit Sketch' }).click()
await page.waitForTimeout(400)
let prevContent = await page.locator('.cm-content').innerText()
await expect(page.getByTestId('segment-overlay')).toHaveCount(1)
await test.step('drag circle center handle', async () => {
await page.dragAndDrop('#stream', '#stream', {
sourcePosition: { x: startPX[0], y: startPX[1] },
targetPosition: { x: startPX[0] + dragPX, y: startPX[1] - dragPX },
})
await page.waitForTimeout(100)
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
prevContent = await page.locator('.cm-content').innerText()
})
await test.step('drag circle radius handle', async () => {
await page.waitForTimeout(100)
const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]')
await page.waitForTimeout(100)
await page.dragAndDrop('#stream', '#stream', {
sourcePosition: { x: lineEnd.x - 5, y: lineEnd.y },
targetPosition: { x: lineEnd.x + dragPX, y: lineEnd.y + dragPX },
})
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
prevContent = await page.locator('.cm-content').innerText()
})
// expect the code to have changed
await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> circle({ center: [7.26, -2.37], radius: 11.79 }, %)
`)
})
test('Can edit a sketch that has been extruded in the same pipe', async ({
page,
}) => {

View File

@ -532,6 +532,64 @@ test(
})
}
)
test(
'Draft circle should look right',
{ tag: '@snapshot' },
async ({ page, context }) => {
// FIXME: Skip on macos its being weird.
// test.skip(process.platform === 'darwin', 'Skip on macos')
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeVisible()
// click on "Start Sketch" button
await u.clearCommandLogs()
await u.doAndWaitForImageDiff(
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
200
)
// select a plane
await page.mouse.click(700, 200)
await expect(page.locator('.cm-content')).toHaveText(
`const sketch001 = startSketchOn('XZ')`
)
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
await u.closeDebugPanel()
const startXPx = 600
// Equip the rectangle tool
// await page.getByRole('button', { name: 'line Line', exact: true }).click()
await page.getByTestId('circle-center').click()
// Draw the rectangle
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 10, { steps: 5 })
// Ensure the draft rectangle looks the same as it usually does
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
await expect(page.locator('.cm-content')).toHaveText(
`const sketch001 = startSketchOn('XZ')
|> circle({ center: [14.44, -2.44], radius: 1 }, %)`
)
}
)
test.describe(
'Client side scene scale should match engine scale',

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@ -365,10 +365,10 @@ const box = startSketchOn('XY')
svg(startSketchOn(keychain, 'end'), [-33, 32], -thickness)
startSketchOn(keychain, 'end')
|> circle([
|> circle({ center: [
width / 2,
height - (keychainHoleSize + 1.5)
], keychainHoleSize, %)
], radius: keychainHoleSize }, %)
|> extrude(-thickness, %)`
export const TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR = `const thing = 1`

View File

@ -774,6 +774,80 @@ const part001 = startSketchOn('XZ')
locator: '[data-overlay-toolbar-index="12"]',
})
})
test('for segment [circle]', async ({ page }) => {
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const part001 = startSketchOn('XZ')
|> circle({ center: [1 + 0, 0], radius: 8 }, %)
`
)
localStorage.setItem('disableAxis', 'true')
})
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
// wait for execution done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
await page
.getByText('circle({ center: [1 + 0, 0], radius: 8 }, %)')
.click()
await page.waitForTimeout(100)
await page.getByRole('button', { name: 'Edit Sketch' }).click()
await page.waitForTimeout(500)
await expect(page.getByTestId('segment-overlay')).toHaveCount(1)
const clickUnconstrained = _clickUnconstrained(page)
const clickConstrained = _clickConstrained(page)
const hoverPos = { x: 789, y: 114 } as const
let ang = await u.getAngle('[data-overlay-index="0"]')
console.log('angl', ang)
console.log('circle center x')
await clickConstrained({
hoverPos,
constraintType: 'xAbsolute',
expectBeforeUnconstrained:
'circle({ center: [1 + 0, 0], radius: 8 }, %)',
expectAfterUnconstrained: 'circle({ center: [1, 0], radius: 8 }, %)',
expectFinal: 'circle({ center: [xAbs001, 0], radius: 8 }, %)',
ang: ang + 105,
steps: 6,
locator: '[data-overlay-toolbar-index="0"]',
})
console.log('circle center y')
await clickUnconstrained({
hoverPos,
constraintType: 'yAbsolute',
expectBeforeUnconstrained:
'circle({ center: [xAbs001, 0], radius: 8 }, %)',
expectAfterUnconstrained:
'circle({ center: [xAbs001, yAbs001], radius: 8 }, %)',
expectFinal: 'circle({ center: [xAbs001, 0], radius: 8 }, %)',
ang: ang + 105,
steps: 10,
locator: '[data-overlay-toolbar-index="0"]',
})
console.log('circle radius')
await clickUnconstrained({
hoverPos,
constraintType: 'radius',
expectBeforeUnconstrained:
'circle({ center: [xAbs001, 0], radius: 8 }, %)',
expectAfterUnconstrained:
'circle({ center: [xAbs001, 0], radius: radius001 }, %)',
expectFinal: 'circle({ center: [xAbs001, 0], radius: 8 }, %)',
ang: ang + 105,
steps: 10,
locator: '[data-overlay-toolbar-index="0"]',
})
})
})
test.describe('Testing deleting a segment', () => {
const _deleteSegmentSequence =

View File

@ -34,13 +34,11 @@ import { CustomIcon, CustomIconName } from 'components/CustomIcon'
import { ConstrainInfo } from 'lang/std/stdTypes'
import { getConstraintInfo } from 'lang/std/sketch'
import { Dialog, Popover, Transition } from '@headlessui/react'
import { LineInputsType } from 'lang/std/sketchcombos'
import toast from 'react-hot-toast'
import { InstanceProps, create } from 'react-modal-promise'
import { executeAst } from 'lang/langHelpers'
import {
deleteSegmentFromPipeExpression,
makeRemoveSingleConstraintInput,
removeSingleConstraintInfo,
} from 'lang/modifyAst'
import { ActionButton } from 'components/ActionButton'
@ -126,7 +124,8 @@ export const ClientSideScene = ({
} else if (
state.matches({ Sketch: 'Line tool' }) ||
state.matches({ Sketch: 'Tangential arc to' }) ||
state.matches({ Sketch: 'Rectangle tool' })
state.matches({ Sketch: 'Rectangle tool' }) ||
state.matches({ Sketch: 'Circle tool' })
) {
cursor = 'crosshair'
} else {
@ -514,6 +513,11 @@ const ConstraintSymbol = ({
displayName: 'Intersection Offset',
iconName: 'intersection-offset',
},
radius: {
varName: 'radius',
displayName: 'Radius',
iconName: 'dimension',
},
// implicit constraints
vertical: {
@ -542,12 +546,10 @@ const ConstraintSymbol = ({
iconName: 'dimension',
},
}
const varName =
_type in varNameMap ? varNameMap[_type as LineInputsType].varName : 'var'
const name: CustomIconName = varNameMap[_type as LineInputsType].iconName
const displayName = varNameMap[_type as LineInputsType]?.displayName
const implicitDesc =
varNameMap[_type as LineInputsType]?.implicitConstraintDesc
const varName = _type in varNameMap ? varNameMap[_type].varName : 'var'
const name: CustomIconName = varNameMap[_type].iconName
const displayName = varNameMap[_type]?.displayName
const implicitDesc = varNameMap[_type]?.implicitConstraintDesc
const _node = useMemo(
() => getNodeFromPath<Expr>(kclManager.ast, pathToNode),
@ -604,13 +606,10 @@ const ConstraintSymbol = ({
if (trap(_node1)) return Promise.reject(_node1)
const shallowPath = _node1.shallowPath
const input = makeRemoveSingleConstraintInput(
argPosition,
shallowPath
)
if (!input || !context.sketchDetails) return
if (!context.sketchDetails || !argPosition) return
const transform = removeSingleConstraintInfo(
input,
shallowPath,
argPosition,
kclManager.ast,
kclManager.programMemory
)

View File

@ -62,9 +62,10 @@ import {
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
import { executeAst } from 'lang/langHelpers'
import {
circleSegment,
createArcGeometry,
dashedStraight,
profileStart,
createProfileStartHandle,
straightSegment,
tangentialArcToSegment,
} from './segments'
@ -72,6 +73,7 @@ import {
addCallExpressionsToPipe,
addCloseToPipe,
addNewSketchLn,
changeCircleArguments,
changeSketchArguments,
updateStartProfileAtArgs,
} from 'lang/std/sketch'
@ -87,6 +89,7 @@ import {
createArrayExpression,
createCallExpressionStdLib,
createLiteral,
createObjectExpression,
createPipeExpression,
createPipeSubstitution,
findUniqueName,
@ -119,9 +122,22 @@ export const TANGENTIAL_ARC_TO__SEGMENT_DASH =
'tangential-arc-to-segment-body-dashed'
export const TANGENTIAL_ARC_TO_SEGMENT = 'tangential-arc-to-segment'
export const TANGENTIAL_ARC_TO_SEGMENT_BODY = 'tangential-arc-to-segment-body'
export const CIRCLE_SEGMENT = 'circle-segment'
export const CIRCLE_SEGMENT_BODY = 'circle-segment-body'
export const CIRCLE_SEGMENT_DASH = 'circle-segment-body-dashed'
export const CIRCLE_CENTER_HANDLE = 'circle-center-handle'
export const SEGMENT_WIDTH_PX = 1.6
export const HIDE_SEGMENT_LENGTH = 75 // in pixels
export const HIDE_HOVER_SEGMENT_LENGTH = 60 // in pixels
export const SEGMENT_BODIES = [
STRAIGHT_SEGMENT,
TANGENTIAL_ARC_TO_SEGMENT,
CIRCLE_SEGMENT,
]
export const SEGMENT_BODIES_PLUS_PROFILE_START = [
...SEGMENT_BODIES,
PROFILE_START,
]
type Vec3Array = [number, number, number]
@ -186,6 +202,26 @@ export class SceneEntities {
})
)
}
if (
segment.userData.from &&
segment.userData.to &&
segment.userData.center &&
segment.userData.radius &&
segment.userData.type === CIRCLE_SEGMENT
) {
callbacks.push(
this.updateCircleSegment({
prevSegment: segment.userData.prevSegment,
from: segment.userData.from,
to: segment.userData.to,
center: segment.userData.center,
radius: segment.userData.radius,
group: segment,
scale: factor,
})
)
}
if (segment.name === PROFILE_START) {
segment.scale.set(factor, factor, factor)
}
@ -422,7 +458,8 @@ export class SceneEntities {
maybeModdedAst,
sketchGroup.start.__geoMeta.sourceRange
)
const _profileStart = profileStart({
if (sketchGroup?.value?.[0].type !== 'Circle') {
const _profileStart = createProfileStartHandle({
from: sketchGroup.start.from,
id: sketchGroup.start.__geoMeta.id,
pathToNode: segPathToNode,
@ -435,6 +472,7 @@ export class SceneEntities {
})
group.add(_profileStart)
this.activeSegments[JSON.stringify(segPathToNode)] = _profileStart
}
const callbacks: (() => SegmentOverlayPayload | null)[] = []
sketchGroup.value.forEach((segment, index) => {
let segPathToNode = getNodePathFromSourceRange(
@ -499,6 +537,32 @@ export class SceneEntities {
scale: factor,
})
)
} else if (segment.type === 'Circle') {
seg = circleSegment({
prevSegment: sketchGroup.value[index - 1],
from: segment.from,
to: segment.to,
center: segment.center,
radius: segment.radius,
id: segment.__geoMeta.id,
pathToNode: segPathToNode,
isDraftSegment,
scale: factor,
texture: sceneInfra.extraSegmentTexture,
theme: sceneInfra._theme,
isSelected,
})
callbacks.push(
this.updateCircleSegment({
prevSegment: sketchGroup.value[index - 1],
from: segment.from,
to: segment.to,
center: segment.center,
radius: segment.radius,
group: seg,
scale: factor,
})
)
} else {
seg = straightSegment({
from: segment.from,
@ -603,16 +667,18 @@ export class SceneEntities {
kclManager.programMemory.get(variableDeclarationName),
variableDeclarationName
)
if (err(sg)) return sg
const lastSeg = sg.value?.slice(-1)[0] || sg.start
if (err(sg)) return Promise.reject(sg)
const lastSeg = sg?.value?.slice(-1)[0] || sg.start
const index = sg.value.length // because we've added a new segment that's not in the memory yet, no need for `-1`
const mod = addNewSketchLn({
node: _ast,
programMemory: kclManager.programMemory,
to: [lastSeg.to[0], lastSeg.to[1]],
from: [lastSeg.to[0], lastSeg.to[1]],
input: {
type: 'straight-segment',
to: lastSeg.to,
from: lastSeg.to,
},
fnName: segmentName,
pathToNode: sketchPathToNode,
})
@ -683,8 +749,11 @@ export class SceneEntities {
const tmp = addNewSketchLn({
node: kclManager.ast,
programMemory: kclManager.programMemory,
input: {
type: 'straight-segment',
to: [intersection2d.x, intersection2d.y],
from: [lastSegment.to[0], lastSegment.to[1]],
from: lastSegment.to,
},
fnName:
lastSegment.type === 'TangentialArcTo'
? 'tangentialArcTo'
@ -748,6 +817,11 @@ export class SceneEntities {
const startSketchOn = _node1.node?.declarations
const startSketchOnInit = startSketchOn?.[0]?.init
const sg = sketchGroupFromKclValue(
kclManager.programMemory.get(variableDeclarationName),
variableDeclarationName
)
if (err(sg)) return sg
const tags: [string, string, string] = [
findUniqueName(_ast, 'rectangleSegmentA'),
findUniqueName(_ast, 'rectangleSegmentB'),
@ -805,7 +879,7 @@ export class SceneEntities {
programMemory.get(variableDeclarationName),
variableDeclarationName
)
if (err(sketchGroup)) return sketchGroup
if (err(sketchGroup)) return Promise.reject(sketchGroup)
const sgPaths = sketchGroup.value
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
@ -862,7 +936,7 @@ export class SceneEntities {
programMemory.get(variableDeclarationName),
variableDeclarationName
)
if (err(sketchGroup)) return sketchGroup
if (err(sketchGroup)) return Promise.reject(sketchGroup)
const sgPaths = sketchGroup.value
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
@ -883,6 +957,151 @@ export class SceneEntities {
},
})
}
setupDraftCircle = async (
sketchPathToNode: PathToNode,
forward: [number, number, number],
up: [number, number, number],
sketchOrigin: [number, number, number],
circleCenter: [x: number, y: number]
) => {
let _ast = structuredClone(kclManager.ast)
const _node1 = getNodeFromPath<VariableDeclaration>(
_ast,
sketchPathToNode || [],
'VariableDeclaration'
)
if (trap(_node1)) return Promise.reject(_node1)
const variableDeclarationName =
_node1.node?.declarations?.[0]?.id?.name || ''
const startSketchOn = _node1.node?.declarations
const startSketchOnInit = startSketchOn?.[0]?.init
startSketchOn[0].init = createPipeExpression([
startSketchOnInit,
createCallExpressionStdLib('circle', [
createObjectExpression({
center: createArrayExpression([
createLiteral(roundOff(circleCenter[0])),
createLiteral(roundOff(circleCenter[1])),
]),
radius: createLiteral(1),
}),
createPipeSubstitution(),
]),
])
let _recastAst = parse(recast(_ast))
if (trap(_recastAst)) return Promise.reject(_recastAst)
_ast = _recastAst
// do a quick mock execution to get the program memory up-to-date
await kclManager.executeAstMock(_ast)
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
sketchPathToNode,
forward,
up,
position: sketchOrigin,
maybeModdedAst: _ast,
draftExpressionsIndices: { start: 0, end: 0 },
})
sceneInfra.setCallbacks({
onMove: async (args) => {
const pathToNodeTwo = structuredClone(sketchPathToNode)
pathToNodeTwo[1][0] = 0
const _node = getNodeFromPath<VariableDeclaration>(
truncatedAst,
pathToNodeTwo || [],
'VariableDeclaration'
)
let modded = structuredClone(truncatedAst)
if (trap(_node)) return Promise.reject(_node)
const sketchInit = _node.node?.declarations?.[0]?.init
const x = (args.intersectionPoint.twoD.x || 0) - circleCenter[0]
const y = (args.intersectionPoint.twoD.y || 0) - circleCenter[1]
if (sketchInit.type === 'PipeExpression') {
const moddedResult = changeCircleArguments(
modded,
kclManager.programMemory,
[..._node.deepPath, ['body', 'PipeExpression'], [1, 'index']],
circleCenter,
Math.sqrt(x ** 2 + y ** 2)
)
if (err(moddedResult)) return Promise.reject(moddedResult)
modded = moddedResult.modifiedAst
}
const { programMemory } = await executeAst({
ast: modded,
useFakeExecutor: true,
engineCommandManager: this.engineCommandManager,
programMemoryOverride,
})
this.sceneProgramMemory = programMemory
const sketchGroup = sketchGroupFromKclValue(
programMemory.get(variableDeclarationName),
variableDeclarationName
)
if (err(sketchGroup)) return sketchGroup
const sgPaths = sketchGroup.value
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
this.updateSegment(
sketchGroup.start,
0,
0,
_ast,
orthoFactor,
sketchGroup
)
sgPaths.forEach((seg, index) =>
this.updateSegment(seg, index, 0, _ast, orthoFactor, sketchGroup)
)
},
onClick: async (args) => {
// Commit the rectangle to the full AST/code and return to sketch.idle
const cornerPoint = args.intersectionPoint?.twoD
if (!cornerPoint || args.mouseEvent.button !== 0) return
const x = roundOff((cornerPoint.x || 0) - circleCenter[0])
const y = roundOff((cornerPoint.y || 0) - circleCenter[1])
const _node = getNodeFromPath<VariableDeclaration>(
_ast,
sketchPathToNode || [],
'VariableDeclaration'
)
if (trap(_node)) return Promise.reject(_node)
const sketchInit = _node.node?.declarations?.[0]?.init
let modded = structuredClone(_ast)
if (sketchInit.type === 'PipeExpression') {
const moddedResult = changeCircleArguments(
modded,
kclManager.programMemory,
[..._node.deepPath, ['body', 'PipeExpression'], [1, 'index']],
circleCenter,
Math.sqrt(x ** 2 + y ** 2)
)
if (err(moddedResult)) return Promise.reject(moddedResult)
modded = moddedResult.modifiedAst
let _recastAst = parse(recast(modded))
if (trap(_recastAst)) return Promise.reject(_recastAst)
_ast = _recastAst
// Update the primary AST and unequip the rectangle tool
await kclManager.executeAstMock(_ast)
sceneInfra.modelingSend({ type: 'Finish circle' })
}
},
})
}
setupSketchIdleCallbacks = ({
pathToNode,
up,
@ -951,8 +1170,11 @@ export class SceneEntities {
const mod = addNewSketchLn({
node: kclManager.ast,
programMemory: kclManager.programMemory,
input: {
type: 'straight-segment',
to: [intersectionPoint.twoD.x, intersectionPoint.twoD.y],
from: [prevSegment.from[0], prevSegment.from[1]],
from: prevSegment.from,
},
// TODO assuming it's always a straight segments being added
// as this is easiest, and we'll need to add "tabbing" behavior
// to support other segment types
@ -1051,11 +1273,8 @@ export class SceneEntities {
? new Vector2(profileStart.position.x, profileStart.position.y)
: _intersection2d
const group = getParentGroup(object, [
STRAIGHT_SEGMENT,
TANGENTIAL_ARC_TO_SEGMENT,
PROFILE_START,
])
const group = getParentGroup(object, SEGMENT_BODIES_PLUS_PROFILE_START)
const subGroup = getParentGroup(object, [ARROWHEAD, CIRCLE_CENTER_HANDLE])
if (!group) return
const pathToNode: PathToNode = structuredClone(group.userData.pathToNode)
const varDecIndex = pathToNode[1][0]
@ -1073,7 +1292,7 @@ export class SceneEntities {
group.userData.from[0],
group.userData.from[1],
]
const to: [number, number] = [intersection2d.x, intersection2d.y]
const dragTo: [number, number] = [intersection2d.x, intersection2d.y]
let modifiedAst = draftInfo ? draftInfo.truncatedAst : { ...kclManager.ast }
const _node = getNodeFromPath<CallExpression>(
@ -1096,17 +1315,59 @@ export class SceneEntities {
modded = updateStartProfileAtArgs({
node: modifiedAst,
pathToNode,
to,
input: {
type: 'straight-segment',
to: dragTo,
from,
},
previousProgramMemory: kclManager.programMemory,
})
} else if (
group.name === CIRCLE_SEGMENT &&
// !subGroup treats grabbing the outer circumference of the circle
// as a drag of the center handle
(!subGroup || subGroup?.name === ARROWHEAD)
) {
// is dragging the radius handle
modded = changeSketchArguments(
modifiedAst,
kclManager.programMemory,
[node.start, node.end],
{
type: 'arc-segment',
from,
center: group.userData.center,
radius: Math.sqrt(
(group.userData.center[0] - dragTo[0]) ** 2 +
(group.userData.center[0] - dragTo[0]) ** 2
),
}
)
} else if (
group.name === CIRCLE_SEGMENT &&
subGroup?.name === CIRCLE_CENTER_HANDLE
) {
modded = changeSketchArguments(
modifiedAst,
kclManager.programMemory,
[node.start, node.end],
{
type: 'arc-segment',
from,
center: dragTo,
radius: group.userData.radius,
}
)
} else {
modded = changeSketchArguments(
modifiedAst,
kclManager.programMemory,
[node.start, node.end],
to,
from
{
type: 'straight-segment',
from,
to: dragTo,
}
)
}
if (trap(modded)) return
@ -1224,6 +1485,20 @@ export class SceneEntities {
group,
scale: factor,
})
} else if (
type === CIRCLE_SEGMENT &&
'center' in segment &&
'radius' in segment
) {
return this.updateCircleSegment({
prevSegment: sgPaths[index - 1],
from: segment.from,
to: segment.to,
center: segment.center,
radius: segment.radius,
group,
scale: factor,
})
} else if (type === PROFILE_START) {
group.position.set(segment.from[0], segment.from[1], 0)
group.scale.set(factor, factor, factor)
@ -1249,6 +1524,9 @@ export class SceneEntities {
group.userData.prevSegment = prevSegment
const arrowGroup = group.getObjectByName(ARROWHEAD) as Group
const extraSegmentGroup = group.getObjectByName(EXTRA_SEGMENT_HANDLE)
if (!prevSegment) {
console.trace('prevSegment is undefined')
}
const previousPoint =
prevSegment?.type === 'TangentialArcTo'
@ -1344,6 +1622,111 @@ export class SceneEntities {
angle,
})
}
updateCircleSegment({
prevSegment,
from,
to,
center,
radius,
group,
scale = 1,
}: {
prevSegment: SketchGroup['value'][number]
from: [number, number]
to: [number, number]
center: [number, number]
radius: number
group: Group
scale?: number
}): () => SegmentOverlayPayload | null {
group.userData.from = from
group.userData.to = to
group.userData.center = center
group.userData.radius = radius
group.userData.prevSegment = prevSegment
const arrowGroup = group.getObjectByName(ARROWHEAD) as Group
const circleCenterHandle = group.getObjectByName(
CIRCLE_CENTER_HANDLE
) as Group
const pxLength = (2 * radius * Math.PI) / scale
const shouldHideIdle = pxLength < HIDE_SEGMENT_LENGTH
const shouldHideHover = pxLength < HIDE_HOVER_SEGMENT_LENGTH
const hoveredParent =
sceneInfra.hoveredObject &&
getParentGroup(sceneInfra.hoveredObject, [CIRCLE_SEGMENT])
let isHandlesVisible = !shouldHideIdle
if (hoveredParent && hoveredParent?.uuid === group?.uuid) {
isHandlesVisible = !shouldHideHover
}
if (arrowGroup) {
arrowGroup.position.set(
center[0] + Math.cos(Math.PI / 4) * radius,
center[1] + Math.sin(Math.PI / 4) * radius,
0
)
const arrowheadAngle = Math.PI / 4
arrowGroup.quaternion.setFromUnitVectors(
new Vector3(0, 1, 0),
new Vector3(Math.cos(arrowheadAngle), Math.sin(arrowheadAngle), 0)
)
arrowGroup.scale.set(scale, scale, scale)
arrowGroup.visible = isHandlesVisible
}
if (circleCenterHandle) {
circleCenterHandle.position.set(center[0], center[1], 0)
circleCenterHandle.scale.set(scale, scale, scale)
circleCenterHandle.visible = isHandlesVisible
}
const circleSegmentBody = group.children.find(
(child) => child.userData.type === CIRCLE_SEGMENT_BODY
) as Mesh
if (circleSegmentBody) {
const newGeo = createArcGeometry({
radius,
center,
startAngle: 0,
endAngle: Math.PI * 2,
ccw: true,
scale,
})
circleSegmentBody.geometry = newGeo
}
const circleSegmentBodyDashed = group.children.find(
(child) => child.userData.type === CIRCLE_SEGMENT_DASH
) as Mesh
if (circleSegmentBodyDashed) {
// consider throttling the whole updateTangentialArcToSegment
// if there are more perf considerations going forward
this.throttledUpdateDashedArcGeo({
// ...arcInfo,
center,
radius,
ccw: true,
// make the start end where the handle is
startAngle: Math.PI * 0.25,
endAngle: Math.PI * 2.25,
mesh: circleSegmentBodyDashed,
isDashed: true,
scale,
})
}
return () =>
sceneInfra.updateOverlayDetails({
arrowGroup,
group,
isHandlesVisible,
from: to,
to: [center[0], center[1]],
angle: Math.PI / 4,
})
}
throttledUpdateDashedArcGeo = throttle(
(
args: Parameters<typeof createArcGeometry>[0] & {
@ -1478,7 +1861,7 @@ export class SceneEntities {
}
private _tearDownSketch(
callDepth = 0,
resolve: (val: unknown) => void,
resolve: any,
reject: () => void,
{ removeAxis = true }: { removeAxis?: boolean }
) {
@ -1508,7 +1891,7 @@ export class SceneEntities {
this._tearDownSketch(callDepth + 1, resolve, reject, { removeAxis })
}, delay)
} else {
reject()
resolve(true)
}
}
sceneInfra.camControls.enableRotate = true
@ -1520,7 +1903,7 @@ export class SceneEntities {
removeAxis = true,
}: {
removeAxis?: boolean
} = {}) {
} = {}): Promise<void | Error> {
// I think promisifying this is mostly a side effect of not having
// "setupSketch" correctly capture a promise when it's done
// so we're effectively waiting for to be finished setting up the scene just to tear it down
@ -1538,11 +1921,10 @@ export class SceneEntities {
mat.color.set(obj.userData.baseColor)
mat.color.offsetHSL(0, 0, 0.5)
}
const parent = getParentGroup(selected, [
STRAIGHT_SEGMENT,
TANGENTIAL_ARC_TO_SEGMENT,
PROFILE_START,
])
const parent = getParentGroup(
selected,
SEGMENT_BODIES_PLUS_PROFILE_START
)
if (parent?.userData?.pathToNode) {
const updatedAst = parse(recast(kclManager.ast))
if (trap(updatedAst)) return
@ -1586,6 +1968,16 @@ export class SceneEntities {
group: parent,
scale: factor,
})
} else if (parent.name === CIRCLE_SEGMENT) {
this.updateCircleSegment({
prevSegment: parent.userData.prevSegment,
from: parent.userData.from,
to: parent.userData.to,
center: parent.userData.center,
radius: parent.userData.radius,
group: parent,
scale: factor,
})
}
return
}
@ -1593,11 +1985,10 @@ export class SceneEntities {
},
onMouseLeave: ({ selected, ...rest }: OnMouseEnterLeaveArgs) => {
editorManager.setHighlightRange([[0, 0]])
const parent = getParentGroup(selected, [
STRAIGHT_SEGMENT,
TANGENTIAL_ARC_TO_SEGMENT,
PROFILE_START,
])
const parent = getParentGroup(
selected,
SEGMENT_BODIES_PLUS_PROFILE_START
)
if (parent) {
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
@ -1621,6 +2012,16 @@ export class SceneEntities {
group: parent,
scale: factor,
})
} else if (parent.name === CIRCLE_SEGMENT) {
this.updateCircleSegment({
prevSegment: parent.userData.prevSegment,
from: parent.userData.from,
to: parent.userData.to,
center: parent.userData.center,
radius: parent.userData.radius,
group: parent,
scale: factor,
})
}
}
const isSelected = parent?.userData?.isSelected
@ -1783,7 +2184,7 @@ function prepareTruncatedMemoryAndAst(
export function getParentGroup(
object: any,
stopAt: string[] = [STRAIGHT_SEGMENT, TANGENTIAL_ARC_TO_SEGMENT]
stopAt: string[] = SEGMENT_BODIES
): Group | null {
if (stopAt.includes(object?.userData?.type)) {
return object
@ -1830,10 +2231,7 @@ function colorSegment(object: any, color: number) {
})
return
}
const straightSegmentBody = getParentGroup(object, [
STRAIGHT_SEGMENT,
TANGENTIAL_ARC_TO_SEGMENT,
])
const straightSegmentBody = getParentGroup(object, SEGMENT_BODIES)
if (straightSegmentBody) {
straightSegmentBody.traverse((child) => {
if (child instanceof Mesh && !child.userData.ignoreColorChange) {

View File

@ -111,21 +111,21 @@ export class SceneInfra {
}
extraSegmentTexture: Texture
lastMouseState: MouseState = { type: 'idle' }
onDragStartCallback: (arg: OnDragCallbackArgs) => void = () => {}
onDragEndCallback: (arg: OnDragCallbackArgs) => void = () => {}
onDragCallback: (arg: OnDragCallbackArgs) => void = () => {}
onMoveCallback: (arg: OnMoveCallbackArgs) => void = () => {}
onClickCallback: (arg: OnClickCallbackArgs) => void = () => {}
onMouseEnter: (arg: OnMouseEnterLeaveArgs) => void = () => {}
onMouseLeave: (arg: OnMouseEnterLeaveArgs) => void = () => {}
onDragStartCallback: (arg: OnDragCallbackArgs) => any = () => {}
onDragEndCallback: (arg: OnDragCallbackArgs) => any = () => {}
onDragCallback: (arg: OnDragCallbackArgs) => any = () => {}
onMoveCallback: (arg: OnMoveCallbackArgs) => any = () => {}
onClickCallback: (arg: OnClickCallbackArgs) => any = () => {}
onMouseEnter: (arg: OnMouseEnterLeaveArgs) => any = () => {}
onMouseLeave: (arg: OnMouseEnterLeaveArgs) => any = () => {}
setCallbacks = (callbacks: {
onDragStart?: (arg: OnDragCallbackArgs) => void
onDragEnd?: (arg: OnDragCallbackArgs) => void
onDrag?: (arg: OnDragCallbackArgs) => void
onMove?: (arg: OnMoveCallbackArgs) => void
onClick?: (arg: OnClickCallbackArgs) => void
onMouseEnter?: (arg: OnMouseEnterLeaveArgs) => void
onMouseLeave?: (arg: OnMouseEnterLeaveArgs) => void
onDragStart?: (arg: OnDragCallbackArgs) => any
onDragEnd?: (arg: OnDragCallbackArgs) => any
onDrag?: (arg: OnDragCallbackArgs) => any
onMove?: (arg: OnMoveCallbackArgs) => any
onClick?: (arg: OnClickCallbackArgs) => any
onMouseEnter?: (arg: OnMouseEnterLeaveArgs) => any
onMouseLeave?: (arg: OnMouseEnterLeaveArgs) => any
}) => {
this.onDragStartCallback = callbacks.onDragStart || this.onDragStartCallback
this.onDragEndCallback = callbacks.onDragEnd || this.onDragEndCallback

View File

@ -24,6 +24,10 @@ import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
import { PathToNode, SketchGroup, getTangentialArcToInfo } from 'lang/wasm'
import {
CIRCLE_CENTER_HANDLE,
CIRCLE_SEGMENT,
CIRCLE_SEGMENT_BODY,
CIRCLE_SEGMENT_DASH,
EXTRA_SEGMENT_HANDLE,
EXTRA_SEGMENT_OFFSET_PX,
HIDE_SEGMENT_LENGTH,
@ -46,7 +50,7 @@ import {
import { Themes, getThemeColorForThreeJs } from 'lib/theme'
import { roundOff } from 'lib/utils'
export function profileStart({
export function createProfileStartHandle({
from,
id,
pathToNode,
@ -225,6 +229,28 @@ function createArrowhead(scale = 1, theme: Themes, color?: number): Group {
arrowGroup.scale.set(scale, scale, scale)
return arrowGroup
}
function createCircleCenterHandle(
scale = 1,
theme: Themes,
color?: number
): Group {
const circleCenterGroup = new Group()
const geometry = new BoxGeometry(12, 12, 12) // in pixels scaled later
const baseColor = getThemeColorForThreeJs(theme)
const body = new MeshBasicMaterial({ color })
const mesh = new Mesh(geometry, body)
circleCenterGroup.add(mesh)
circleCenterGroup.userData = {
type: CIRCLE_CENTER_HANDLE,
baseColor,
}
circleCenterGroup.name = CIRCLE_CENTER_HANDLE
circleCenterGroup.scale.set(scale, scale, scale)
return circleCenterGroup
}
function createExtraSegmentHandle(
scale: number,
@ -300,6 +326,86 @@ function createLengthIndicator({
return lengthIndicatorGroup
}
export function circleSegment({
prevSegment,
from,
to,
center,
radius,
id,
pathToNode,
isDraftSegment,
scale = 1,
texture,
theme,
isSelected,
}: {
prevSegment: SketchGroup['value'][number]
from: Coords2d
center: Coords2d
radius: number
to: Coords2d
id: string
pathToNode: PathToNode
isDraftSegment?: boolean
scale?: number
texture: Texture
theme: Themes
isSelected?: boolean
}): Group {
const group = new Group()
const geometry = createArcGeometry({
center,
radius,
startAngle: 0,
endAngle: Math.PI * 2,
ccw: true,
isDashed: isDraftSegment,
scale,
})
const baseColor = getThemeColorForThreeJs(theme)
const color = isSelected ? 0x0000ff : baseColor
const body = new MeshBasicMaterial({ color })
const mesh = new Mesh(geometry, body)
mesh.userData.type = isDraftSegment
? CIRCLE_SEGMENT_DASH
: CIRCLE_SEGMENT_BODY
group.userData = {
type: CIRCLE_SEGMENT,
id,
from,
to,
radius,
center,
ccw: true,
prevSegment,
pathToNode,
isSelected,
baseColor,
}
group.name = CIRCLE_SEGMENT
const arrowGroup = createArrowhead(scale, theme, color)
arrowGroup.position.set(
center[0] + Math.cos(Math.PI / 4) * radius,
center[1] + Math.sin(Math.PI / 4) * radius,
0
)
const circleCenterGroup = createCircleCenterHandle(scale, theme, color)
circleCenterGroup.position.set(center[0], center[1], 0)
const arrowheadAngle = Math.PI / 4
arrowGroup.quaternion.setFromUnitVectors(
new Vector3(0, 1, 0),
new Vector3(Math.cos(arrowheadAngle), Math.sin(arrowheadAngle), 0)
)
group.add(mesh, arrowGroup, circleCenterGroup)
return group
}
export function tangentialArcToSegment({
prevSegment,
from,

View File

@ -50,8 +50,7 @@ import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
import useStateMachineCommands from 'hooks/useStateMachineCommands'
import { modelingMachineCommandConfig } from 'lib/commandBarConfigs/modelingCommandConfig'
import {
STRAIGHT_SEGMENT,
TANGENTIAL_ARC_TO_SEGMENT,
SEGMENT_BODIES,
getParentGroup,
getSketchOrientationDetails,
} from 'clientSideScene/sceneEntities'
@ -168,10 +167,7 @@ export const ModelingMachineProvider = ({
if (event.type !== 'Set mouse state') return {}
const nextSegmentHoverMap = () => {
if (event.data.type === 'isHovering') {
const parent = getParentGroup(event.data.on, [
STRAIGHT_SEGMENT,
TANGENTIAL_ARC_TO_SEGMENT,
])
const parent = getParentGroup(event.data.on, SEGMENT_BODIES)
const pathToNode = parent?.userData?.pathToNode
const pathToNodeString = JSON.stringify(pathToNode)
if (!parent || !pathToNode) return context.segmentHoverMap
@ -187,10 +183,10 @@ export const ModelingMachineProvider = ({
event.data.type === 'idle' &&
context.mouseState.type === 'isHovering'
) {
const mouseOnParent = getParentGroup(context.mouseState.on, [
STRAIGHT_SEGMENT,
TANGENTIAL_ARC_TO_SEGMENT,
])
const mouseOnParent = getParentGroup(
context.mouseState.on,
SEGMENT_BODIES
)
if (!mouseOnParent || !mouseOnParent?.userData?.pathToNode)
return context.segmentHoverMap
const pathToNodeString = JSON.stringify(
@ -204,7 +200,8 @@ export const ModelingMachineProvider = ({
pathToNodeString,
},
})
}, 800) as unknown as number
// overlay timeout is 1s
}, 1000) as unknown as number
return {
...context.segmentHoverMap,
[pathToNodeString]: timeoutId,

View File

@ -10,10 +10,10 @@ import {
transformSecondarySketchLinesTagFirst,
getTransformInfos,
PathToNodeMap,
TransformInfo,
} from '../../lang/std/sketchcombos'
import { kclManager } from 'lib/singletons'
import { err } from 'lib/trap'
import { TransformInfo } from 'lang/std/stdTypes'
export function equalAngleInfo({
selectionRanges,

View File

@ -10,8 +10,8 @@ import {
transformSecondarySketchLinesTagFirst,
getTransformInfos,
PathToNodeMap,
TransformInfo,
} from '../../lang/std/sketchcombos'
import { TransformInfo } from 'lang/std/stdTypes'
import { kclManager } from 'lib/singletons'
import { err } from 'lib/trap'

View File

@ -9,8 +9,8 @@ import {
PathToNodeMap,
getTransformInfos,
transformAstSketchLines,
TransformInfo,
} from '../../lang/std/sketchcombos'
import { TransformInfo } from 'lang/std/stdTypes'
import { kclManager } from 'lib/singletons'
import { err } from 'lib/trap'

View File

@ -11,8 +11,8 @@ import {
transformSecondarySketchLinesTagFirst,
getTransformInfos,
PathToNodeMap,
TransformInfo,
} from '../../lang/std/sketchcombos'
import { TransformInfo } from 'lang/std/stdTypes'
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
import { createVariableDeclaration } from '../../lang/modifyAst'
import { removeDoubleNegatives } from '../AvailableVarsHelpers'

View File

@ -9,8 +9,8 @@ import {
PathToNodeMap,
getRemoveConstraintsTransforms,
transformAstSketchLines,
TransformInfo,
} from '../../lang/std/sketchcombos'
import { TransformInfo } from 'lang/std/stdTypes'
import { kclManager } from 'lib/singletons'
import { err } from 'lib/trap'

View File

@ -9,8 +9,8 @@ import {
getTransformInfos,
transformAstSketchLines,
PathToNodeMap,
TransformInfo,
} from '../../lang/std/sketchcombos'
import { TransformInfo } from 'lang/std/stdTypes'
import {
SetAngleLengthModal,
createSetAngleLengthModal,

View File

@ -10,8 +10,8 @@ import {
transformSecondarySketchLinesTagFirst,
getTransformInfos,
PathToNodeMap,
TransformInfo,
} from '../../lang/std/sketchcombos'
import { TransformInfo } from 'lang/std/stdTypes'
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
import { createVariableDeclaration } from '../../lang/modifyAst'
import { removeDoubleNegatives } from '../AvailableVarsHelpers'

View File

@ -9,8 +9,8 @@ import {
transformSecondarySketchLinesTagFirst,
getTransformInfos,
PathToNodeMap,
TransformInfo,
} from '../../lang/std/sketchcombos'
import { TransformInfo } from 'lang/std/stdTypes'
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
import { createLiteral, createVariableDeclaration } from '../../lang/modifyAst'
import { removeDoubleNegatives } from '../AvailableVarsHelpers'

View File

@ -9,8 +9,8 @@ import {
PathToNodeMap,
getTransformInfos,
transformAstSketchLines,
TransformInfo,
} from '../../lang/std/sketchcombos'
import { TransformInfo } from 'lang/std/stdTypes'
import {
SetAngleLengthModal,
createSetAngleLengthModal,

View File

@ -24,11 +24,9 @@ export type ToolTip =
| 'yLineTo'
| 'angledLineThatIntersects'
| 'tangentialArcTo'
| 'circle'
export const toolTips = [
'sketch_line',
'move',
// original tooltips
export const toolTips: Array<ToolTip> = [
'line',
'lineTo',
'angledLine',
@ -42,7 +40,7 @@ export const toolTips = [
'yLineTo',
'angledLineThatIntersects',
'tangentialArcTo',
] as any as ToolTip[]
]
export async function executeAst({
ast,

View File

@ -20,6 +20,7 @@ import {
import { enginelessExecutor } from '../lib/testHelpers'
import { findUsesOfTagInPipe, getNodePathFromSourceRange } from './queryAst'
import { err } from 'lib/trap'
import { SimplifiedArgDetails, InputArgKeys } from './std/stdTypes'
beforeAll(async () => {
await initPromise
@ -638,11 +639,27 @@ describe('Testing removeSingleConstraintInfo', () => {
code.indexOf(lineOfInterest) + lineOfInterest.length,
]
const pathToNode = getNodePathFromSourceRange(ast, range)
let argPosition: SimplifiedArgDetails
if (key === 'arrayIndex' && typeof value === 'number') {
argPosition = {
type: 'arrayItem',
index: value === 0 ? 0 : 1,
}
} else if (key === 'objectProperty' && typeof value === 'string') {
argPosition = {
type: 'objectProperty',
key: value as InputArgKeys,
}
} else if (key === '') {
argPosition = {
type: 'singleValue',
}
} else {
throw new Error('argPosition is undefined')
}
const mod = removeSingleConstraintInfo(
{
pathToCallExp: pathToNode,
[key]: value,
},
pathToNode,
argPosition,
ast,
programMemory
)
@ -675,12 +692,24 @@ describe('Testing removeSingleConstraintInfo', () => {
code.indexOf(lineOfInterest) + 1,
code.indexOf(lineOfInterest) + lineOfInterest.length,
]
let argPosition: SimplifiedArgDetails
if (key === 'arrayIndex' && typeof value === 'number') {
argPosition = {
type: 'arrayItem',
index: value === 0 ? 0 : 1,
}
} else if (key === 'objectProperty' && typeof value === 'string') {
argPosition = {
type: 'objectProperty',
key: value as InputArgKeys,
}
} else {
throw new Error('argPosition is undefined')
}
const pathToNode = getNodePathFromSourceRange(ast, range)
const mod = removeSingleConstraintInfo(
{
pathToCallExp: pathToNode,
[key]: value,
},
pathToNode,
argPosition,
ast,
programMemory
)

View File

@ -38,7 +38,7 @@ import {
import { DefaultPlaneStr } from 'clientSideScene/sceneEntities'
import { isOverlap, roundOff } from 'lib/utils'
import { KCL_DEFAULT_CONSTANT_PREFIXES } from 'lib/constants'
import { ConstrainInfo } from './std/stdTypes'
import { SimplifiedArgDetails } from './std/stdTypes'
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
import { Models } from '@kittycad/lib'
@ -799,15 +799,10 @@ export function deleteSegmentFromPipeExpression(
)
if (!constraintInfo) return
const input = makeRemoveSingleConstraintInput(
constraintInfo.argPosition,
callExp.shallowPath
)
if (!input) return
if (!constraintInfo.argPosition) return
const transform = removeSingleConstraintInfo(
{
...input,
},
callExp.shallowPath,
constraintInfo.argPosition,
_modifiedAst,
programMemory
)
@ -834,37 +829,9 @@ export function deleteSegmentFromPipeExpression(
return _modifiedAst
}
export function makeRemoveSingleConstraintInput(
argPosition: ConstrainInfo['argPosition'],
pathToNode: PathToNode
): Parameters<typeof removeSingleConstraintInfo>[0] | false {
return argPosition?.type === 'singleValue'
? {
pathToCallExp: pathToNode,
}
: argPosition?.type === 'arrayItem'
? {
pathToCallExp: pathToNode,
arrayIndex: argPosition.index,
}
: argPosition?.type === 'objectProperty'
? {
pathToCallExp: pathToNode,
objectProperty: argPosition.key,
}
: false
}
export function removeSingleConstraintInfo(
{
pathToCallExp,
arrayIndex,
objectProperty,
}: {
pathToCallExp: PathToNode
arrayIndex?: number
objectProperty?: string
},
pathToCallExp: PathToNode,
argDetails: SimplifiedArgDetails,
ast: Program,
programMemory: ProgramMemory
):
@ -875,8 +842,7 @@ export function removeSingleConstraintInfo(
| false {
const transform = removeSingleConstraint({
pathToCallExp,
arrayIndex,
objectProperty,
inputDetails: argDetails,
ast,
})
if (!transform) return false

View File

@ -16,6 +16,7 @@ import {
VariableDeclaration,
VariableDeclarator,
sketchGroupFromKclValue,
ObjectExpression,
} from './wasm'
import { createIdentifier, splitPathAtLastIndex } from './modifyAst'
import { getSketchSegmentFromSourceRange } from './std/sketchConstraints'
@ -934,3 +935,12 @@ export function hasExtrudableGeometry(ast: Program) {
})
return Object.keys(theMap).length > 0
}
export function getObjExpProperty(
node: ObjectExpression,
propName: string
): { exp: Expr; index: number } | null {
const index = node.properties.findIndex(({ key }) => key.name === propName)
if (index === -1) return null
return { exp: node.properties[index].value, index }
}

View File

@ -123,8 +123,11 @@ describe('testing changeSketchArguments', () => {
ast,
programMemory,
[sourceStart, sourceStart + lineToChange.length],
[2, 3],
[0, 0]
{
type: 'straight-segment',
from: [0, 0],
to: [2, 3],
}
)
if (err(changeSketchArgsRetVal)) return changeSketchArgsRetVal
expect(recast(changeSketchArgsRetVal.modifiedAst)).toBe(expectedCode)
@ -150,8 +153,11 @@ const mySketch001 = startSketchOn('XY')
const newSketchLnRetVal = addNewSketchLn({
node: ast,
programMemory,
to: [2, 3],
input: {
type: 'straight-segment',
from: [0, 0],
to: [2, 3],
},
fnName: 'lineTo',
pathToNode: [
['body', ''],

File diff suppressed because it is too large Load Diff

View File

@ -43,6 +43,16 @@ export function getSketchSegmentFromSourceRange(
index: number
}
| Error {
const lineIndex = sketchGroup.value.findIndex(
({ __geoMeta: { sourceRange } }: Path) =>
sourceRange[0] <= rangeStart && sourceRange[1] >= rangeEnd
)
const line = sketchGroup.value[lineIndex]
if (line)
return {
segment: line,
index: lineIndex,
}
const startSourceRange = sketchGroup.start?.__geoMeta.sourceRange
if (
startSourceRange &&
@ -51,17 +61,7 @@ export function getSketchSegmentFromSourceRange(
sketchGroup.start
)
return { segment: { ...sketchGroup.start, type: 'Base' }, index: -1 }
const lineIndex = sketchGroup.value.findIndex(
({ __geoMeta: { sourceRange } }: Path) =>
sourceRange[0] <= rangeStart && sourceRange[1] >= rangeEnd
)
const line = sketchGroup.value[lineIndex]
if (!line) return new Error('could not find matching line')
return {
segment: line,
index: lineIndex,
}
return new Error('could not find matching segment')
}
export function isSketchVariablesLinked(

File diff suppressed because it is too large Load Diff

View File

@ -9,22 +9,8 @@ import {
CallExpression,
Literal,
} from '../wasm'
import { EngineCommandManager } from './engineConnection'
import { LineInputsType } from './sketchcombos'
export interface InternalFirstArg {
programMemory: ProgramMemory
name?: string
sourceRange: SourceRange
engineCommandManager: EngineCommandManager
code: string
}
export interface PathReturn {
programMemory: ProgramMemory
currentPath: Path
}
export interface ModifyAstBase {
node: Program
// TODO #896: Remove ProgramMemory from this interface
@ -37,85 +23,196 @@ export interface AddTagInfo {
pathToNode: PathToNode
}
interface addCall extends ModifyAstBase {
to: [number, number]
/** Inputs for all straight segments, to and from are absolute values, as this gives a
* consistent base that can be converted to all of the line, angledLine, etc segment types
* One notable exception to "straight segment" is that tangentialArcTo is included in this
* Input type since it too only takes x-y values and is able to get extra info it needs
* to be tangential from the previous segment */
interface StraightSegmentInput {
type: 'straight-segment'
from: [number, number]
to: [number, number]
}
/** Inputs for arcs, excluding tangentialArcTo for reasons explain in
* the @straightSegmentInput comment */
interface ArcSegmentInput {
type: 'arc-segment'
from: [number, number]
center: [number, number]
radius: number
}
/**
* SegmentInputs is a union type that can be either a StraightSegmentInput or an ArcSegmentInput.
*
* - StraightSegmentInput: Represents a straight segment with a starting point (from) and an ending point (to).
* - ArcSegmentInput: Represents an arc segment with a starting point (from), a center point, and a radius.
*/
export type SegmentInputs = StraightSegmentInput | ArcSegmentInput
/**
* Interface for adding or replacing a sketch stblib call expression to a sketch.
* Replacing normally means adding or removing a constraint
*
* @property segmentInput - The input segment data, which can be either a straight segment or an arc segment.
* @property replaceExistingCallback - An optional callback function to replace an existing call expression,
* if not provided, a new call expression will be added using segMentInput values.
* @property referencedSegment - An optional path to a referenced segment.
* @property spliceBetween=false - Defaults to false. Normal behavior is to add a new callExpression to the end of the pipeExpression.
*/
interface addCall extends ModifyAstBase {
segmentInput: SegmentInputs
replaceExistingCallback?: (rawArgs: RawArgs) => CreatedSketchExprResult
referencedSegment?: Path
replaceExisting?: boolean
createCallback?: TransformCallback // TODO: #29 probably should not be optional
/// defaults to false, normal behavior is to add a new callExpression to the end of the pipeExpression
spliceBetween?: boolean
}
interface updateArgs extends ModifyAstBase {
from: [number, number]
to: [number, number]
input: SegmentInputs
}
export type VarValueKeys = 'angle' | 'offset' | 'length' | 'to' | 'intersectTag'
export type InputArgKeys =
| 'angle'
| 'offset'
| 'length'
| 'to'
| 'intersectTag'
| 'radius'
| 'center'
export interface SingleValueInput<T> {
type: 'singleValue'
argType: LineInputsType
value: T
expr: T
}
export interface ArrayItemInput<T> {
type: 'arrayItem'
index: 0 | 1
argType: LineInputsType
value: T
expr: T
}
export interface ObjectPropertyInput<T> {
type: 'objectProperty'
key: VarValueKeys
key: InputArgKeys
argType: LineInputsType
value: T
expr: T
}
export interface ArrayOrObjItemInput<T> {
interface ArrayOrObjItemInput<T> {
type: 'arrayOrObjItem'
key: VarValueKeys
key: InputArgKeys
index: 0 | 1
argType: LineInputsType
value: T
expr: T
}
export type _VarValue<T> =
interface ArrayInObject<T> {
type: 'arrayInObject'
key: InputArgKeys
argType: LineInputsType
index: 0 | 1
expr: T
}
type _InputArg<T> =
| SingleValueInput<T>
| ArrayItemInput<T>
| ObjectPropertyInput<T>
| ArrayOrObjItemInput<T>
| ArrayInObject<T>
export type VarValue = _VarValue<Expr>
export type RawValue = _VarValue<Literal>
/**
* {@link RawArg.expr} is the current expression for each of the args for a segment
* i.e. if the expression is 5 + 6, {@link RawArg.expr} will be that binary expression
*
* Other properties on this type describe how the args are defined for this particular segment
* i.e. line uses [x, y] style inputs, while angledLine uses either [angle, length] or {angle, length}
* and circle uses {center: [x, y], radius: number}
* Which is why a union type is used that can be type narrowed using the {@link RawArg.type} property
* {@link RawArg.expr} is common to all of these types
*/
export type InputArg = _InputArg<Expr>
export type VarValues = Array<VarValue>
export type RawValues = Array<RawValue>
/**
* {@link RawArg.expr} is the literal equivalent of whatever current expression is
* i.e. if the expression is 5 + 6, the literal would be 11
* but of course works for expressions like myVar + someFn() etc too
* This is useful in cases where we want to "un-constrain" inputs to segments
*/
type RawArg = _InputArg<Literal>
type SimplifiedVarValue =
export type InputArgs = Array<InputArg>
/**
* The literal equivalent of whatever current expression is
* i.e. if the expression is 5 + 6, the literal would be 11
* but of course works for expressions like myVar + someFn() etc too
* This is useful in cases where we want to "un-constrain" inputs to segments
*/
export type RawArgs = Array<RawArg>
/**
* Serves the same role as {@link InputArg} on {@link RawArg}
* but without the {@link RawArg.expr} property, since it is not needed
* when we only need to know where there arg is.
*/
export type SimplifiedArgDetails =
| {
type: 'singleValue'
}
| { type: 'arrayItem'; index: 0 | 1 }
| { type: 'objectProperty'; key: VarValueKeys }
| { type: 'objectProperty'; key: InputArgKeys }
| {
type: 'arrayInObject'
key: InputArgKeys
index: 0 | 1
}
export type TransformCallback = (
args: [Expr, Expr],
literalValues: RawValues,
referencedSegment?: Path
) => {
/**
* Represents the result of creating a sketch expression (line, tangentialArcTo, angledLine, circle, etc.).
*
* @property {Expr} callExp - This is the main result; recasting the expression should give the user the new function call.
* @property {number} [valueUsedInTransform] - Aside from `callExp`, we also return the number used in the transform, which is useful for constraints.
* For example, when adding a "horizontal distance" constraint, we don't want the segments to move, just constrain them in place.
* So the second segment will probably be something like `lineTo([segEndX($firstSegTag) + someLiteral, 123], %)` where `someLiteral` is
* the value of the current horizontal distance, that we calculate to constrain the second segment without it moving.
* We can run the ast-mod to get this constraint `valueUsedInTransform` without applying the mod so that we can surface this to the user in a modal.
* We show them the modal where they can specify the distance they want to constrain to.
* We pre-fill this with the current value `valueUsedInTransform`, which they can accept or change, and we'll use that to apply the final ast-mod.
*/
export interface CreatedSketchExprResult {
callExp: Expr
valueUsedInTransform?: number
}
export type CreateStdLibSketchCallExpr = (args: {
inputs: InputArgs
rawArgs: RawArgs
referenceSegName: string
tag?: Expr
forceValueUsedInTransform?: Expr
referencedSegment?: Path
}) => CreatedSketchExprResult
export type TransformInfo = {
tooltip: ToolTip
createNode: CreateStdLibSketchCallExpr
}
export interface ConstrainInfo {
stdLibFnName: ToolTip
type: LineInputsType | 'vertical' | 'horizontal' | 'tangentialWithPrevious'
type:
| LineInputsType
| 'vertical'
| 'horizontal'
| 'tangentialWithPrevious'
| 'radius'
isConstrained: boolean
sourceRange: SourceRange
pathToNode: PathToNode
value: string
calculatedValue?: any
argPosition?: SimplifiedVarValue
argPosition?: SimplifiedArgDetails
}
export interface SketchLineHelper {

View File

@ -20,10 +20,8 @@ import {
} from 'lang/queryAst'
import { CommandArgument } from './commandTypes'
import {
STRAIGHT_SEGMENT,
TANGENTIAL_ARC_TO_SEGMENT,
getParentGroup,
PROFILE_START,
SEGMENT_BODIES_PLUS_PROFILE_START,
} from 'clientSideScene/sceneEntities'
import { Mesh, Object3D, Object3DEventMap } from 'three'
import { AXIS_GROUP, X_AXIS } from 'clientSideScene/sceneInfra'
@ -162,11 +160,7 @@ export async function getEventForSelectWithPoint({
export function getEventForSegmentSelection(
obj: Object3D<Object3DEventMap>
): ModelingMachineEvent | null {
const group = getParentGroup(obj, [
STRAIGHT_SEGMENT,
TANGENTIAL_ARC_TO_SEGMENT,
PROFILE_START,
])
const group = getParentGroup(obj, SEGMENT_BODIES_PLUS_PROFILE_START)
const axisGroup = getParentGroup(obj, [AXIS_GROUP])
if (!group && !axisGroup) return null
if (axisGroup?.userData.type === AXIS_GROUP) {
@ -303,12 +297,7 @@ function updateSceneObjectColors(codeBasedSelections: Selection[]) {
const updated = kclManager.ast
Object.values(sceneEntitiesManager.activeSegments).forEach((segmentGroup) => {
if (
![STRAIGHT_SEGMENT, TANGENTIAL_ARC_TO_SEGMENT, PROFILE_START].includes(
segmentGroup?.name
)
)
return
if (!SEGMENT_BODIES_PLUS_PROFILE_START.includes(segmentGroup?.name)) return
const nodeMeta = getNodeFromPath<CallExpression>(
updated,
segmentGroup.userData.pathToNode,

View File

@ -2,7 +2,8 @@ import { CustomIconName } from 'components/CustomIcon'
import { DEV } from 'env'
import { commandBarMachine } from 'machines/commandBarMachine'
import {
canRectangleTool,
canRectangleOrCircleTool,
isClosedSketch,
isEditingExistingSketch,
modelingMachine,
} from 'machines/modelingMachine'
@ -301,7 +302,11 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
state.matches('Sketch no face') ||
state.matches({
Sketch: { 'Rectangle tool': 'Awaiting second corner' },
}),
}) ||
state.matches({
Sketch: { 'Circle tool': 'Awaiting Radius' },
}) ||
isClosedSketch(state.context),
title: 'Line',
hotkey: (state) =>
state.matches({ Sketch: 'Line tool' }) ? ['Esc', 'L'] : 'L',
@ -324,8 +329,15 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
icon: 'arc',
status: 'available',
disabled: (state) =>
!isEditingExistingSketch(state.context) &&
!state.matches({ Sketch: 'Tangential arc to' }),
(!isEditingExistingSketch(state.context) &&
!state.matches({ Sketch: 'Tangential arc to' })) ||
state.matches({
Sketch: { 'Rectangle tool': 'Awaiting second corner' },
}) ||
state.matches({
Sketch: { 'Circle tool': 'Awaiting Radius' },
}) ||
isClosedSketch(state.context),
title: 'Tangential Arc',
hotkey: (state) =>
state.matches({ Sketch: 'Tangential arc to' }) ? ['Esc', 'A'] : 'A',
@ -363,10 +375,22 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
[
{
id: 'circle-center',
onClick: () => console.error('Center circle not yet implemented'),
onClick: ({ modelingState, modelingSend }) =>
modelingSend({
type: 'change tool',
data: {
tool: !modelingState.matches({ Sketch: 'Circle tool' })
? 'circle'
: 'none',
},
}),
icon: 'circle',
status: 'unavailable',
status: 'available',
title: 'Center circle',
disabled: (state) =>
!canRectangleOrCircleTool(state.context) &&
!state.matches({ Sketch: 'Circle tool' }),
isActive: (state) => state.matches({ Sketch: 'Circle tool' }),
showTitle: false,
description: 'Start drawing a circle from its center',
links: [
@ -382,7 +406,6 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
console.error('Three-point circle not yet implemented'),
icon: 'circle',
status: 'unavailable',
disabled: () => true,
title: 'Three-point circle',
showTitle: false,
description: 'Draw a circle defined by three points',
@ -404,7 +427,7 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
icon: 'rectangle',
status: 'available',
disabled: (state) =>
!canRectangleTool(state.context) &&
!canRectangleOrCircleTool(state.context) &&
!state.matches({ Sketch: 'Rectangle tool' }),
title: 'Corner rectangle',
hotkey: (state) =>

View File

@ -58,6 +58,8 @@ import { deleteSegment } from 'clientSideScene/ClientSideSceneComp'
import { executeAst } from 'lang/langHelpers'
import toast from 'react-hot-toast'
import { ToolbarModeName } from 'lib/toolbar'
import { quaternionFromUpNForward } from 'clientSideScene/helpers'
import { Vector3 } from 'three'
export const MODELING_PERSIST_KEY = 'MODELING_PERSIST_KEY'
@ -159,7 +161,12 @@ export interface Store {
openPanes: SidebarType[]
}
export type SketchTool = 'line' | 'tangentialArc' | 'rectangle' | 'none'
export type SketchTool =
| 'line'
| 'tangentialArc'
| 'rectangle'
| 'circle'
| 'none'
export type ModelingMachineEvent =
| {
@ -211,6 +218,10 @@ export type ModelingMachineEvent =
type: 'Add rectangle origin'
data: [x: number, y: number]
}
| {
type: 'Add circle origin'
data: [x: number, y: number]
}
| {
type: 'xstate.done.actor.animate-to-face'
output: SketchDetails
@ -244,6 +255,7 @@ export type ModelingMachineEvent =
}
}
| { type: 'Finish rectangle' }
| { type: 'Finish circle' }
| { type: 'Artifact graph populated' }
| { type: 'Artifact graph emptied' }
@ -464,7 +476,10 @@ export const modelingMachine = setup({
isEditingExistingSketch({ sketchDetails }),
'next is rectangle': ({ context: { sketchDetails, currentTool } }) =>
currentTool === 'rectangle' && canRectangleTool({ sketchDetails }),
currentTool === 'rectangle' &&
canRectangleOrCircleTool({ sketchDetails }),
'next is circle': ({ context: { sketchDetails, currentTool } }) =>
currentTool === 'circle' && canRectangleOrCircleTool({ sketchDetails }),
'next is line': ({ context }) => context.currentTool === 'line',
'next is none': ({ context }) => {
console.log('is next none?', context)
@ -694,6 +709,42 @@ export const modelingMachine = setup({
},
})
},
'listen for circle origin': ({ context: { sketchDetails } }) => {
if (!sketchDetails) return
sceneEntitiesManager.createIntersectionPlane()
const quaternion = quaternionFromUpNForward(
new Vector3(...sketchDetails.yAxis),
new Vector3(...sketchDetails.zAxis)
)
// Position the click raycast plane
if (sceneEntitiesManager.intersectionPlane) {
sceneEntitiesManager.intersectionPlane.setRotationFromQuaternion(
quaternion
)
sceneEntitiesManager.intersectionPlane.position.copy(
new Vector3(...(sketchDetails?.origin || [0, 0, 0]))
)
}
sceneInfra.setCallbacks({
onClick: async (args) => {
if (!args) return
if (args.mouseEvent.which !== 1) return
const { intersectionPoint } = args
if (!intersectionPoint?.twoD || !sketchDetails?.sketchPathToNode)
return
const twoD = args.intersectionPoint?.twoD
if (twoD) {
sceneInfra.modelingSend({
type: 'Add circle origin',
data: [twoD.x, twoD.y],
})
} else {
console.error('No intersection point found')
}
},
})
},
'set up draft rectangle': ({ context: { sketchDetails }, event }) => {
if (event.type !== 'Add rectangle origin') return
if (!sketchDetails || !event.data) return
@ -706,6 +757,18 @@ export const modelingMachine = setup({
event.data
)
},
'set up draft circle': ({ context: { sketchDetails }, event }) => {
if (event.type !== 'Add circle origin') return
if (!sketchDetails || !event.data) return
// eslint-disable-next-line @typescript-eslint/no-floating-promises
sceneEntitiesManager.setupDraftCircle(
sketchDetails.sketchPathToNode,
sketchDetails.zAxis,
sketchDetails.yAxis,
sketchDetails.origin,
event.data
)
},
'set up draft line without teardown': ({ context: { sketchDetails } }) => {
if (!sketchDetails) return
// eslint-disable-next-line @typescript-eslint/no-floating-promises
@ -1805,10 +1868,43 @@ export const modelingMachine = setup({
target: 'Tangential arc to',
guard: 'next is tangential arc',
},
{
target: 'Circle tool',
guard: 'next is circle',
},
],
entry: 'assign tool in context',
},
'Circle tool': {
on: {
'change tool': 'Change Tool',
},
states: {
'Awaiting origin': {
on: {
'Add circle origin': {
target: 'Awaiting Radius',
actions: 'set up draft circle',
},
},
},
'Awaiting Radius': {
on: {
'Finish circle': 'Finished Circle',
},
},
'Finished Circle': {
always: '#Modeling.Sketch.SketchIdle',
},
},
initial: 'Awaiting origin',
entry: 'listen for circle origin',
},
},
initial: 'Init',
@ -1946,10 +2042,13 @@ export function isEditingExistingSketch({
(item) =>
item.type === 'CallExpression' && item.callee.name === 'startProfileAt'
)
return hasStartProfileAt && pipeExpression.body.length > 2
const hasCircle = pipeExpression.body.some(
(item) => item.type === 'CallExpression' && item.callee.name === 'circle'
)
return (hasStartProfileAt && pipeExpression.body.length > 2) || hasCircle
}
export function canRectangleTool({
export function canRectangleOrCircleTool({
sketchDetails,
}: {
sketchDetails: SketchDetails | null
@ -1964,3 +2063,25 @@ export function canRectangleTool({
if (err(node)) return false
return node.node?.declarations?.[0]?.init.type !== 'PipeExpression'
}
/** If the sketch contains `close` or `circle` stdlib functions it must be closed */
export function isClosedSketch({
sketchDetails,
}: {
sketchDetails: SketchDetails | null
}): boolean {
const node = getNodeFromPath<VariableDeclaration>(
kclManager.ast,
sketchDetails?.sketchPathToNode || [],
'VariableDeclaration'
)
// This should not be returning false, and it should be caught
// but we need to simulate old behavior to move on.
if (err(node)) return false
if (node.node?.declarations?.[0]?.init.type !== 'PipeExpression') return false
return node.node.declarations[0].init.body.some(
(yo) =>
yo.type === 'CallExpression' &&
(yo.callee.name === 'close' || yo.callee.name === 'circle')
)
}

View File

@ -937,6 +937,9 @@ mod tests {
fn get_autocomplete_snippet_circle() {
let circle_fn: Box<dyn StdLibFn> = Box::new(crate::std::shapes::Circle);
let snippet = circle_fn.to_autocomplete_snippet().unwrap();
assert_eq!(snippet, r#"circle([${0:3.14}, ${1:3.14}], ${2:3.14}, ${3:%})${}"#);
assert_eq!(
snippet,
r#"circle({ center: [${0:3.14}, ${1:3.14}], radius: ${2:3.14} }, ${3:%})${}"#
);
}
}

View File

@ -1416,6 +1416,19 @@ pub enum Path {
/// arc's direction
ccw: bool,
},
/// a complete arc
Circle {
#[serde(flatten)]
base: BasePath,
/// the arc's center
#[ts(type = "[number, number]")]
center: [f64; 2],
/// the arc's radius
radius: f64,
/// arc's direction
// Maybe this one's not needed since it's a full revolution?
ccw: bool,
},
/// A path that is horizontal.
Horizontal {
#[serde(flatten)]
@ -1448,6 +1461,7 @@ impl Path {
Path::Base { base } => base.geo_meta.id,
Path::TangentialArcTo { base, .. } => base.geo_meta.id,
Path::TangentialArc { base, .. } => base.geo_meta.id,
Path::Circle { base, .. } => base.geo_meta.id,
}
}
@ -1459,6 +1473,7 @@ impl Path {
Path::Base { base } => base.tag.clone(),
Path::TangentialArcTo { base, .. } => base.tag.clone(),
Path::TangentialArc { base, .. } => base.tag.clone(),
Path::Circle { base, .. } => base.tag.clone(),
}
}
@ -1470,6 +1485,7 @@ impl Path {
Path::Base { base } => base,
Path::TangentialArcTo { base, .. } => base,
Path::TangentialArc { base, .. } => base,
Path::Circle { base, .. } => base,
}
}
@ -1481,6 +1497,7 @@ impl Path {
Path::Base { base } => Some(base),
Path::TangentialArcTo { base, .. } => Some(base),
Path::TangentialArc { base, .. } => Some(base),
Path::Circle { base, .. } => Some(base),
}
}
}
@ -2572,7 +2589,7 @@ fn transform = (replicaId) => {
fn layer = () => {
return startSketchOn("XY")
|> circle([0, 0], 1, %, $tag1)
|> circle({ center: [0, 0], radius: 1 }, %, $tag1)
|> extrude(10, %)
}
@ -2700,7 +2717,7 @@ fn transform = (replicaId) => {
fn layer = () => {
return startSketchOn("XY")
|> circle([0, 0], 1, %, $tag1)
|> circle({ center: [0, 0], radius: 1 }, %, $tag1)
|> extrude(10, %)
}

View File

@ -261,8 +261,7 @@ impl Args {
&self,
) -> Result<
(
[f64; 2],
f64,
crate::std::shapes::CircleData,
crate::std::shapes::SketchSurfaceOrGroup,
Option<TagDeclarator>,
),
@ -619,6 +618,7 @@ fn from_user_val<T: DeserializeOwned>(arg: &KclValue) -> Option<T> {
impl_from_arg_via_json!(super::sketch::AngledLineData);
impl_from_arg_via_json!(super::sketch::AngledLineToData);
impl_from_arg_via_json!(super::sketch::AngledLineThatIntersectsData);
impl_from_arg_via_json!(super::shapes::CircleData);
impl_from_arg_via_json!(super::sketch::ArcData);
impl_from_arg_via_json!(super::sketch::TangentialArcData);
impl_from_arg_via_json!(super::sketch::BezierData);

View File

@ -50,7 +50,7 @@ pub async fn int(args: Args) -> Result<KclValue, KclError> {
///
/// ```no_run
/// const sketch001 = startSketchOn('XZ')
/// |> circle([0, 0], 2, %)
/// |> circle({ center: [0, 0], radius: 2 }, %)
/// const extrude001 = extrude(5, sketch001)
///
/// const pattern01 = patternTransform(int(ceil(5 / 2)), (id) => {

View File

@ -231,7 +231,7 @@ pub(crate) async fn do_post_extrude(
.flat_map(|path| {
if let Some(Some(actual_face_id)) = face_id_map.get(&path.get_base().geo_meta.id) {
match path {
Path::TangentialArc { .. } | Path::TangentialArcTo { .. } => {
Path::TangentialArc { .. } | Path::TangentialArcTo { .. } | Path::Circle { .. } => {
let extrude_surface = ExtrudeSurface::ExtrudeArc(crate::executor::ExtrudeArc {
face_id: *actual_face_id,
tag: path.get_base().tag.clone(),

View File

@ -42,7 +42,7 @@ pub async fn helix(args: Args) -> Result<KclValue, KclError> {
///
/// ```no_run
/// const part001 = startSketchOn('XY')
/// |> circle([5, 5], 10, %)
/// |> circle({ center: [5, 5], radius: 10 }, %)
/// |> extrude(10, %)
/// |> helix({
/// angleStart: 0,

View File

@ -91,10 +91,10 @@ pub async fn loft(args: Args) -> Result<KclValue, KclError> {
/// |> close(%)
///
/// const circleSketch0 = startSketchOn(offsetPlane('XY', 75))
/// |> circle([0, 100], 50, %)
/// |> circle({ center: [0, 100], radius: 50 }, %)
///
/// const circleSketch1 = startSketchOn(offsetPlane('XY', 150))
/// |> circle([0, 100], 20, %)
/// |> circle({ center: [0, 100], radius: 20 }, %)
///
/// loft([squareSketch, circleSketch0, circleSketch1])
/// ```
@ -110,10 +110,10 @@ pub async fn loft(args: Args) -> Result<KclValue, KclError> {
/// |> close(%)
///
/// const circleSketch0 = startSketchOn(offsetPlane('XY', 75))
/// |> circle([0, 100], 50, %)
/// |> circle({ center: [0, 100], radius: 50 }, %)
///
/// const circleSketch1 = startSketchOn(offsetPlane('XY', 150))
/// |> circle([0, 100], 20, %)
/// |> circle({ center: [0, 100], radius: 20 }, %)
///
/// loft([squareSketch, circleSketch0, circleSketch1], {
/// // This can be set to override the automatically determined

View File

@ -113,7 +113,7 @@ pub async fn pi(args: Args) -> Result<KclValue, KclError> {
/// const circumference = 70
///
/// const exampleSketch = startSketchOn("XZ")
/// |> circle([0, 0], circumference/ (2 * pi()), %)
/// |> circle({ center: [0, 0], radius: circumference/ (2 * pi()) }, %)
///
/// const example = extrude(5, exampleSketch)
/// ```

View File

@ -37,7 +37,7 @@ use crate::{
ast::types::FunctionExpression,
docs::StdLibFn,
errors::KclError,
executor::{KclValue, ProgramMemory, SketchGroup, SketchSurface},
executor::{KclValue, ProgramMemory},
std::kcl_stdlib::KclStdLibFn,
};

View File

@ -117,7 +117,7 @@ pub async fn pattern_transform(args: Args) -> Result<KclValue, KclError> {
/// // Each layer is just a pretty thin cylinder.
/// fn layer = () => {
/// return startSketchOn("XY") // or some other plane idk
/// |> circle([0, 0], 1, %, $tag1)
/// |> circle({ center: [0, 0], radius: 1 }, %, $tag1)
/// |> extrude(h, %)
/// }
/// // The vase is 100 layers tall.
@ -318,7 +318,7 @@ pub async fn pattern_linear_2d(args: Args) -> Result<KclValue, KclError> {
///
/// ```no_run
/// const exampleSketch = startSketchOn('XZ')
/// |> circle([0, 0], 1, %)
/// |> circle({ center: [0, 0], radius: 1 }, %)
/// |> patternLinear2d({
/// axis: [1, 0],
/// repetitions: 6,
@ -651,7 +651,7 @@ pub async fn pattern_circular_3d(args: Args) -> Result<KclValue, KclError> {
///
/// ```no_run
/// const exampleSketch = startSketchOn('XZ')
/// |> circle([0, 0], 1, %)
/// |> circle({ center: [0, 0], radius: 1 }, %)
///
/// const example = extrude(-5, exampleSketch)
/// |> patternCircular3d({

View File

@ -77,7 +77,7 @@ pub async fn offset_plane(args: Args) -> Result<KclValue, KclError> {
/// |> close(%)
///
/// const circleSketch = startSketchOn(offsetPlane('XY', 150))
/// |> circle([0, 100], 50, %)
/// |> circle({ center: [0, 100], radius: 50 }, %)
///
/// loft([squareSketch, circleSketch])
/// ```
@ -93,7 +93,7 @@ pub async fn offset_plane(args: Args) -> Result<KclValue, KclError> {
/// |> close(%)
///
/// const circleSketch = startSketchOn(offsetPlane('XZ', 150))
/// |> circle([0, 100], 50, %)
/// |> circle({ center: [0, 100], radius: 50 }, %)
///
/// loft([squareSketch, circleSketch])
/// ```
@ -109,7 +109,7 @@ pub async fn offset_plane(args: Args) -> Result<KclValue, KclError> {
/// |> close(%)
///
/// const circleSketch = startSketchOn(offsetPlane('YZ', 150))
/// |> circle([0, 100], 50, %)
/// |> circle({ center: [0, 100], radius: 50 }, %)
///
/// loft([squareSketch, circleSketch])
/// ```
@ -125,7 +125,7 @@ pub async fn offset_plane(args: Args) -> Result<KclValue, KclError> {
/// |> close(%)
///
/// const circleSketch = startSketchOn(offsetPlane('-XZ', -150))
/// |> circle([0, 100], 50, %)
/// |> circle({ center: [0, 100], radius: 50 }, %)
///
/// loft([squareSketch, circleSketch])
/// ```

View File

@ -133,7 +133,7 @@ pub async fn revolve(args: Args) -> Result<KclValue, KclError> {
/// ```no_run
/// // A donut shape.
/// const sketch001 = startSketchOn('XY')
/// |> circle([15, 0], 5, %)
/// |> circle({ center: [15, 0], radius: 5 }, %)
/// |> revolve({
/// angle: 360,
/// axis: 'y'
@ -185,7 +185,7 @@ pub async fn revolve(args: Args) -> Result<KclValue, KclError> {
/// |> extrude(20, %)
///
/// const sketch001 = startSketchOn(box, "END")
/// |> circle([10,10], 4, %)
/// |> circle({ center: [10,10], radius: 4 }, %)
/// |> revolve({
/// angle: -90,
/// axis: 'y'
@ -202,7 +202,7 @@ pub async fn revolve(args: Args) -> Result<KclValue, KclError> {
/// |> extrude(20, %)
///
/// const sketch001 = startSketchOn(box, "END")
/// |> circle([10,10], 4, %)
/// |> circle({ center: [10,10], radius: 4 }, %)
/// |> revolve({
/// angle: 90,
/// axis: getOppositeEdge(revolveAxis)
@ -219,7 +219,7 @@ pub async fn revolve(args: Args) -> Result<KclValue, KclError> {
/// |> extrude(20, %)
///
/// const sketch001 = startSketchOn(box, "END")
/// |> circle([10,10], 4, %)
/// |> circle({ center: [10,10], radius: 4 }, %)
/// |> revolve({
/// angle: 90,
/// axis: getOppositeEdge(revolveAxis),

View File

@ -2,14 +2,15 @@
use anyhow::Result;
use derive_docs::stdlib;
use kittycad::types::{Angle, ModelingCmd};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::{
ast::types::TagDeclarator,
errors::KclError,
executor::KclValue,
std::{Args, SketchGroup, SketchSurface},
errors::{KclError, KclErrorDetails},
executor::{BasePath, GeoMeta, KclValue, Path, SketchGroup, SketchSurface},
std::Args,
};
/// A sketch surface or a sketch group.
@ -21,12 +22,24 @@ pub enum SketchSurfaceOrGroup {
SketchGroup(Box<SketchGroup>),
}
/// Data for drawing an angled line that intersects with a given line.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(rename_all = "camelCase")]
// TODO: make sure the docs on the args below are correct.
pub struct CircleData {
/// The center of the circle.
pub center: [f64; 2],
/// The circle radius
pub radius: f64,
}
/// Sketch a circle.
pub async fn circle(args: Args) -> Result<KclValue, KclError> {
let (center, radius, sketch_surface_or_group, tag): ([f64; 2], f64, SketchSurfaceOrGroup, Option<TagDeclarator>) =
let (data, sketch_surface_or_group, tag): (CircleData, SketchSurfaceOrGroup, Option<TagDeclarator>) =
args.get_circle_args()?;
let sketch_group = inner_circle(center, radius, sketch_surface_or_group, tag, args).await?;
let sketch_group = inner_circle(data, sketch_surface_or_group, tag, args).await?;
Ok(KclValue::new_user_val(sketch_group.meta.clone(), sketch_group))
}
@ -35,7 +48,7 @@ pub async fn circle(args: Args) -> Result<KclValue, KclError> {
///
/// ```no_run
/// const exampleSketch = startSketchOn("-XZ")
/// |> circle([0, 0], 10, %)
/// |> circle({ center: [0, 0], radius: 10 }, %)
///
/// const example = extrude(5, exampleSketch)
/// ```
@ -47,7 +60,7 @@ pub async fn circle(args: Args) -> Result<KclValue, KclError> {
/// |> line([0, 30], %)
/// |> line([-30, 0], %)
/// |> close(%)
/// |> hole(circle([0, 15], 5, %), %)
/// |> hole(circle({ center: [0, 15], radius: 5 }, %), %)
///
/// const example = extrude(5, exampleSketch)
/// ```
@ -55,8 +68,7 @@ pub async fn circle(args: Args) -> Result<KclValue, KclError> {
name = "circle",
}]
async fn inner_circle(
center: [f64; 2],
radius: f64,
data: CircleData,
sketch_surface_or_group: SketchSurfaceOrGroup,
tag: Option<TagDeclarator>,
args: Args,
@ -65,23 +77,70 @@ async fn inner_circle(
SketchSurfaceOrGroup::SketchSurface(surface) => surface,
SketchSurfaceOrGroup::SketchGroup(group) => group.on,
};
let mut sketch_group =
crate::std::sketch::inner_start_profile_at([center[0] + radius, center[1]], sketch_surface, None, args.clone())
.await?;
// Call arc.
sketch_group = crate::std::sketch::inner_arc(
crate::std::sketch::ArcData::AnglesAndRadius {
angle_start: 0.0,
angle_end: 360.0,
radius,
},
sketch_group,
tag,
let sketch_group = crate::std::sketch::inner_start_profile_at(
[data.center[0] + data.radius, data.center[1]],
sketch_surface,
None,
args.clone(),
)
.await?;
// Call close.
crate::std::sketch::inner_close(sketch_group, None, args).await
let angle_start = Angle::from_degrees(0.0);
let angle_end = Angle::from_degrees(360.0);
if angle_start == angle_end {
return Err(KclError::Type(KclErrorDetails {
message: "Arc start and end angles must be different".to_string(),
source_ranges: vec![args.source_range],
}));
}
let id = uuid::Uuid::new_v4();
args.batch_modeling_cmd(
id,
ModelingCmd::ExtendPath {
path: sketch_group.id,
segment: kittycad::types::PathSegment::Arc {
start: angle_start,
end: angle_end,
center: data.center.into(),
radius: data.radius,
relative: false,
},
},
)
.await?;
let current_path = Path::Circle {
base: BasePath {
from: data.center,
to: data.center,
tag: tag.clone(),
geo_meta: GeoMeta {
id,
metadata: args.source_range.into(),
},
},
radius: data.radius,
center: data.center,
ccw: angle_start.degrees() < angle_end.degrees(),
};
let mut new_sketch_group = sketch_group.clone();
if let Some(tag) = &tag {
new_sketch_group.add_tag(tag, &current_path);
}
new_sketch_group.value.push(current_path);
args.batch_modeling_cmd(
id,
ModelingCmd::ClosePath {
path_id: new_sketch_group.id,
},
)
.await?;
Ok(new_sketch_group)
}

View File

@ -2059,8 +2059,8 @@ pub async fn hole(args: Args) -> Result<KclValue, KclError> {
/// |> line([5, 0], %)
/// |> line([0, -5], %)
/// |> close(%)
/// |> hole(circle([1, 1], .25, %), %)
/// |> hole(circle([1, 4], .25, %), %)
/// |> hole(circle({ center: [1, 1], radius: .25 }, %), %)
/// |> hole(circle({ center: [1, 4], radius: .25 }, %), %)
///
/// const example = extrude(1, exampleSketch)
/// ```
@ -2077,7 +2077,7 @@ pub async fn hole(args: Args) -> Result<KclValue, KclError> {
/// }
///
/// const exampleSketch = startSketchOn('-XZ')
/// |> circle([0, 0], 3, %)
/// |> circle({ center: [0, 0], radius: 3 }, %)
/// |> hole(squareHoleSketch(), %)
/// const example = extrude(1, exampleSketch)
/// ```

View File

@ -1028,10 +1028,10 @@ const tabs_r = startSketchOn({
|> line([0, -10], %)
|> line([-10, -5], %)
|> close(%)
|> hole(circle([
|> hole(circle({ center: [
width / 2 + thk + hole_diam,
length / 2 - hole_diam
], hole_diam / 2, %), %)
], radius: hole_diam / 2 }, %), %)
|> extrude(-thk, %)
|> patternLinear3d({
axis: [0, -1, 0],
@ -1052,10 +1052,10 @@ const tabs_l = startSketchOn({
|> line([0, -10], %)
|> line([10, -5], %)
|> close(%)
|> hole(circle([
|> hole(circle({ center: [
-width / 2 - thk - hole_diam,
length / 2 - hole_diam
], hole_diam / 2, %), %)
], radius: hole_diam / 2 }, %), %)
|> extrude(-thk, %)
|> patternLinear3d({
axis: [0, -1, 0],
@ -1148,10 +1148,10 @@ const tabs_r = startSketchOn({
|> line([0, -10], %)
|> line([-10, -5], %)
|> close(%)
|> hole(circle([
|> hole(circle({ center: [
width / 2 + thk + hole_diam,
length / 2 - hole_diam
], hole_diam / 2, %), %)
], radius: hole_diam / 2 }, %), %)
|> extrude(-thk, %)
|> patternLinear3d({
axis: [0, -1, 0],
@ -1172,10 +1172,10 @@ const tabs_l = startSketchOn({
|> line([0, -10], %)
|> line([10, -5], %)
|> close(%)
|> hole(circle([
|> hole(circle({ center: [
-width / 2 - thk - hole_diam,
length / 2 - hole_diam
], hole_diam / 2, %), %)
], radius: hole_diam / 2 }, %), %)
|> extrude(-thk, %)
|> patternLinear3d({
axis: [0, -1, 0],

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 102 KiB

View File

@ -1,3 +1,3 @@
const cylinder = startSketchOn('XY')
|> circle([0,0], 22, %)
|> circle({ center: [0, 0], radius: 22 }, %)
|> extrude(14, %)

View File

@ -61,8 +61,8 @@ const case = startSketchOn('XY')
fn m25Screw = (x, y, height) => {
const screw = startSketchOn("XY")
|> startProfileAt([0, 0], %)
|> circle([x, y], 2.5, %)
|> hole(circle([x, y], 1.25, %), %)
|> circle({ center: [x, y], radius: 2.5 }, %)
|> hole(circle({ center: [x, y], radius: 1.25 }, %), %)
|> extrude(height, %)
return screw
}

View File

@ -1,6 +1,7 @@
// A mounting bracket for the Focusrite Scarlett Solo audio interface
// This is a bracket that holds an audio device underneath a desk or shelf. The audio device has dimensions of 144mm wide, 80mm length and 45mm depth with fillets of 6mm. This mounting bracket is designed to be 3D printed with PLA material
// define constants in mm
const radius = 6.0
const width = 144.0
@ -79,10 +80,13 @@ const tabsR = startSketchOn(tabPlane)
|> line([0, -tabLength / 3 * 2], %, $edge12)
|> line([-tabWidth, -tabLength / 3], %, $edge13)
|> close(%, $edge14)
|> hole(circle([
|> hole(circle({
center: [
width / 2 + thk + tabWidth / 2,
length / 2 + thk - (tabLength / (3 / 2))
], holeDiam / 2, %), %)
],
radius: holeDiam / 2
}, %), %)
|> extrude(-tabThk, %)
|> fillet({
radius: holeDiam / 2,
@ -104,10 +108,13 @@ const tabsL = startSketchOn(tabPlane)
|> line([0, -tabLength / 3 * 2], %, $edge22)
|> line([tabWidth, -tabLength / 3], %, $edge23)
|> close(%, $edge24)
|> hole(circle([
|> hole(circle({
center: [
-width / 2 - thk - (tabWidth / 2),
length / 2 + thk - (tabLength / (3 / 2))
], holeDiam / 2, %), %)
],
radius: holeDiam / 2
}, %), %)
|> extrude(-tabThk, %)
|> fillet({
radius: holeDiam / 2,

View File

@ -80,10 +80,13 @@ const tabsR = startSketchOn(tabPlane)
|> line([0, -tabLength / 3 * 2], %, $edge12)
|> line([-tabWidth, -tabLength / 3], %, $edge13)
|> close(%, $edge14)
|> hole(circle([
|> hole(circle({
center: [
width / 2 + thk + tabWidth / 2,
length / 2 + thk - (tabLength / (3 / 2))
], holeDiam / 2, %), %)
],
radius: holeDiam / 2
}, %), %)
|> extrude(-tabThk, %)
|> fillet({
radius: holeDiam / 2,
@ -105,10 +108,13 @@ const tabsL = startSketchOn(tabPlane)
|> line([0, -tabLength / 3 * 2], %, $edge22)
|> line([tabWidth, -tabLength / 3], %, $edge23)
|> close(%, $edge24)
|> hole(circle([
|> hole(circle({
center: [
-width / 2 - thk - (tabWidth / 2),
length / 2 + thk - (tabLength / (3 / 2))
], holeDiam / 2, %), %)
],
radius: holeDiam / 2
}, %), %)
|> extrude(-tabThk, %)
|> fillet({
radius: holeDiam / 2,

View File

@ -1,4 +1,4 @@
const part001 = startSketchOn('XY')
|> circle([5, 5], 10, %)
|> circle({ center: [5, 5], radius: 10 }, %)
|> extrude(10, %)
|> helix({revolutions: 16, angle_start: 0, ccw: true}, %)

View File

@ -1,4 +1,4 @@
const part001 = startSketchOn('XY')
|> circle([5, 5], 10, %)
|> circle({ center: [5, 5], radius: 10 }, %)
|> extrude(10, %)
|> helix({revolutions: 16, angle_start: 0}, %)

View File

@ -1,4 +1,4 @@
const part001 = startSketchOn('XY')
|> circle([5, 5], 10, %)
|> circle({ center: [5, 5], radius: 10 }, %)
|> extrude(-10, %)
|> helix({revolutions: 16, angle_start: 0}, %)

View File

@ -1,4 +1,4 @@
const part001 = startSketchOn('XY')
|> circle([5, 5], 10, %)
|> circle({ center: [5, 5], radius: 10 }, %)
|> extrude(10, %)
|> helix({revolutions: 16, angle_start: 0, length: 3}, %)

View File

@ -39,10 +39,10 @@ const shellExtrude = startSketchOn(s, "start")
|> extrude(-(height - t), %)
const peg = startSketchOn(s, "end")
|> circle([
|> circle({ center: [
-(total_width / 2 - wSegments),
-(total_length / 2 - lSegments)
], bumpDiam / 2, %)
], radius: bumpDiam / 2 }, %)
|> patternLinear2d({
axis: [1, 0],
repetitions: 5,

View File

@ -14,7 +14,7 @@ fn transform = (replicaId) => {
// Each layer is just a pretty thin cylinder with a fillet.
fn layer = () => {
return startSketchOn("XY") // or some other plane idk
|> circle([0, 0], 1, %, $tag1)
|> circle({ center: [0, 0], radius: 1 }, %, $tag1)
|> extrude(h, %)
// |> fillet({
// radius: h / 2.01,

View File

@ -30,22 +30,22 @@ fn caster = (originStart) => {
|> xLine(-3.543, %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
|> hole(circle([
|> hole(circle({ center: [
(3.543 - 2.756) / 2,
(3.543 - 2.756) / 2
], 8.8 / 2 / 25.4, %), %)
|> hole(circle([
], radius: 8.8 / 2 / 25.4}, %), %)
|> hole(circle({ center: [
(3.543 - 2.756) / 2 + 2.756,
(3.543 - 2.756) / 2
], 8.8 / 2 / 25.4, %), %)
|> hole(circle([
], radius: 8.8 / 2 / 25.4 }, %), %)
|> hole(circle({ center: [
(3.543 - 2.756) / 2,
(3.543 - 2.756) / 2 + 2.756
], 8.8 / 2 / 25.4, %), %)
|> hole(circle([
], radius: 8.8 / 2 / 25.4 }, %), %)
|> hole(circle({ center: [
(3.543 - 2.756) / 2 + 2.756,
(3.543 - 2.756) / 2 + 2.756
], 8.8 / 2 / 25.4, %), %)
], radius: 8.8 / 2 / 25.4 }, %), %)
|> extrude(-.25, %)
const sketch002c = startSketchOn(sketch001c, 'START')
@ -71,7 +71,7 @@ fn caster = (originStart) => {
}
}
const sketch003c = startSketchOn(plane002c)
|> circle([0, 1.2], 2.48 / 2, %)
|> circle({ center: [0, 1.2], radius 2.48 / 2 }, %)
const examplec = extrude(-1 - (3 / 16), sketch003c)
return examplec
}

View File

@ -28,22 +28,22 @@ fn caster = (originStart) => {
|> xLine(-3.543, %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
|> hole(circle([
|> hole(circle({ center: [
(3.543 - 2.756) / 2,
(3.543 - 2.756) / 2
], 8.8 / 2 / 25.4, %), %)
|> hole(circle([
], radius: 8.8 / 2 / 25.4 }, %), %)
|> hole(circle({ center: [
(3.543 - 2.756) / 2 + 2.756,
(3.543 - 2.756) / 2
], 8.8 / 2 / 25.4, %), %)
|> hole(circle([
], radius: 8.8 / 2 / 25.4 }, %), %)
|> hole(circle({ center: [
(3.543 - 2.756) / 2,
(3.543 - 2.756) / 2 + 2.756
], 8.8 / 2 / 25.4, %), %)
|> hole(circle([
], radius: 8.8 / 2 / 25.4 }, %), %)
|> hole(circle({ center: [
(3.543 - 2.756) / 2 + 2.756,
(3.543 - 2.756) / 2 + 2.756
], 8.8 / 2 / 25.4, %), %)
], radius: 8.8 / 2 / 25.4 }, %), %)
|> extrude(-.25, %)
const sketch002c = startSketchOn(sketch001c, 'START')
@ -69,7 +69,7 @@ fn caster = (originStart) => {
}
}
const sketch003c = startSketchOn(plane002c)
|> circle([0, 1.2], 2.48 / 2, %)
|> circle({ center: [0, 1.2], radisu: 2.48 / 2 }, %)
const examplec = extrude(-1 - (3 / 16), sketch003c)
return examplec
}

View File

@ -12,5 +12,5 @@ const part001 = cube([0,0], 20)
|> extrude(20, %)
const part002 = startSketchOn(part001, "end")
|> circle([0, 0], 5, %, $myCircle)
|> circle({ center: [0, 0], radisu: 5 }, %, $myCircle)
|> extrude(5, %)

View File

@ -62,10 +62,10 @@ fn tr = (i) => {
// Create the pegs on the top of the base
const totalBumps = (wbumps * lbumps)-1
const peg = startSketchOn(s, 'end')
|> circle([
|> circle({ center: [
-(pitch*(wbumps-1)/2),
-(pitch*(lbumps-1)/2)
], bumpDiam / 2, %)
], radius: bumpDiam / 2 }, %)
|> patternLinear2d({
axis: [1, 0],
repetitions: wbumps-1,

View File

@ -301,8 +301,8 @@ async fn kcl_test_holes() {
|> line([10, 0], %)
|> line([0, -10], %)
|> close(%)
|> hole(circle([2, 2], .5, %), %)
|> hole(circle([2, 8], .5, %), %)
|> hole(circle({ center: [2, 2], radius: .5 }, %), %)
|> hole(circle({ center: [2, 8], radius: .5 }, %), %)
|> extrude(2, %)
"#;
@ -354,10 +354,10 @@ const holeRadius = 1
const holeIndex = 6
const part = roundedRectangle([0, 0], 20, 20, 4)
|> hole(circle([-holeIndex, holeIndex], holeRadius, %), %)
|> hole(circle([holeIndex, holeIndex], holeRadius, %), %)
|> hole(circle([-holeIndex, -holeIndex], holeRadius, %), %)
|> hole(circle([holeIndex, -holeIndex], holeRadius, %), %)
|> hole(circle({ center: [-holeIndex, holeIndex], radius: holeRadius }, %), %)
|> hole(circle({ center: [holeIndex, holeIndex], radius: holeRadius }, %), %)
|> hole(circle({ center: [-holeIndex, -holeIndex], radius: holeRadius }, %), %)
|> hole(circle({ center: [holeIndex, -holeIndex], radius: holeRadius }, %), %)
|> extrude(2, %)
"#;
@ -367,7 +367,7 @@ const part = roundedRectangle([0, 0], 20, 20, 4)
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_top_level_expression() {
let code = r#"startSketchOn('XY') |> circle([0,0], 22, %) |> extrude(14, %)"#;
let code = r#"startSketchOn('XY') |> circle({ center: [0,0], radius: 22 }, %) |> extrude(14, %)"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
assert_out("top_level_expression", &result);
@ -378,7 +378,7 @@ async fn kcl_test_patterns_linear_basic_with_math() {
let code = r#"const num = 12
const distance = 5
const part = startSketchOn('XY')
|> circle([0,0], 2, %)
|> circle({ center: [0,0], radius: 2 }, %)
|> patternLinear2d({axis: [0,1], repetitions: num -1, distance: distance - 1}, %)
|> extrude(1, %)
"#;
@ -390,7 +390,7 @@ const part = startSketchOn('XY')
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_patterns_linear_basic() {
let code = r#"const part = startSketchOn('XY')
|> circle([0,0], 2, %)
|> circle({ center: [0,0], radius: 2 }, %)
|> patternLinear2d({axis: [0,1], repetitions: 12, distance: 4}, %)
|> extrude(1, %)
"#;
@ -418,7 +418,7 @@ async fn kcl_test_patterns_linear_basic_3d() {
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_patterns_linear_basic_negative_distance() {
let code = r#"const part = startSketchOn('XY')
|> circle([0,0], 2, %)
|> circle({ center: [0,0], radius: 2 }, %)
|> patternLinear2d({axis: [0,1], repetitions: 12, distance: -2}, %)
|> extrude(1, %)
"#;
@ -430,7 +430,7 @@ async fn kcl_test_patterns_linear_basic_negative_distance() {
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_patterns_linear_basic_negative_axis() {
let code = r#"const part = startSketchOn('XY')
|> circle([0,0], 2, %)
|> circle({ center: [0,0], radius: 2 }, %)
|> patternLinear2d({axis: [0,-1], repetitions: 12, distance: 2}, %)
|> extrude(1, %)
"#;
@ -442,7 +442,7 @@ async fn kcl_test_patterns_linear_basic_negative_axis() {
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_patterns_linear_basic_holes() {
let code = r#"const circles = startSketchOn('XY')
|> circle([5, 5], 1, %)
|> circle({ center: [5, 5], radius: 1 }, %)
|> patternLinear2d({axis: [1,1], repetitions: 12, distance: 3}, %)
const rectangle = startSketchOn('XY')
@ -463,7 +463,7 @@ const rectangle = startSketchOn('XY')
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_patterns_circular_basic_2d() {
let code = r#"const part = startSketchOn('XY')
|> circle([0,0], 2, %)
|> circle({ center: [0,0], radius: 2 }, %)
|> patternCircular2d({center: [20, 20], repetitions: 12, arcDegrees: 210, rotateDuplicates: true}, %)
|> extrude(1, %)
"#;
@ -787,8 +787,8 @@ async fn kcl_test_stdlib_kcl_error_right_code_path() {
|> line([10, 0], %)
|> line([0, -10], %)
|> close(%)
|> hole(circle([2, 2], .5), %)
|> hole(circle([2, 8], .5, %), %)
|> hole(circle({ center: [2, 2], radius: .5 }), %)
|> hole(circle({ center: [2, 8], radius: .5 }, %), %)
|> extrude(2, %)
"#;
@ -816,7 +816,7 @@ const part001 = cube([0,0], 20)
|> extrude(20, %)
const part002 = startSketchOn(part001, "end")
|> circle([0, 0], 5, %)
|> circle({ center: [0, 0], radius: 5 }, %)
|> extrude(5, %)
"#;
@ -1085,7 +1085,7 @@ async fn kcl_test_revolve_on_face_circle_edge() {
|> extrude(20, %)
const sketch001 = startSketchOn(box, "END")
|> circle([10,10], 4, %)
|> circle({ center: [10,10], radius: 4 }, %)
|> revolve({
angle: 90,
axis: getOppositeEdge(revolveAxis)
@ -1107,7 +1107,7 @@ async fn kcl_test_revolve_on_face_circle() {
|> extrude(20, %)
const sketch001 = startSketchOn(box, "END")
|> circle([10,10], 4, %)
|> circle({ center: [10,10], radius: 4 }, %)
|> revolve({
angle: -90,
axis: 'y'
@ -1147,7 +1147,7 @@ const sketch001 = startSketchOn(box, "end")
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_basic_revolve_circle() {
let code = r#"const sketch001 = startSketchOn('XY')
|> circle([15, 0], 5, %)
|> circle({ center: [15, 0], radius: 5 }, %)
|> revolve({
angle: 360,
axis: 'y'
@ -1271,10 +1271,10 @@ async fn kcl_test_member_expression_in_params() {
z_axis: { x: 0, y: 1, z: 0 }
}
})
|> circle([0, 0], capDia / 2, %)
|> circle({ center: [0, 0], radius: capDia / 2 }, %)
|> extrude(capHeadLength, %)
const screw = startSketchOn(screwHead, "start")
|> circle([0, 0], dia / 2, %)
|> circle({ center: [0, 0], radius: dia / 2 }, %)
|> extrude(length, %)
return screw
}
@ -1343,7 +1343,7 @@ async fn kcl_test_error_empty_start_sketch_on_string() {
|> extrude(100, %)
const secondSketch = startSketchOn(part001, '')
|> circle([-20, 50], 40, %)
|> circle({ center: [-20, 50], radius: 40 }, %)
|> extrude(20, %)
"#;
@ -1373,7 +1373,7 @@ fn squareHole = (l, w) => {
}
const extrusion = startSketchOn('XY')
|> circle([0, 0], dia/2, %)
|> circle({ center: [0, 0], radius: dia/2 }, %)
|> hole(squareHole(length, width, height), %)
|> extrude(height, %)
"#;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 0 B