Compare commits
50 Commits
franknoiro
...
kurt-circl
Author | SHA1 | Date | |
---|---|---|---|
c26709c06a | |||
fa580d4035 | |||
93d3df4877 | |||
9fafc90c57 | |||
04e82bf728 | |||
0abe4c4082 | |||
266c601fd4 | |||
28a63194c7 | |||
a6aff874bb | |||
25080e9895 | |||
99ffc82ffa | |||
4219a2c31d | |||
bb265ca833 | |||
63dea715bc | |||
15871e6245 | |||
7ab9b3ee46 | |||
e794c5b0e9 | |||
a1dd884013 | |||
994f71bce5 | |||
b55652f9b7 | |||
9d6a09766c | |||
840e75e5a1 | |||
466511ac46 | |||
85abcde086 | |||
48d347b4de | |||
b3c4aec8db | |||
a59de4efa3 | |||
57a460f57a | |||
59284ffa50 | |||
5592185371 | |||
998b194712 | |||
6753a9e9f8 | |||
abb8b95b88 | |||
94b0510521 | |||
72e522dd51 | |||
706af591c5 | |||
a1f8ac4548 | |||
0d5a8aea93 | |||
3e4316b614 | |||
c6577a5ae6 | |||
379960561d | |||
8d912fa62a | |||
bfd92a626b | |||
44e07ca6e5 | |||
22c854815a | |||
3409aa5cfd | |||
c56c446e15 | |||
1988888699 | |||
acfa95f728 | |||
b7c71c01cf |
2
.github/workflows/playwright.yml
vendored
@ -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:
|
||||
|
@ -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],
|
||||
|
@ -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],
|
||||
|
@ -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],
|
||||
|
@ -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],
|
||||
|
@ -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],
|
||||
|
@ -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],
|
||||
|
@ -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],
|
||||
|
@ -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],
|
||||
|
@ -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],
|
||||
|
@ -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],
|
||||
|
@ -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],
|
||||
|
@ -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, %)`)
|
||||
|
||||
|
@ -149,14 +149,16 @@ test.describe('Sketch tests', () => {
|
||||
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
await page.mouse.click(700, 200)
|
||||
await expect(async () => {
|
||||
await page.mouse.click(700, 200)
|
||||
|
||||
await expect.poll(u.normalisedEditorCode)
|
||||
.toBe(`const sketch001 = startSketchOn('XZ')
|
||||
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,
|
||||
}) => {
|
||||
|
@ -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',
|
||||
|
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 37 KiB |
@ -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`
|
||||
|
@ -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 =
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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,19 +458,21 @@ export class SceneEntities {
|
||||
maybeModdedAst,
|
||||
sketchGroup.start.__geoMeta.sourceRange
|
||||
)
|
||||
const _profileStart = profileStart({
|
||||
from: sketchGroup.start.from,
|
||||
id: sketchGroup.start.__geoMeta.id,
|
||||
pathToNode: segPathToNode,
|
||||
scale: factor,
|
||||
theme: sceneInfra._theme,
|
||||
})
|
||||
_profileStart.layers.set(SKETCH_LAYER)
|
||||
_profileStart.traverse((child) => {
|
||||
child.layers.set(SKETCH_LAYER)
|
||||
})
|
||||
group.add(_profileStart)
|
||||
this.activeSegments[JSON.stringify(segPathToNode)] = _profileStart
|
||||
if (sketchGroup?.value?.[0].type !== 'Circle') {
|
||||
const _profileStart = createProfileStartHandle({
|
||||
from: sketchGroup.start.from,
|
||||
id: sketchGroup.start.__geoMeta.id,
|
||||
pathToNode: segPathToNode,
|
||||
scale: factor,
|
||||
theme: sceneInfra._theme,
|
||||
})
|
||||
_profileStart.layers.set(SKETCH_LAYER)
|
||||
_profileStart.traverse((child) => {
|
||||
child.layers.set(SKETCH_LAYER)
|
||||
})
|
||||
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,
|
||||
to: [intersection2d.x, intersection2d.y],
|
||||
from: [lastSegment.to[0], lastSegment.to[1]],
|
||||
input: {
|
||||
type: 'straight-segment',
|
||||
to: [intersection2d.x, intersection2d.y],
|
||||
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,
|
||||
to: [intersectionPoint.twoD.x, intersectionPoint.twoD.y],
|
||||
from: [prevSegment.from[0], prevSegment.from[1]],
|
||||
input: {
|
||||
type: 'straight-segment',
|
||||
to: [intersectionPoint.twoD.x, intersectionPoint.twoD.y],
|
||||
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,
|
||||
from,
|
||||
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) {
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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'
|
||||
|
@ -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'
|
||||
|
||||
|
@ -9,8 +9,8 @@ import {
|
||||
getTransformInfos,
|
||||
transformAstSketchLines,
|
||||
PathToNodeMap,
|
||||
TransformInfo,
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { TransformInfo } from 'lang/std/stdTypes'
|
||||
import {
|
||||
SetAngleLengthModal,
|
||||
createSetAngleLengthModal,
|
||||
|
@ -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'
|
||||
|
@ -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'
|
||||
|
@ -9,8 +9,8 @@ import {
|
||||
PathToNodeMap,
|
||||
getTransformInfos,
|
||||
transformAstSketchLines,
|
||||
TransformInfo,
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { TransformInfo } from 'lang/std/stdTypes'
|
||||
import {
|
||||
SetAngleLengthModal,
|
||||
createSetAngleLengthModal,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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 }
|
||||
}
|
||||
|
@ -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],
|
||||
from: [0, 0],
|
||||
input: {
|
||||
type: 'straight-segment',
|
||||
from: [0, 0],
|
||||
to: [2, 3],
|
||||
},
|
||||
fnName: 'lineTo',
|
||||
pathToNode: [
|
||||
['body', ''],
|
||||
|
@ -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(
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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) =>
|
||||
|
@ -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')
|
||||
)
|
||||
}
|
||||
|
@ -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:%})${}"#
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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, %)
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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) => {
|
||||
|
@ -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(),
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
/// ```
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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({
|
||||
|
@ -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])
|
||||
/// ```
|
||||
|
@ -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),
|
||||
|
@ -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, ¤t_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)
|
||||
}
|
||||
|
@ -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)
|
||||
/// ```
|
||||
|
@ -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],
|
||||
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 63 KiB |
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 136 KiB |
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 136 KiB |
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 102 KiB |
@ -1,3 +1,3 @@
|
||||
const cylinder = startSketchOn('XY')
|
||||
|> circle([0,0], 22, %)
|
||||
|> circle({ center: [0, 0], radius: 22 }, %)
|
||||
|> extrude(14, %)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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([
|
||||
width / 2 + thk + tabWidth / 2,
|
||||
length / 2 + thk - (tabLength / (3 / 2))
|
||||
], holeDiam / 2, %), %)
|
||||
|> hole(circle({
|
||||
center: [
|
||||
width / 2 + thk + tabWidth / 2,
|
||||
length / 2 + thk - (tabLength / (3 / 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([
|
||||
-width / 2 - thk - (tabWidth / 2),
|
||||
length / 2 + thk - (tabLength / (3 / 2))
|
||||
], holeDiam / 2, %), %)
|
||||
|> hole(circle({
|
||||
center: [
|
||||
-width / 2 - thk - (tabWidth / 2),
|
||||
length / 2 + thk - (tabLength / (3 / 2))
|
||||
],
|
||||
radius: holeDiam / 2
|
||||
}, %), %)
|
||||
|> extrude(-tabThk, %)
|
||||
|> fillet({
|
||||
radius: holeDiam / 2,
|
||||
|
@ -80,10 +80,13 @@ const tabsR = startSketchOn(tabPlane)
|
||||
|> line([0, -tabLength / 3 * 2], %, $edge12)
|
||||
|> line([-tabWidth, -tabLength / 3], %, $edge13)
|
||||
|> close(%, $edge14)
|
||||
|> hole(circle([
|
||||
width / 2 + thk + tabWidth / 2,
|
||||
length / 2 + thk - (tabLength / (3 / 2))
|
||||
], holeDiam / 2, %), %)
|
||||
|> hole(circle({
|
||||
center: [
|
||||
width / 2 + thk + tabWidth / 2,
|
||||
length / 2 + thk - (tabLength / (3 / 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([
|
||||
-width / 2 - thk - (tabWidth / 2),
|
||||
length / 2 + thk - (tabLength / (3 / 2))
|
||||
], holeDiam / 2, %), %)
|
||||
|> hole(circle({
|
||||
center: [
|
||||
-width / 2 - thk - (tabWidth / 2),
|
||||
length / 2 + thk - (tabLength / (3 / 2))
|
||||
],
|
||||
radius: holeDiam / 2
|
||||
}, %), %)
|
||||
|> extrude(-tabThk, %)
|
||||
|> fillet({
|
||||
radius: holeDiam / 2,
|
||||
|
@ -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}, %)
|
||||
|
@ -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}, %)
|
||||
|
@ -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}, %)
|
||||
|
@ -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}, %)
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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, %)
|
||||
|
@ -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,
|
||||
|
@ -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, %)
|
||||
"#;
|
||||
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 0 B |