Compare commits
50 Commits
api-deux-p
...
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
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-14]
|
os: [ubuntu-latest, windows-latest, macos-14]
|
||||||
timeout-minutes: 40
|
timeout-minutes: 60
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
needs: check-rust-changes
|
needs: check-rust-changes
|
||||||
steps:
|
steps:
|
||||||
|
@ -270,6 +270,26 @@ const extrusion = extrude(5, sketch001)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -479,6 +499,26 @@ const extrusion = extrude(5, sketch001)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
|
@ -274,6 +274,26 @@ const extrusion = extrude(5, sketch001)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -483,6 +503,26 @@ const extrusion = extrude(5, sketch001)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
|
@ -189,6 +189,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -398,6 +418,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -609,6 +649,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -818,6 +878,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
|
@ -188,6 +188,26 @@ const extrusion = extrude(10, sketch001)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -397,6 +417,26 @@ const extrusion = extrude(10, sketch001)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -608,6 +648,26 @@ const extrusion = extrude(10, sketch001)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -817,6 +877,26 @@ const extrusion = extrude(10, sketch001)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
|
@ -190,6 +190,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -399,6 +419,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -610,6 +650,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -819,6 +879,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
|
@ -282,6 +282,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -491,6 +511,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -702,6 +742,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -911,6 +971,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
|
@ -187,6 +187,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -396,6 +416,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -607,6 +647,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -816,6 +876,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
|
@ -187,6 +187,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -396,6 +416,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -607,6 +647,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -816,6 +876,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
|
@ -200,6 +200,26 @@ const exampleSketch = startSketchOn('XZ')
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -409,6 +429,26 @@ const exampleSketch = startSketchOn('XZ')
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -620,6 +660,26 @@ const exampleSketch = startSketchOn('XZ')
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -829,6 +889,26 @@ const exampleSketch = startSketchOn('XZ')
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
|
@ -193,6 +193,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -402,6 +422,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -613,6 +653,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -822,6 +882,26 @@ const example = extrude(10, exampleSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
|
@ -415,6 +415,26 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
@ -819,6 +839,26 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
|
|||||||
to: [number, number],
|
to: [number, number],
|
||||||
type: "TangentialArc",
|
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.
|
// The from point.
|
||||||
from: [number, number],
|
from: [number, number],
|
||||||
|
@ -558,7 +558,7 @@ test.describe('Editor tests', () => {
|
|||||||
await page.keyboard.press('ArrowDown')
|
await page.keyboard.press('ArrowDown')
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
await page.keyboard.type(`const extrusion = startSketchOn('XY')
|
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), %)
|
|> hole(squareHole(length, width, height), %)
|
||||||
|> extrude(height, %)`)
|
|> extrude(height, %)`)
|
||||||
|
|
||||||
|
@ -149,14 +149,16 @@ test.describe('Sketch tests', () => {
|
|||||||
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
await page.mouse.click(700, 200)
|
await expect(async () => {
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
|
||||||
await expect.poll(u.normalisedEditorCode)
|
await expect.poll(u.normalisedEditorCode, { timeout: 1000 })
|
||||||
.toBe(`const sketch001 = startSketchOn('XZ')
|
.toBe(`const sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([12.34, -12.34], %)
|
|> startProfileAt([12.34, -12.34], %)
|
||||||
|> line([-12.34, 12.34], %)
|
|> line([-12.34, 12.34], %)
|
||||||
|
|
||||||
`)
|
`)
|
||||||
|
}).toPass({ timeout: 40_000, intervals: [1_000] })
|
||||||
})
|
})
|
||||||
test('Can exit selection of face', async ({ page }) => {
|
test('Can exit selection of face', async ({ page }) => {
|
||||||
// Load the app with the code panes
|
// 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 ({
|
test('Can edit a sketch that has been extruded in the same pipe', async ({
|
||||||
page,
|
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(
|
test.describe(
|
||||||
'Client side scene scale should match engine scale',
|
'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)
|
svg(startSketchOn(keychain, 'end'), [-33, 32], -thickness)
|
||||||
|
|
||||||
startSketchOn(keychain, 'end')
|
startSketchOn(keychain, 'end')
|
||||||
|> circle([
|
|> circle({ center: [
|
||||||
width / 2,
|
width / 2,
|
||||||
height - (keychainHoleSize + 1.5)
|
height - (keychainHoleSize + 1.5)
|
||||||
], keychainHoleSize, %)
|
], radius: keychainHoleSize }, %)
|
||||||
|> extrude(-thickness, %)`
|
|> extrude(-thickness, %)`
|
||||||
|
|
||||||
export const TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR = `const thing = 1`
|
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"]',
|
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', () => {
|
test.describe('Testing deleting a segment', () => {
|
||||||
const _deleteSegmentSequence =
|
const _deleteSegmentSequence =
|
||||||
|
@ -34,13 +34,11 @@ import { CustomIcon, CustomIconName } from 'components/CustomIcon'
|
|||||||
import { ConstrainInfo } from 'lang/std/stdTypes'
|
import { ConstrainInfo } from 'lang/std/stdTypes'
|
||||||
import { getConstraintInfo } from 'lang/std/sketch'
|
import { getConstraintInfo } from 'lang/std/sketch'
|
||||||
import { Dialog, Popover, Transition } from '@headlessui/react'
|
import { Dialog, Popover, Transition } from '@headlessui/react'
|
||||||
import { LineInputsType } from 'lang/std/sketchcombos'
|
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { InstanceProps, create } from 'react-modal-promise'
|
import { InstanceProps, create } from 'react-modal-promise'
|
||||||
import { executeAst } from 'lang/langHelpers'
|
import { executeAst } from 'lang/langHelpers'
|
||||||
import {
|
import {
|
||||||
deleteSegmentFromPipeExpression,
|
deleteSegmentFromPipeExpression,
|
||||||
makeRemoveSingleConstraintInput,
|
|
||||||
removeSingleConstraintInfo,
|
removeSingleConstraintInfo,
|
||||||
} from 'lang/modifyAst'
|
} from 'lang/modifyAst'
|
||||||
import { ActionButton } from 'components/ActionButton'
|
import { ActionButton } from 'components/ActionButton'
|
||||||
@ -126,7 +124,8 @@ export const ClientSideScene = ({
|
|||||||
} else if (
|
} else if (
|
||||||
state.matches({ Sketch: 'Line tool' }) ||
|
state.matches({ Sketch: 'Line tool' }) ||
|
||||||
state.matches({ Sketch: 'Tangential arc to' }) ||
|
state.matches({ Sketch: 'Tangential arc to' }) ||
|
||||||
state.matches({ Sketch: 'Rectangle tool' })
|
state.matches({ Sketch: 'Rectangle tool' }) ||
|
||||||
|
state.matches({ Sketch: 'Circle tool' })
|
||||||
) {
|
) {
|
||||||
cursor = 'crosshair'
|
cursor = 'crosshair'
|
||||||
} else {
|
} else {
|
||||||
@ -514,6 +513,11 @@ const ConstraintSymbol = ({
|
|||||||
displayName: 'Intersection Offset',
|
displayName: 'Intersection Offset',
|
||||||
iconName: 'intersection-offset',
|
iconName: 'intersection-offset',
|
||||||
},
|
},
|
||||||
|
radius: {
|
||||||
|
varName: 'radius',
|
||||||
|
displayName: 'Radius',
|
||||||
|
iconName: 'dimension',
|
||||||
|
},
|
||||||
|
|
||||||
// implicit constraints
|
// implicit constraints
|
||||||
vertical: {
|
vertical: {
|
||||||
@ -542,12 +546,10 @@ const ConstraintSymbol = ({
|
|||||||
iconName: 'dimension',
|
iconName: 'dimension',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
const varName =
|
const varName = _type in varNameMap ? varNameMap[_type].varName : 'var'
|
||||||
_type in varNameMap ? varNameMap[_type as LineInputsType].varName : 'var'
|
const name: CustomIconName = varNameMap[_type].iconName
|
||||||
const name: CustomIconName = varNameMap[_type as LineInputsType].iconName
|
const displayName = varNameMap[_type]?.displayName
|
||||||
const displayName = varNameMap[_type as LineInputsType]?.displayName
|
const implicitDesc = varNameMap[_type]?.implicitConstraintDesc
|
||||||
const implicitDesc =
|
|
||||||
varNameMap[_type as LineInputsType]?.implicitConstraintDesc
|
|
||||||
|
|
||||||
const _node = useMemo(
|
const _node = useMemo(
|
||||||
() => getNodeFromPath<Expr>(kclManager.ast, pathToNode),
|
() => getNodeFromPath<Expr>(kclManager.ast, pathToNode),
|
||||||
@ -604,13 +606,10 @@ const ConstraintSymbol = ({
|
|||||||
if (trap(_node1)) return Promise.reject(_node1)
|
if (trap(_node1)) return Promise.reject(_node1)
|
||||||
const shallowPath = _node1.shallowPath
|
const shallowPath = _node1.shallowPath
|
||||||
|
|
||||||
const input = makeRemoveSingleConstraintInput(
|
if (!context.sketchDetails || !argPosition) return
|
||||||
argPosition,
|
|
||||||
shallowPath
|
|
||||||
)
|
|
||||||
if (!input || !context.sketchDetails) return
|
|
||||||
const transform = removeSingleConstraintInfo(
|
const transform = removeSingleConstraintInfo(
|
||||||
input,
|
shallowPath,
|
||||||
|
argPosition,
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
kclManager.programMemory
|
kclManager.programMemory
|
||||||
)
|
)
|
||||||
|
@ -62,9 +62,10 @@ import {
|
|||||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
||||||
import { executeAst } from 'lang/langHelpers'
|
import { executeAst } from 'lang/langHelpers'
|
||||||
import {
|
import {
|
||||||
|
circleSegment,
|
||||||
createArcGeometry,
|
createArcGeometry,
|
||||||
dashedStraight,
|
dashedStraight,
|
||||||
profileStart,
|
createProfileStartHandle,
|
||||||
straightSegment,
|
straightSegment,
|
||||||
tangentialArcToSegment,
|
tangentialArcToSegment,
|
||||||
} from './segments'
|
} from './segments'
|
||||||
@ -72,6 +73,7 @@ import {
|
|||||||
addCallExpressionsToPipe,
|
addCallExpressionsToPipe,
|
||||||
addCloseToPipe,
|
addCloseToPipe,
|
||||||
addNewSketchLn,
|
addNewSketchLn,
|
||||||
|
changeCircleArguments,
|
||||||
changeSketchArguments,
|
changeSketchArguments,
|
||||||
updateStartProfileAtArgs,
|
updateStartProfileAtArgs,
|
||||||
} from 'lang/std/sketch'
|
} from 'lang/std/sketch'
|
||||||
@ -87,6 +89,7 @@ import {
|
|||||||
createArrayExpression,
|
createArrayExpression,
|
||||||
createCallExpressionStdLib,
|
createCallExpressionStdLib,
|
||||||
createLiteral,
|
createLiteral,
|
||||||
|
createObjectExpression,
|
||||||
createPipeExpression,
|
createPipeExpression,
|
||||||
createPipeSubstitution,
|
createPipeSubstitution,
|
||||||
findUniqueName,
|
findUniqueName,
|
||||||
@ -119,9 +122,22 @@ export const TANGENTIAL_ARC_TO__SEGMENT_DASH =
|
|||||||
'tangential-arc-to-segment-body-dashed'
|
'tangential-arc-to-segment-body-dashed'
|
||||||
export const TANGENTIAL_ARC_TO_SEGMENT = 'tangential-arc-to-segment'
|
export const TANGENTIAL_ARC_TO_SEGMENT = 'tangential-arc-to-segment'
|
||||||
export const TANGENTIAL_ARC_TO_SEGMENT_BODY = 'tangential-arc-to-segment-body'
|
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 SEGMENT_WIDTH_PX = 1.6
|
||||||
export const HIDE_SEGMENT_LENGTH = 75 // in pixels
|
export const HIDE_SEGMENT_LENGTH = 75 // in pixels
|
||||||
export const HIDE_HOVER_SEGMENT_LENGTH = 60 // 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]
|
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) {
|
if (segment.name === PROFILE_START) {
|
||||||
segment.scale.set(factor, factor, factor)
|
segment.scale.set(factor, factor, factor)
|
||||||
}
|
}
|
||||||
@ -422,19 +458,21 @@ export class SceneEntities {
|
|||||||
maybeModdedAst,
|
maybeModdedAst,
|
||||||
sketchGroup.start.__geoMeta.sourceRange
|
sketchGroup.start.__geoMeta.sourceRange
|
||||||
)
|
)
|
||||||
const _profileStart = profileStart({
|
if (sketchGroup?.value?.[0].type !== 'Circle') {
|
||||||
from: sketchGroup.start.from,
|
const _profileStart = createProfileStartHandle({
|
||||||
id: sketchGroup.start.__geoMeta.id,
|
from: sketchGroup.start.from,
|
||||||
pathToNode: segPathToNode,
|
id: sketchGroup.start.__geoMeta.id,
|
||||||
scale: factor,
|
pathToNode: segPathToNode,
|
||||||
theme: sceneInfra._theme,
|
scale: factor,
|
||||||
})
|
theme: sceneInfra._theme,
|
||||||
_profileStart.layers.set(SKETCH_LAYER)
|
})
|
||||||
_profileStart.traverse((child) => {
|
_profileStart.layers.set(SKETCH_LAYER)
|
||||||
child.layers.set(SKETCH_LAYER)
|
_profileStart.traverse((child) => {
|
||||||
})
|
child.layers.set(SKETCH_LAYER)
|
||||||
group.add(_profileStart)
|
})
|
||||||
this.activeSegments[JSON.stringify(segPathToNode)] = _profileStart
|
group.add(_profileStart)
|
||||||
|
this.activeSegments[JSON.stringify(segPathToNode)] = _profileStart
|
||||||
|
}
|
||||||
const callbacks: (() => SegmentOverlayPayload | null)[] = []
|
const callbacks: (() => SegmentOverlayPayload | null)[] = []
|
||||||
sketchGroup.value.forEach((segment, index) => {
|
sketchGroup.value.forEach((segment, index) => {
|
||||||
let segPathToNode = getNodePathFromSourceRange(
|
let segPathToNode = getNodePathFromSourceRange(
|
||||||
@ -499,6 +537,32 @@ export class SceneEntities {
|
|||||||
scale: factor,
|
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 {
|
} else {
|
||||||
seg = straightSegment({
|
seg = straightSegment({
|
||||||
from: segment.from,
|
from: segment.from,
|
||||||
@ -603,16 +667,18 @@ export class SceneEntities {
|
|||||||
kclManager.programMemory.get(variableDeclarationName),
|
kclManager.programMemory.get(variableDeclarationName),
|
||||||
variableDeclarationName
|
variableDeclarationName
|
||||||
)
|
)
|
||||||
if (err(sg)) return sg
|
if (err(sg)) return Promise.reject(sg)
|
||||||
const lastSeg = sg.value?.slice(-1)[0] || sg.start
|
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 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({
|
const mod = addNewSketchLn({
|
||||||
node: _ast,
|
node: _ast,
|
||||||
programMemory: kclManager.programMemory,
|
programMemory: kclManager.programMemory,
|
||||||
to: [lastSeg.to[0], lastSeg.to[1]],
|
input: {
|
||||||
from: [lastSeg.to[0], lastSeg.to[1]],
|
type: 'straight-segment',
|
||||||
|
to: lastSeg.to,
|
||||||
|
from: lastSeg.to,
|
||||||
|
},
|
||||||
fnName: segmentName,
|
fnName: segmentName,
|
||||||
pathToNode: sketchPathToNode,
|
pathToNode: sketchPathToNode,
|
||||||
})
|
})
|
||||||
@ -683,8 +749,11 @@ export class SceneEntities {
|
|||||||
const tmp = addNewSketchLn({
|
const tmp = addNewSketchLn({
|
||||||
node: kclManager.ast,
|
node: kclManager.ast,
|
||||||
programMemory: kclManager.programMemory,
|
programMemory: kclManager.programMemory,
|
||||||
to: [intersection2d.x, intersection2d.y],
|
input: {
|
||||||
from: [lastSegment.to[0], lastSegment.to[1]],
|
type: 'straight-segment',
|
||||||
|
to: [intersection2d.x, intersection2d.y],
|
||||||
|
from: lastSegment.to,
|
||||||
|
},
|
||||||
fnName:
|
fnName:
|
||||||
lastSegment.type === 'TangentialArcTo'
|
lastSegment.type === 'TangentialArcTo'
|
||||||
? 'tangentialArcTo'
|
? 'tangentialArcTo'
|
||||||
@ -748,6 +817,11 @@ export class SceneEntities {
|
|||||||
const startSketchOn = _node1.node?.declarations
|
const startSketchOn = _node1.node?.declarations
|
||||||
const startSketchOnInit = startSketchOn?.[0]?.init
|
const startSketchOnInit = startSketchOn?.[0]?.init
|
||||||
|
|
||||||
|
const sg = sketchGroupFromKclValue(
|
||||||
|
kclManager.programMemory.get(variableDeclarationName),
|
||||||
|
variableDeclarationName
|
||||||
|
)
|
||||||
|
if (err(sg)) return sg
|
||||||
const tags: [string, string, string] = [
|
const tags: [string, string, string] = [
|
||||||
findUniqueName(_ast, 'rectangleSegmentA'),
|
findUniqueName(_ast, 'rectangleSegmentA'),
|
||||||
findUniqueName(_ast, 'rectangleSegmentB'),
|
findUniqueName(_ast, 'rectangleSegmentB'),
|
||||||
@ -805,7 +879,7 @@ export class SceneEntities {
|
|||||||
programMemory.get(variableDeclarationName),
|
programMemory.get(variableDeclarationName),
|
||||||
variableDeclarationName
|
variableDeclarationName
|
||||||
)
|
)
|
||||||
if (err(sketchGroup)) return sketchGroup
|
if (err(sketchGroup)) return Promise.reject(sketchGroup)
|
||||||
const sgPaths = sketchGroup.value
|
const sgPaths = sketchGroup.value
|
||||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||||
|
|
||||||
@ -862,7 +936,7 @@ export class SceneEntities {
|
|||||||
programMemory.get(variableDeclarationName),
|
programMemory.get(variableDeclarationName),
|
||||||
variableDeclarationName
|
variableDeclarationName
|
||||||
)
|
)
|
||||||
if (err(sketchGroup)) return sketchGroup
|
if (err(sketchGroup)) return Promise.reject(sketchGroup)
|
||||||
const sgPaths = sketchGroup.value
|
const sgPaths = sketchGroup.value
|
||||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
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 = ({
|
setupSketchIdleCallbacks = ({
|
||||||
pathToNode,
|
pathToNode,
|
||||||
up,
|
up,
|
||||||
@ -951,8 +1170,11 @@ export class SceneEntities {
|
|||||||
const mod = addNewSketchLn({
|
const mod = addNewSketchLn({
|
||||||
node: kclManager.ast,
|
node: kclManager.ast,
|
||||||
programMemory: kclManager.programMemory,
|
programMemory: kclManager.programMemory,
|
||||||
to: [intersectionPoint.twoD.x, intersectionPoint.twoD.y],
|
input: {
|
||||||
from: [prevSegment.from[0], prevSegment.from[1]],
|
type: 'straight-segment',
|
||||||
|
to: [intersectionPoint.twoD.x, intersectionPoint.twoD.y],
|
||||||
|
from: prevSegment.from,
|
||||||
|
},
|
||||||
// TODO assuming it's always a straight segments being added
|
// TODO assuming it's always a straight segments being added
|
||||||
// as this is easiest, and we'll need to add "tabbing" behavior
|
// as this is easiest, and we'll need to add "tabbing" behavior
|
||||||
// to support other segment types
|
// to support other segment types
|
||||||
@ -1051,11 +1273,8 @@ export class SceneEntities {
|
|||||||
? new Vector2(profileStart.position.x, profileStart.position.y)
|
? new Vector2(profileStart.position.x, profileStart.position.y)
|
||||||
: _intersection2d
|
: _intersection2d
|
||||||
|
|
||||||
const group = getParentGroup(object, [
|
const group = getParentGroup(object, SEGMENT_BODIES_PLUS_PROFILE_START)
|
||||||
STRAIGHT_SEGMENT,
|
const subGroup = getParentGroup(object, [ARROWHEAD, CIRCLE_CENTER_HANDLE])
|
||||||
TANGENTIAL_ARC_TO_SEGMENT,
|
|
||||||
PROFILE_START,
|
|
||||||
])
|
|
||||||
if (!group) return
|
if (!group) return
|
||||||
const pathToNode: PathToNode = structuredClone(group.userData.pathToNode)
|
const pathToNode: PathToNode = structuredClone(group.userData.pathToNode)
|
||||||
const varDecIndex = pathToNode[1][0]
|
const varDecIndex = pathToNode[1][0]
|
||||||
@ -1073,7 +1292,7 @@ export class SceneEntities {
|
|||||||
group.userData.from[0],
|
group.userData.from[0],
|
||||||
group.userData.from[1],
|
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 }
|
let modifiedAst = draftInfo ? draftInfo.truncatedAst : { ...kclManager.ast }
|
||||||
|
|
||||||
const _node = getNodeFromPath<CallExpression>(
|
const _node = getNodeFromPath<CallExpression>(
|
||||||
@ -1096,17 +1315,59 @@ export class SceneEntities {
|
|||||||
modded = updateStartProfileAtArgs({
|
modded = updateStartProfileAtArgs({
|
||||||
node: modifiedAst,
|
node: modifiedAst,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
to,
|
input: {
|
||||||
from,
|
type: 'straight-segment',
|
||||||
|
to: dragTo,
|
||||||
|
from,
|
||||||
|
},
|
||||||
previousProgramMemory: kclManager.programMemory,
|
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 {
|
} else {
|
||||||
modded = changeSketchArguments(
|
modded = changeSketchArguments(
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
kclManager.programMemory,
|
kclManager.programMemory,
|
||||||
[node.start, node.end],
|
[node.start, node.end],
|
||||||
to,
|
{
|
||||||
from
|
type: 'straight-segment',
|
||||||
|
from,
|
||||||
|
to: dragTo,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (trap(modded)) return
|
if (trap(modded)) return
|
||||||
@ -1224,6 +1485,20 @@ export class SceneEntities {
|
|||||||
group,
|
group,
|
||||||
scale: factor,
|
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) {
|
} else if (type === PROFILE_START) {
|
||||||
group.position.set(segment.from[0], segment.from[1], 0)
|
group.position.set(segment.from[0], segment.from[1], 0)
|
||||||
group.scale.set(factor, factor, factor)
|
group.scale.set(factor, factor, factor)
|
||||||
@ -1249,6 +1524,9 @@ export class SceneEntities {
|
|||||||
group.userData.prevSegment = prevSegment
|
group.userData.prevSegment = prevSegment
|
||||||
const arrowGroup = group.getObjectByName(ARROWHEAD) as Group
|
const arrowGroup = group.getObjectByName(ARROWHEAD) as Group
|
||||||
const extraSegmentGroup = group.getObjectByName(EXTRA_SEGMENT_HANDLE)
|
const extraSegmentGroup = group.getObjectByName(EXTRA_SEGMENT_HANDLE)
|
||||||
|
if (!prevSegment) {
|
||||||
|
console.trace('prevSegment is undefined')
|
||||||
|
}
|
||||||
|
|
||||||
const previousPoint =
|
const previousPoint =
|
||||||
prevSegment?.type === 'TangentialArcTo'
|
prevSegment?.type === 'TangentialArcTo'
|
||||||
@ -1344,6 +1622,111 @@ export class SceneEntities {
|
|||||||
angle,
|
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(
|
throttledUpdateDashedArcGeo = throttle(
|
||||||
(
|
(
|
||||||
args: Parameters<typeof createArcGeometry>[0] & {
|
args: Parameters<typeof createArcGeometry>[0] & {
|
||||||
@ -1478,7 +1861,7 @@ export class SceneEntities {
|
|||||||
}
|
}
|
||||||
private _tearDownSketch(
|
private _tearDownSketch(
|
||||||
callDepth = 0,
|
callDepth = 0,
|
||||||
resolve: (val: unknown) => void,
|
resolve: any,
|
||||||
reject: () => void,
|
reject: () => void,
|
||||||
{ removeAxis = true }: { removeAxis?: boolean }
|
{ removeAxis = true }: { removeAxis?: boolean }
|
||||||
) {
|
) {
|
||||||
@ -1508,7 +1891,7 @@ export class SceneEntities {
|
|||||||
this._tearDownSketch(callDepth + 1, resolve, reject, { removeAxis })
|
this._tearDownSketch(callDepth + 1, resolve, reject, { removeAxis })
|
||||||
}, delay)
|
}, delay)
|
||||||
} else {
|
} else {
|
||||||
reject()
|
resolve(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sceneInfra.camControls.enableRotate = true
|
sceneInfra.camControls.enableRotate = true
|
||||||
@ -1520,7 +1903,7 @@ export class SceneEntities {
|
|||||||
removeAxis = true,
|
removeAxis = true,
|
||||||
}: {
|
}: {
|
||||||
removeAxis?: boolean
|
removeAxis?: boolean
|
||||||
} = {}) {
|
} = {}): Promise<void | Error> {
|
||||||
// I think promisifying this is mostly a side effect of not having
|
// I think promisifying this is mostly a side effect of not having
|
||||||
// "setupSketch" correctly capture a promise when it's done
|
// "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
|
// 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.set(obj.userData.baseColor)
|
||||||
mat.color.offsetHSL(0, 0, 0.5)
|
mat.color.offsetHSL(0, 0, 0.5)
|
||||||
}
|
}
|
||||||
const parent = getParentGroup(selected, [
|
const parent = getParentGroup(
|
||||||
STRAIGHT_SEGMENT,
|
selected,
|
||||||
TANGENTIAL_ARC_TO_SEGMENT,
|
SEGMENT_BODIES_PLUS_PROFILE_START
|
||||||
PROFILE_START,
|
)
|
||||||
])
|
|
||||||
if (parent?.userData?.pathToNode) {
|
if (parent?.userData?.pathToNode) {
|
||||||
const updatedAst = parse(recast(kclManager.ast))
|
const updatedAst = parse(recast(kclManager.ast))
|
||||||
if (trap(updatedAst)) return
|
if (trap(updatedAst)) return
|
||||||
@ -1586,6 +1968,16 @@ export class SceneEntities {
|
|||||||
group: parent,
|
group: parent,
|
||||||
scale: factor,
|
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
|
return
|
||||||
}
|
}
|
||||||
@ -1593,11 +1985,10 @@ export class SceneEntities {
|
|||||||
},
|
},
|
||||||
onMouseLeave: ({ selected, ...rest }: OnMouseEnterLeaveArgs) => {
|
onMouseLeave: ({ selected, ...rest }: OnMouseEnterLeaveArgs) => {
|
||||||
editorManager.setHighlightRange([[0, 0]])
|
editorManager.setHighlightRange([[0, 0]])
|
||||||
const parent = getParentGroup(selected, [
|
const parent = getParentGroup(
|
||||||
STRAIGHT_SEGMENT,
|
selected,
|
||||||
TANGENTIAL_ARC_TO_SEGMENT,
|
SEGMENT_BODIES_PLUS_PROFILE_START
|
||||||
PROFILE_START,
|
)
|
||||||
])
|
|
||||||
if (parent) {
|
if (parent) {
|
||||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||||
|
|
||||||
@ -1621,6 +2012,16 @@ export class SceneEntities {
|
|||||||
group: parent,
|
group: parent,
|
||||||
scale: factor,
|
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
|
const isSelected = parent?.userData?.isSelected
|
||||||
@ -1783,7 +2184,7 @@ function prepareTruncatedMemoryAndAst(
|
|||||||
|
|
||||||
export function getParentGroup(
|
export function getParentGroup(
|
||||||
object: any,
|
object: any,
|
||||||
stopAt: string[] = [STRAIGHT_SEGMENT, TANGENTIAL_ARC_TO_SEGMENT]
|
stopAt: string[] = SEGMENT_BODIES
|
||||||
): Group | null {
|
): Group | null {
|
||||||
if (stopAt.includes(object?.userData?.type)) {
|
if (stopAt.includes(object?.userData?.type)) {
|
||||||
return object
|
return object
|
||||||
@ -1830,10 +2231,7 @@ function colorSegment(object: any, color: number) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const straightSegmentBody = getParentGroup(object, [
|
const straightSegmentBody = getParentGroup(object, SEGMENT_BODIES)
|
||||||
STRAIGHT_SEGMENT,
|
|
||||||
TANGENTIAL_ARC_TO_SEGMENT,
|
|
||||||
])
|
|
||||||
if (straightSegmentBody) {
|
if (straightSegmentBody) {
|
||||||
straightSegmentBody.traverse((child) => {
|
straightSegmentBody.traverse((child) => {
|
||||||
if (child instanceof Mesh && !child.userData.ignoreColorChange) {
|
if (child instanceof Mesh && !child.userData.ignoreColorChange) {
|
||||||
|
@ -111,21 +111,21 @@ export class SceneInfra {
|
|||||||
}
|
}
|
||||||
extraSegmentTexture: Texture
|
extraSegmentTexture: Texture
|
||||||
lastMouseState: MouseState = { type: 'idle' }
|
lastMouseState: MouseState = { type: 'idle' }
|
||||||
onDragStartCallback: (arg: OnDragCallbackArgs) => void = () => {}
|
onDragStartCallback: (arg: OnDragCallbackArgs) => any = () => {}
|
||||||
onDragEndCallback: (arg: OnDragCallbackArgs) => void = () => {}
|
onDragEndCallback: (arg: OnDragCallbackArgs) => any = () => {}
|
||||||
onDragCallback: (arg: OnDragCallbackArgs) => void = () => {}
|
onDragCallback: (arg: OnDragCallbackArgs) => any = () => {}
|
||||||
onMoveCallback: (arg: OnMoveCallbackArgs) => void = () => {}
|
onMoveCallback: (arg: OnMoveCallbackArgs) => any = () => {}
|
||||||
onClickCallback: (arg: OnClickCallbackArgs) => void = () => {}
|
onClickCallback: (arg: OnClickCallbackArgs) => any = () => {}
|
||||||
onMouseEnter: (arg: OnMouseEnterLeaveArgs) => void = () => {}
|
onMouseEnter: (arg: OnMouseEnterLeaveArgs) => any = () => {}
|
||||||
onMouseLeave: (arg: OnMouseEnterLeaveArgs) => void = () => {}
|
onMouseLeave: (arg: OnMouseEnterLeaveArgs) => any = () => {}
|
||||||
setCallbacks = (callbacks: {
|
setCallbacks = (callbacks: {
|
||||||
onDragStart?: (arg: OnDragCallbackArgs) => void
|
onDragStart?: (arg: OnDragCallbackArgs) => any
|
||||||
onDragEnd?: (arg: OnDragCallbackArgs) => void
|
onDragEnd?: (arg: OnDragCallbackArgs) => any
|
||||||
onDrag?: (arg: OnDragCallbackArgs) => void
|
onDrag?: (arg: OnDragCallbackArgs) => any
|
||||||
onMove?: (arg: OnMoveCallbackArgs) => void
|
onMove?: (arg: OnMoveCallbackArgs) => any
|
||||||
onClick?: (arg: OnClickCallbackArgs) => void
|
onClick?: (arg: OnClickCallbackArgs) => any
|
||||||
onMouseEnter?: (arg: OnMouseEnterLeaveArgs) => void
|
onMouseEnter?: (arg: OnMouseEnterLeaveArgs) => any
|
||||||
onMouseLeave?: (arg: OnMouseEnterLeaveArgs) => void
|
onMouseLeave?: (arg: OnMouseEnterLeaveArgs) => any
|
||||||
}) => {
|
}) => {
|
||||||
this.onDragStartCallback = callbacks.onDragStart || this.onDragStartCallback
|
this.onDragStartCallback = callbacks.onDragStart || this.onDragStartCallback
|
||||||
this.onDragEndCallback = callbacks.onDragEnd || this.onDragEndCallback
|
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 { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
|
||||||
import { PathToNode, SketchGroup, getTangentialArcToInfo } from 'lang/wasm'
|
import { PathToNode, SketchGroup, getTangentialArcToInfo } from 'lang/wasm'
|
||||||
import {
|
import {
|
||||||
|
CIRCLE_CENTER_HANDLE,
|
||||||
|
CIRCLE_SEGMENT,
|
||||||
|
CIRCLE_SEGMENT_BODY,
|
||||||
|
CIRCLE_SEGMENT_DASH,
|
||||||
EXTRA_SEGMENT_HANDLE,
|
EXTRA_SEGMENT_HANDLE,
|
||||||
EXTRA_SEGMENT_OFFSET_PX,
|
EXTRA_SEGMENT_OFFSET_PX,
|
||||||
HIDE_SEGMENT_LENGTH,
|
HIDE_SEGMENT_LENGTH,
|
||||||
@ -46,7 +50,7 @@ import {
|
|||||||
import { Themes, getThemeColorForThreeJs } from 'lib/theme'
|
import { Themes, getThemeColorForThreeJs } from 'lib/theme'
|
||||||
import { roundOff } from 'lib/utils'
|
import { roundOff } from 'lib/utils'
|
||||||
|
|
||||||
export function profileStart({
|
export function createProfileStartHandle({
|
||||||
from,
|
from,
|
||||||
id,
|
id,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
@ -225,6 +229,28 @@ function createArrowhead(scale = 1, theme: Themes, color?: number): Group {
|
|||||||
arrowGroup.scale.set(scale, scale, scale)
|
arrowGroup.scale.set(scale, scale, scale)
|
||||||
return arrowGroup
|
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(
|
function createExtraSegmentHandle(
|
||||||
scale: number,
|
scale: number,
|
||||||
@ -300,6 +326,86 @@ function createLengthIndicator({
|
|||||||
return lengthIndicatorGroup
|
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({
|
export function tangentialArcToSegment({
|
||||||
prevSegment,
|
prevSegment,
|
||||||
from,
|
from,
|
||||||
|
@ -50,8 +50,7 @@ import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
|
|||||||
import useStateMachineCommands from 'hooks/useStateMachineCommands'
|
import useStateMachineCommands from 'hooks/useStateMachineCommands'
|
||||||
import { modelingMachineCommandConfig } from 'lib/commandBarConfigs/modelingCommandConfig'
|
import { modelingMachineCommandConfig } from 'lib/commandBarConfigs/modelingCommandConfig'
|
||||||
import {
|
import {
|
||||||
STRAIGHT_SEGMENT,
|
SEGMENT_BODIES,
|
||||||
TANGENTIAL_ARC_TO_SEGMENT,
|
|
||||||
getParentGroup,
|
getParentGroup,
|
||||||
getSketchOrientationDetails,
|
getSketchOrientationDetails,
|
||||||
} from 'clientSideScene/sceneEntities'
|
} from 'clientSideScene/sceneEntities'
|
||||||
@ -168,10 +167,7 @@ export const ModelingMachineProvider = ({
|
|||||||
if (event.type !== 'Set mouse state') return {}
|
if (event.type !== 'Set mouse state') return {}
|
||||||
const nextSegmentHoverMap = () => {
|
const nextSegmentHoverMap = () => {
|
||||||
if (event.data.type === 'isHovering') {
|
if (event.data.type === 'isHovering') {
|
||||||
const parent = getParentGroup(event.data.on, [
|
const parent = getParentGroup(event.data.on, SEGMENT_BODIES)
|
||||||
STRAIGHT_SEGMENT,
|
|
||||||
TANGENTIAL_ARC_TO_SEGMENT,
|
|
||||||
])
|
|
||||||
const pathToNode = parent?.userData?.pathToNode
|
const pathToNode = parent?.userData?.pathToNode
|
||||||
const pathToNodeString = JSON.stringify(pathToNode)
|
const pathToNodeString = JSON.stringify(pathToNode)
|
||||||
if (!parent || !pathToNode) return context.segmentHoverMap
|
if (!parent || !pathToNode) return context.segmentHoverMap
|
||||||
@ -187,10 +183,10 @@ export const ModelingMachineProvider = ({
|
|||||||
event.data.type === 'idle' &&
|
event.data.type === 'idle' &&
|
||||||
context.mouseState.type === 'isHovering'
|
context.mouseState.type === 'isHovering'
|
||||||
) {
|
) {
|
||||||
const mouseOnParent = getParentGroup(context.mouseState.on, [
|
const mouseOnParent = getParentGroup(
|
||||||
STRAIGHT_SEGMENT,
|
context.mouseState.on,
|
||||||
TANGENTIAL_ARC_TO_SEGMENT,
|
SEGMENT_BODIES
|
||||||
])
|
)
|
||||||
if (!mouseOnParent || !mouseOnParent?.userData?.pathToNode)
|
if (!mouseOnParent || !mouseOnParent?.userData?.pathToNode)
|
||||||
return context.segmentHoverMap
|
return context.segmentHoverMap
|
||||||
const pathToNodeString = JSON.stringify(
|
const pathToNodeString = JSON.stringify(
|
||||||
@ -204,7 +200,8 @@ export const ModelingMachineProvider = ({
|
|||||||
pathToNodeString,
|
pathToNodeString,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}, 800) as unknown as number
|
// overlay timeout is 1s
|
||||||
|
}, 1000) as unknown as number
|
||||||
return {
|
return {
|
||||||
...context.segmentHoverMap,
|
...context.segmentHoverMap,
|
||||||
[pathToNodeString]: timeoutId,
|
[pathToNodeString]: timeoutId,
|
||||||
|
@ -10,10 +10,10 @@ import {
|
|||||||
transformSecondarySketchLinesTagFirst,
|
transformSecondarySketchLinesTagFirst,
|
||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
TransformInfo,
|
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
import { kclManager } from 'lib/singletons'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
|
|
||||||
export function equalAngleInfo({
|
export function equalAngleInfo({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
|
@ -10,8 +10,8 @@ import {
|
|||||||
transformSecondarySketchLinesTagFirst,
|
transformSecondarySketchLinesTagFirst,
|
||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
TransformInfo,
|
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
import { kclManager } from 'lib/singletons'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@ import {
|
|||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
transformAstSketchLines,
|
transformAstSketchLines,
|
||||||
TransformInfo,
|
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
import { kclManager } from 'lib/singletons'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
|
||||||
|
@ -11,8 +11,8 @@ import {
|
|||||||
transformSecondarySketchLinesTagFirst,
|
transformSecondarySketchLinesTagFirst,
|
||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
TransformInfo,
|
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
||||||
import { createVariableDeclaration } from '../../lang/modifyAst'
|
import { createVariableDeclaration } from '../../lang/modifyAst'
|
||||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||||
|
@ -9,8 +9,8 @@ import {
|
|||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
getRemoveConstraintsTransforms,
|
getRemoveConstraintsTransforms,
|
||||||
transformAstSketchLines,
|
transformAstSketchLines,
|
||||||
TransformInfo,
|
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
import { kclManager } from 'lib/singletons'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@ import {
|
|||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
transformAstSketchLines,
|
transformAstSketchLines,
|
||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
TransformInfo,
|
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
import {
|
import {
|
||||||
SetAngleLengthModal,
|
SetAngleLengthModal,
|
||||||
createSetAngleLengthModal,
|
createSetAngleLengthModal,
|
||||||
|
@ -10,8 +10,8 @@ import {
|
|||||||
transformSecondarySketchLinesTagFirst,
|
transformSecondarySketchLinesTagFirst,
|
||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
TransformInfo,
|
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
||||||
import { createVariableDeclaration } from '../../lang/modifyAst'
|
import { createVariableDeclaration } from '../../lang/modifyAst'
|
||||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||||
|
@ -9,8 +9,8 @@ import {
|
|||||||
transformSecondarySketchLinesTagFirst,
|
transformSecondarySketchLinesTagFirst,
|
||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
TransformInfo,
|
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
||||||
import { createLiteral, createVariableDeclaration } from '../../lang/modifyAst'
|
import { createLiteral, createVariableDeclaration } from '../../lang/modifyAst'
|
||||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||||
|
@ -9,8 +9,8 @@ import {
|
|||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
transformAstSketchLines,
|
transformAstSketchLines,
|
||||||
TransformInfo,
|
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
import {
|
import {
|
||||||
SetAngleLengthModal,
|
SetAngleLengthModal,
|
||||||
createSetAngleLengthModal,
|
createSetAngleLengthModal,
|
||||||
|
@ -24,11 +24,9 @@ export type ToolTip =
|
|||||||
| 'yLineTo'
|
| 'yLineTo'
|
||||||
| 'angledLineThatIntersects'
|
| 'angledLineThatIntersects'
|
||||||
| 'tangentialArcTo'
|
| 'tangentialArcTo'
|
||||||
|
| 'circle'
|
||||||
|
|
||||||
export const toolTips = [
|
export const toolTips: Array<ToolTip> = [
|
||||||
'sketch_line',
|
|
||||||
'move',
|
|
||||||
// original tooltips
|
|
||||||
'line',
|
'line',
|
||||||
'lineTo',
|
'lineTo',
|
||||||
'angledLine',
|
'angledLine',
|
||||||
@ -42,7 +40,7 @@ export const toolTips = [
|
|||||||
'yLineTo',
|
'yLineTo',
|
||||||
'angledLineThatIntersects',
|
'angledLineThatIntersects',
|
||||||
'tangentialArcTo',
|
'tangentialArcTo',
|
||||||
] as any as ToolTip[]
|
]
|
||||||
|
|
||||||
export async function executeAst({
|
export async function executeAst({
|
||||||
ast,
|
ast,
|
||||||
|
@ -20,6 +20,7 @@ import {
|
|||||||
import { enginelessExecutor } from '../lib/testHelpers'
|
import { enginelessExecutor } from '../lib/testHelpers'
|
||||||
import { findUsesOfTagInPipe, getNodePathFromSourceRange } from './queryAst'
|
import { findUsesOfTagInPipe, getNodePathFromSourceRange } from './queryAst'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
import { SimplifiedArgDetails, InputArgKeys } from './std/stdTypes'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await initPromise
|
await initPromise
|
||||||
@ -638,11 +639,27 @@ describe('Testing removeSingleConstraintInfo', () => {
|
|||||||
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
]
|
]
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
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(
|
const mod = removeSingleConstraintInfo(
|
||||||
{
|
pathToNode,
|
||||||
pathToCallExp: pathToNode,
|
argPosition,
|
||||||
[key]: value,
|
|
||||||
},
|
|
||||||
ast,
|
ast,
|
||||||
programMemory
|
programMemory
|
||||||
)
|
)
|
||||||
@ -675,12 +692,24 @@ describe('Testing removeSingleConstraintInfo', () => {
|
|||||||
code.indexOf(lineOfInterest) + 1,
|
code.indexOf(lineOfInterest) + 1,
|
||||||
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
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 pathToNode = getNodePathFromSourceRange(ast, range)
|
||||||
const mod = removeSingleConstraintInfo(
|
const mod = removeSingleConstraintInfo(
|
||||||
{
|
pathToNode,
|
||||||
pathToCallExp: pathToNode,
|
argPosition,
|
||||||
[key]: value,
|
|
||||||
},
|
|
||||||
ast,
|
ast,
|
||||||
programMemory
|
programMemory
|
||||||
)
|
)
|
||||||
|
@ -38,7 +38,7 @@ import {
|
|||||||
import { DefaultPlaneStr } from 'clientSideScene/sceneEntities'
|
import { DefaultPlaneStr } from 'clientSideScene/sceneEntities'
|
||||||
import { isOverlap, roundOff } from 'lib/utils'
|
import { isOverlap, roundOff } from 'lib/utils'
|
||||||
import { KCL_DEFAULT_CONSTANT_PREFIXES } from 'lib/constants'
|
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 { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
|
|
||||||
@ -799,15 +799,10 @@ export function deleteSegmentFromPipeExpression(
|
|||||||
)
|
)
|
||||||
if (!constraintInfo) return
|
if (!constraintInfo) return
|
||||||
|
|
||||||
const input = makeRemoveSingleConstraintInput(
|
if (!constraintInfo.argPosition) return
|
||||||
constraintInfo.argPosition,
|
|
||||||
callExp.shallowPath
|
|
||||||
)
|
|
||||||
if (!input) return
|
|
||||||
const transform = removeSingleConstraintInfo(
|
const transform = removeSingleConstraintInfo(
|
||||||
{
|
callExp.shallowPath,
|
||||||
...input,
|
constraintInfo.argPosition,
|
||||||
},
|
|
||||||
_modifiedAst,
|
_modifiedAst,
|
||||||
programMemory
|
programMemory
|
||||||
)
|
)
|
||||||
@ -834,37 +829,9 @@ export function deleteSegmentFromPipeExpression(
|
|||||||
return _modifiedAst
|
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(
|
export function removeSingleConstraintInfo(
|
||||||
{
|
pathToCallExp: PathToNode,
|
||||||
pathToCallExp,
|
argDetails: SimplifiedArgDetails,
|
||||||
arrayIndex,
|
|
||||||
objectProperty,
|
|
||||||
}: {
|
|
||||||
pathToCallExp: PathToNode
|
|
||||||
arrayIndex?: number
|
|
||||||
objectProperty?: string
|
|
||||||
},
|
|
||||||
ast: Program,
|
ast: Program,
|
||||||
programMemory: ProgramMemory
|
programMemory: ProgramMemory
|
||||||
):
|
):
|
||||||
@ -875,8 +842,7 @@ export function removeSingleConstraintInfo(
|
|||||||
| false {
|
| false {
|
||||||
const transform = removeSingleConstraint({
|
const transform = removeSingleConstraint({
|
||||||
pathToCallExp,
|
pathToCallExp,
|
||||||
arrayIndex,
|
inputDetails: argDetails,
|
||||||
objectProperty,
|
|
||||||
ast,
|
ast,
|
||||||
})
|
})
|
||||||
if (!transform) return false
|
if (!transform) return false
|
||||||
|
@ -16,6 +16,7 @@ import {
|
|||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
sketchGroupFromKclValue,
|
sketchGroupFromKclValue,
|
||||||
|
ObjectExpression,
|
||||||
} from './wasm'
|
} from './wasm'
|
||||||
import { createIdentifier, splitPathAtLastIndex } from './modifyAst'
|
import { createIdentifier, splitPathAtLastIndex } from './modifyAst'
|
||||||
import { getSketchSegmentFromSourceRange } from './std/sketchConstraints'
|
import { getSketchSegmentFromSourceRange } from './std/sketchConstraints'
|
||||||
@ -934,3 +935,12 @@ export function hasExtrudableGeometry(ast: Program) {
|
|||||||
})
|
})
|
||||||
return Object.keys(theMap).length > 0
|
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,
|
ast,
|
||||||
programMemory,
|
programMemory,
|
||||||
[sourceStart, sourceStart + lineToChange.length],
|
[sourceStart, sourceStart + lineToChange.length],
|
||||||
[2, 3],
|
{
|
||||||
[0, 0]
|
type: 'straight-segment',
|
||||||
|
from: [0, 0],
|
||||||
|
to: [2, 3],
|
||||||
|
}
|
||||||
)
|
)
|
||||||
if (err(changeSketchArgsRetVal)) return changeSketchArgsRetVal
|
if (err(changeSketchArgsRetVal)) return changeSketchArgsRetVal
|
||||||
expect(recast(changeSketchArgsRetVal.modifiedAst)).toBe(expectedCode)
|
expect(recast(changeSketchArgsRetVal.modifiedAst)).toBe(expectedCode)
|
||||||
@ -150,8 +153,11 @@ const mySketch001 = startSketchOn('XY')
|
|||||||
const newSketchLnRetVal = addNewSketchLn({
|
const newSketchLnRetVal = addNewSketchLn({
|
||||||
node: ast,
|
node: ast,
|
||||||
programMemory,
|
programMemory,
|
||||||
to: [2, 3],
|
input: {
|
||||||
from: [0, 0],
|
type: 'straight-segment',
|
||||||
|
from: [0, 0],
|
||||||
|
to: [2, 3],
|
||||||
|
},
|
||||||
fnName: 'lineTo',
|
fnName: 'lineTo',
|
||||||
pathToNode: [
|
pathToNode: [
|
||||||
['body', ''],
|
['body', ''],
|
||||||
|
@ -43,6 +43,16 @@ export function getSketchSegmentFromSourceRange(
|
|||||||
index: number
|
index: number
|
||||||
}
|
}
|
||||||
| Error {
|
| 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
|
const startSourceRange = sketchGroup.start?.__geoMeta.sourceRange
|
||||||
if (
|
if (
|
||||||
startSourceRange &&
|
startSourceRange &&
|
||||||
@ -51,17 +61,7 @@ export function getSketchSegmentFromSourceRange(
|
|||||||
sketchGroup.start
|
sketchGroup.start
|
||||||
)
|
)
|
||||||
return { segment: { ...sketchGroup.start, type: 'Base' }, index: -1 }
|
return { segment: { ...sketchGroup.start, type: 'Base' }, index: -1 }
|
||||||
|
return new Error('could not find matching segment')
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isSketchVariablesLinked(
|
export function isSketchVariablesLinked(
|
||||||
|
@ -9,22 +9,8 @@ import {
|
|||||||
CallExpression,
|
CallExpression,
|
||||||
Literal,
|
Literal,
|
||||||
} from '../wasm'
|
} from '../wasm'
|
||||||
import { EngineCommandManager } from './engineConnection'
|
|
||||||
import { LineInputsType } from './sketchcombos'
|
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 {
|
export interface ModifyAstBase {
|
||||||
node: Program
|
node: Program
|
||||||
// TODO #896: Remove ProgramMemory from this interface
|
// TODO #896: Remove ProgramMemory from this interface
|
||||||
@ -37,85 +23,196 @@ export interface AddTagInfo {
|
|||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
}
|
}
|
||||||
|
|
||||||
interface addCall extends ModifyAstBase {
|
/** Inputs for all straight segments, to and from are absolute values, as this gives a
|
||||||
to: [number, number]
|
* 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]
|
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
|
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
|
spliceBetween?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface updateArgs extends ModifyAstBase {
|
interface updateArgs extends ModifyAstBase {
|
||||||
from: [number, number]
|
input: SegmentInputs
|
||||||
to: [number, number]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type VarValueKeys = 'angle' | 'offset' | 'length' | 'to' | 'intersectTag'
|
export type InputArgKeys =
|
||||||
|
| 'angle'
|
||||||
|
| 'offset'
|
||||||
|
| 'length'
|
||||||
|
| 'to'
|
||||||
|
| 'intersectTag'
|
||||||
|
| 'radius'
|
||||||
|
| 'center'
|
||||||
export interface SingleValueInput<T> {
|
export interface SingleValueInput<T> {
|
||||||
type: 'singleValue'
|
type: 'singleValue'
|
||||||
argType: LineInputsType
|
argType: LineInputsType
|
||||||
value: T
|
expr: T
|
||||||
}
|
}
|
||||||
export interface ArrayItemInput<T> {
|
export interface ArrayItemInput<T> {
|
||||||
type: 'arrayItem'
|
type: 'arrayItem'
|
||||||
index: 0 | 1
|
index: 0 | 1
|
||||||
argType: LineInputsType
|
argType: LineInputsType
|
||||||
value: T
|
expr: T
|
||||||
}
|
}
|
||||||
export interface ObjectPropertyInput<T> {
|
export interface ObjectPropertyInput<T> {
|
||||||
type: 'objectProperty'
|
type: 'objectProperty'
|
||||||
key: VarValueKeys
|
key: InputArgKeys
|
||||||
argType: LineInputsType
|
argType: LineInputsType
|
||||||
value: T
|
expr: T
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ArrayOrObjItemInput<T> {
|
interface ArrayOrObjItemInput<T> {
|
||||||
type: 'arrayOrObjItem'
|
type: 'arrayOrObjItem'
|
||||||
key: VarValueKeys
|
key: InputArgKeys
|
||||||
index: 0 | 1
|
index: 0 | 1
|
||||||
argType: LineInputsType
|
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>
|
| SingleValueInput<T>
|
||||||
| ArrayItemInput<T>
|
| ArrayItemInput<T>
|
||||||
| ObjectPropertyInput<T>
|
| ObjectPropertyInput<T>
|
||||||
| ArrayOrObjItemInput<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: 'singleValue'
|
||||||
}
|
}
|
||||||
| { type: 'arrayItem'; index: 0 | 1 }
|
| { 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],
|
* Represents the result of creating a sketch expression (line, tangentialArcTo, angledLine, circle, etc.).
|
||||||
literalValues: RawValues,
|
*
|
||||||
referencedSegment?: Path
|
* @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
|
callExp: Expr
|
||||||
valueUsedInTransform?: number
|
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 {
|
export interface ConstrainInfo {
|
||||||
stdLibFnName: ToolTip
|
stdLibFnName: ToolTip
|
||||||
type: LineInputsType | 'vertical' | 'horizontal' | 'tangentialWithPrevious'
|
type:
|
||||||
|
| LineInputsType
|
||||||
|
| 'vertical'
|
||||||
|
| 'horizontal'
|
||||||
|
| 'tangentialWithPrevious'
|
||||||
|
| 'radius'
|
||||||
isConstrained: boolean
|
isConstrained: boolean
|
||||||
sourceRange: SourceRange
|
sourceRange: SourceRange
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
value: string
|
value: string
|
||||||
calculatedValue?: any
|
calculatedValue?: any
|
||||||
argPosition?: SimplifiedVarValue
|
argPosition?: SimplifiedArgDetails
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SketchLineHelper {
|
export interface SketchLineHelper {
|
||||||
|
@ -20,10 +20,8 @@ import {
|
|||||||
} from 'lang/queryAst'
|
} from 'lang/queryAst'
|
||||||
import { CommandArgument } from './commandTypes'
|
import { CommandArgument } from './commandTypes'
|
||||||
import {
|
import {
|
||||||
STRAIGHT_SEGMENT,
|
|
||||||
TANGENTIAL_ARC_TO_SEGMENT,
|
|
||||||
getParentGroup,
|
getParentGroup,
|
||||||
PROFILE_START,
|
SEGMENT_BODIES_PLUS_PROFILE_START,
|
||||||
} from 'clientSideScene/sceneEntities'
|
} from 'clientSideScene/sceneEntities'
|
||||||
import { Mesh, Object3D, Object3DEventMap } from 'three'
|
import { Mesh, Object3D, Object3DEventMap } from 'three'
|
||||||
import { AXIS_GROUP, X_AXIS } from 'clientSideScene/sceneInfra'
|
import { AXIS_GROUP, X_AXIS } from 'clientSideScene/sceneInfra'
|
||||||
@ -162,11 +160,7 @@ export async function getEventForSelectWithPoint({
|
|||||||
export function getEventForSegmentSelection(
|
export function getEventForSegmentSelection(
|
||||||
obj: Object3D<Object3DEventMap>
|
obj: Object3D<Object3DEventMap>
|
||||||
): ModelingMachineEvent | null {
|
): ModelingMachineEvent | null {
|
||||||
const group = getParentGroup(obj, [
|
const group = getParentGroup(obj, SEGMENT_BODIES_PLUS_PROFILE_START)
|
||||||
STRAIGHT_SEGMENT,
|
|
||||||
TANGENTIAL_ARC_TO_SEGMENT,
|
|
||||||
PROFILE_START,
|
|
||||||
])
|
|
||||||
const axisGroup = getParentGroup(obj, [AXIS_GROUP])
|
const axisGroup = getParentGroup(obj, [AXIS_GROUP])
|
||||||
if (!group && !axisGroup) return null
|
if (!group && !axisGroup) return null
|
||||||
if (axisGroup?.userData.type === AXIS_GROUP) {
|
if (axisGroup?.userData.type === AXIS_GROUP) {
|
||||||
@ -303,12 +297,7 @@ function updateSceneObjectColors(codeBasedSelections: Selection[]) {
|
|||||||
const updated = kclManager.ast
|
const updated = kclManager.ast
|
||||||
|
|
||||||
Object.values(sceneEntitiesManager.activeSegments).forEach((segmentGroup) => {
|
Object.values(sceneEntitiesManager.activeSegments).forEach((segmentGroup) => {
|
||||||
if (
|
if (!SEGMENT_BODIES_PLUS_PROFILE_START.includes(segmentGroup?.name)) return
|
||||||
![STRAIGHT_SEGMENT, TANGENTIAL_ARC_TO_SEGMENT, PROFILE_START].includes(
|
|
||||||
segmentGroup?.name
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
const nodeMeta = getNodeFromPath<CallExpression>(
|
||||||
updated,
|
updated,
|
||||||
segmentGroup.userData.pathToNode,
|
segmentGroup.userData.pathToNode,
|
||||||
|
@ -2,7 +2,8 @@ import { CustomIconName } from 'components/CustomIcon'
|
|||||||
import { DEV } from 'env'
|
import { DEV } from 'env'
|
||||||
import { commandBarMachine } from 'machines/commandBarMachine'
|
import { commandBarMachine } from 'machines/commandBarMachine'
|
||||||
import {
|
import {
|
||||||
canRectangleTool,
|
canRectangleOrCircleTool,
|
||||||
|
isClosedSketch,
|
||||||
isEditingExistingSketch,
|
isEditingExistingSketch,
|
||||||
modelingMachine,
|
modelingMachine,
|
||||||
} from 'machines/modelingMachine'
|
} from 'machines/modelingMachine'
|
||||||
@ -301,7 +302,11 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
state.matches('Sketch no face') ||
|
state.matches('Sketch no face') ||
|
||||||
state.matches({
|
state.matches({
|
||||||
Sketch: { 'Rectangle tool': 'Awaiting second corner' },
|
Sketch: { 'Rectangle tool': 'Awaiting second corner' },
|
||||||
}),
|
}) ||
|
||||||
|
state.matches({
|
||||||
|
Sketch: { 'Circle tool': 'Awaiting Radius' },
|
||||||
|
}) ||
|
||||||
|
isClosedSketch(state.context),
|
||||||
title: 'Line',
|
title: 'Line',
|
||||||
hotkey: (state) =>
|
hotkey: (state) =>
|
||||||
state.matches({ Sketch: 'Line tool' }) ? ['Esc', 'L'] : 'L',
|
state.matches({ Sketch: 'Line tool' }) ? ['Esc', 'L'] : 'L',
|
||||||
@ -324,8 +329,15 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
icon: 'arc',
|
icon: 'arc',
|
||||||
status: 'available',
|
status: 'available',
|
||||||
disabled: (state) =>
|
disabled: (state) =>
|
||||||
!isEditingExistingSketch(state.context) &&
|
(!isEditingExistingSketch(state.context) &&
|
||||||
!state.matches({ Sketch: 'Tangential arc to' }),
|
!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',
|
title: 'Tangential Arc',
|
||||||
hotkey: (state) =>
|
hotkey: (state) =>
|
||||||
state.matches({ Sketch: 'Tangential arc to' }) ? ['Esc', 'A'] : 'A',
|
state.matches({ Sketch: 'Tangential arc to' }) ? ['Esc', 'A'] : 'A',
|
||||||
@ -363,10 +375,22 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
id: 'circle-center',
|
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',
|
icon: 'circle',
|
||||||
status: 'unavailable',
|
status: 'available',
|
||||||
title: 'Center circle',
|
title: 'Center circle',
|
||||||
|
disabled: (state) =>
|
||||||
|
!canRectangleOrCircleTool(state.context) &&
|
||||||
|
!state.matches({ Sketch: 'Circle tool' }),
|
||||||
|
isActive: (state) => state.matches({ Sketch: 'Circle tool' }),
|
||||||
showTitle: false,
|
showTitle: false,
|
||||||
description: 'Start drawing a circle from its center',
|
description: 'Start drawing a circle from its center',
|
||||||
links: [
|
links: [
|
||||||
@ -382,7 +406,6 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
console.error('Three-point circle not yet implemented'),
|
console.error('Three-point circle not yet implemented'),
|
||||||
icon: 'circle',
|
icon: 'circle',
|
||||||
status: 'unavailable',
|
status: 'unavailable',
|
||||||
disabled: () => true,
|
|
||||||
title: 'Three-point circle',
|
title: 'Three-point circle',
|
||||||
showTitle: false,
|
showTitle: false,
|
||||||
description: 'Draw a circle defined by three points',
|
description: 'Draw a circle defined by three points',
|
||||||
@ -404,7 +427,7 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
icon: 'rectangle',
|
icon: 'rectangle',
|
||||||
status: 'available',
|
status: 'available',
|
||||||
disabled: (state) =>
|
disabled: (state) =>
|
||||||
!canRectangleTool(state.context) &&
|
!canRectangleOrCircleTool(state.context) &&
|
||||||
!state.matches({ Sketch: 'Rectangle tool' }),
|
!state.matches({ Sketch: 'Rectangle tool' }),
|
||||||
title: 'Corner rectangle',
|
title: 'Corner rectangle',
|
||||||
hotkey: (state) =>
|
hotkey: (state) =>
|
||||||
|
@ -58,6 +58,8 @@ import { deleteSegment } from 'clientSideScene/ClientSideSceneComp'
|
|||||||
import { executeAst } from 'lang/langHelpers'
|
import { executeAst } from 'lang/langHelpers'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { ToolbarModeName } from 'lib/toolbar'
|
import { ToolbarModeName } from 'lib/toolbar'
|
||||||
|
import { quaternionFromUpNForward } from 'clientSideScene/helpers'
|
||||||
|
import { Vector3 } from 'three'
|
||||||
|
|
||||||
export const MODELING_PERSIST_KEY = 'MODELING_PERSIST_KEY'
|
export const MODELING_PERSIST_KEY = 'MODELING_PERSIST_KEY'
|
||||||
|
|
||||||
@ -159,7 +161,12 @@ export interface Store {
|
|||||||
openPanes: SidebarType[]
|
openPanes: SidebarType[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SketchTool = 'line' | 'tangentialArc' | 'rectangle' | 'none'
|
export type SketchTool =
|
||||||
|
| 'line'
|
||||||
|
| 'tangentialArc'
|
||||||
|
| 'rectangle'
|
||||||
|
| 'circle'
|
||||||
|
| 'none'
|
||||||
|
|
||||||
export type ModelingMachineEvent =
|
export type ModelingMachineEvent =
|
||||||
| {
|
| {
|
||||||
@ -211,6 +218,10 @@ export type ModelingMachineEvent =
|
|||||||
type: 'Add rectangle origin'
|
type: 'Add rectangle origin'
|
||||||
data: [x: number, y: number]
|
data: [x: number, y: number]
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
type: 'Add circle origin'
|
||||||
|
data: [x: number, y: number]
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
type: 'xstate.done.actor.animate-to-face'
|
type: 'xstate.done.actor.animate-to-face'
|
||||||
output: SketchDetails
|
output: SketchDetails
|
||||||
@ -244,6 +255,7 @@ export type ModelingMachineEvent =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
| { type: 'Finish rectangle' }
|
| { type: 'Finish rectangle' }
|
||||||
|
| { type: 'Finish circle' }
|
||||||
| { type: 'Artifact graph populated' }
|
| { type: 'Artifact graph populated' }
|
||||||
| { type: 'Artifact graph emptied' }
|
| { type: 'Artifact graph emptied' }
|
||||||
|
|
||||||
@ -464,7 +476,10 @@ export const modelingMachine = setup({
|
|||||||
isEditingExistingSketch({ sketchDetails }),
|
isEditingExistingSketch({ sketchDetails }),
|
||||||
|
|
||||||
'next is rectangle': ({ context: { sketchDetails, currentTool } }) =>
|
'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 line': ({ context }) => context.currentTool === 'line',
|
||||||
'next is none': ({ context }) => {
|
'next is none': ({ context }) => {
|
||||||
console.log('is next 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 }) => {
|
'set up draft rectangle': ({ context: { sketchDetails }, event }) => {
|
||||||
if (event.type !== 'Add rectangle origin') return
|
if (event.type !== 'Add rectangle origin') return
|
||||||
if (!sketchDetails || !event.data) return
|
if (!sketchDetails || !event.data) return
|
||||||
@ -706,6 +757,18 @@ export const modelingMachine = setup({
|
|||||||
event.data
|
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 } }) => {
|
'set up draft line without teardown': ({ context: { sketchDetails } }) => {
|
||||||
if (!sketchDetails) return
|
if (!sketchDetails) return
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
@ -1805,10 +1868,43 @@ export const modelingMachine = setup({
|
|||||||
target: 'Tangential arc to',
|
target: 'Tangential arc to',
|
||||||
guard: 'next is tangential arc',
|
guard: 'next is tangential arc',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
target: 'Circle tool',
|
||||||
|
guard: 'next is circle',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
entry: 'assign tool in context',
|
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',
|
initial: 'Init',
|
||||||
@ -1946,10 +2042,13 @@ export function isEditingExistingSketch({
|
|||||||
(item) =>
|
(item) =>
|
||||||
item.type === 'CallExpression' && item.callee.name === 'startProfileAt'
|
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: SketchDetails | null
|
sketchDetails: SketchDetails | null
|
||||||
@ -1964,3 +2063,25 @@ export function canRectangleTool({
|
|||||||
if (err(node)) return false
|
if (err(node)) return false
|
||||||
return node.node?.declarations?.[0]?.init.type !== 'PipeExpression'
|
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() {
|
fn get_autocomplete_snippet_circle() {
|
||||||
let circle_fn: Box<dyn StdLibFn> = Box::new(crate::std::shapes::Circle);
|
let circle_fn: Box<dyn StdLibFn> = Box::new(crate::std::shapes::Circle);
|
||||||
let snippet = circle_fn.to_autocomplete_snippet().unwrap();
|
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
|
/// arc's direction
|
||||||
ccw: bool,
|
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.
|
/// A path that is horizontal.
|
||||||
Horizontal {
|
Horizontal {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
@ -1448,6 +1461,7 @@ impl Path {
|
|||||||
Path::Base { base } => base.geo_meta.id,
|
Path::Base { base } => base.geo_meta.id,
|
||||||
Path::TangentialArcTo { base, .. } => base.geo_meta.id,
|
Path::TangentialArcTo { base, .. } => base.geo_meta.id,
|
||||||
Path::TangentialArc { 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::Base { base } => base.tag.clone(),
|
||||||
Path::TangentialArcTo { base, .. } => base.tag.clone(),
|
Path::TangentialArcTo { base, .. } => base.tag.clone(),
|
||||||
Path::TangentialArc { 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::Base { base } => base,
|
||||||
Path::TangentialArcTo { base, .. } => base,
|
Path::TangentialArcTo { base, .. } => base,
|
||||||
Path::TangentialArc { base, .. } => base,
|
Path::TangentialArc { base, .. } => base,
|
||||||
|
Path::Circle { base, .. } => base,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1481,6 +1497,7 @@ impl Path {
|
|||||||
Path::Base { base } => Some(base),
|
Path::Base { base } => Some(base),
|
||||||
Path::TangentialArcTo { base, .. } => Some(base),
|
Path::TangentialArcTo { base, .. } => Some(base),
|
||||||
Path::TangentialArc { base, .. } => Some(base),
|
Path::TangentialArc { base, .. } => Some(base),
|
||||||
|
Path::Circle { base, .. } => Some(base),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2572,7 +2589,7 @@ fn transform = (replicaId) => {
|
|||||||
|
|
||||||
fn layer = () => {
|
fn layer = () => {
|
||||||
return startSketchOn("XY")
|
return startSketchOn("XY")
|
||||||
|> circle([0, 0], 1, %, $tag1)
|
|> circle({ center: [0, 0], radius: 1 }, %, $tag1)
|
||||||
|> extrude(10, %)
|
|> extrude(10, %)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2700,7 +2717,7 @@ fn transform = (replicaId) => {
|
|||||||
|
|
||||||
fn layer = () => {
|
fn layer = () => {
|
||||||
return startSketchOn("XY")
|
return startSketchOn("XY")
|
||||||
|> circle([0, 0], 1, %, $tag1)
|
|> circle({ center: [0, 0], radius: 1 }, %, $tag1)
|
||||||
|> extrude(10, %)
|
|> extrude(10, %)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,8 +261,7 @@ impl Args {
|
|||||||
&self,
|
&self,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
(
|
(
|
||||||
[f64; 2],
|
crate::std::shapes::CircleData,
|
||||||
f64,
|
|
||||||
crate::std::shapes::SketchSurfaceOrGroup,
|
crate::std::shapes::SketchSurfaceOrGroup,
|
||||||
Option<TagDeclarator>,
|
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::AngledLineData);
|
||||||
impl_from_arg_via_json!(super::sketch::AngledLineToData);
|
impl_from_arg_via_json!(super::sketch::AngledLineToData);
|
||||||
impl_from_arg_via_json!(super::sketch::AngledLineThatIntersectsData);
|
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::ArcData);
|
||||||
impl_from_arg_via_json!(super::sketch::TangentialArcData);
|
impl_from_arg_via_json!(super::sketch::TangentialArcData);
|
||||||
impl_from_arg_via_json!(super::sketch::BezierData);
|
impl_from_arg_via_json!(super::sketch::BezierData);
|
||||||
|
@ -50,7 +50,7 @@ pub async fn int(args: Args) -> Result<KclValue, KclError> {
|
|||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// const sketch001 = startSketchOn('XZ')
|
/// const sketch001 = startSketchOn('XZ')
|
||||||
/// |> circle([0, 0], 2, %)
|
/// |> circle({ center: [0, 0], radius: 2 }, %)
|
||||||
/// const extrude001 = extrude(5, sketch001)
|
/// const extrude001 = extrude(5, sketch001)
|
||||||
///
|
///
|
||||||
/// const pattern01 = patternTransform(int(ceil(5 / 2)), (id) => {
|
/// const pattern01 = patternTransform(int(ceil(5 / 2)), (id) => {
|
||||||
|
@ -231,7 +231,7 @@ pub(crate) async fn do_post_extrude(
|
|||||||
.flat_map(|path| {
|
.flat_map(|path| {
|
||||||
if let Some(Some(actual_face_id)) = face_id_map.get(&path.get_base().geo_meta.id) {
|
if let Some(Some(actual_face_id)) = face_id_map.get(&path.get_base().geo_meta.id) {
|
||||||
match path {
|
match path {
|
||||||
Path::TangentialArc { .. } | Path::TangentialArcTo { .. } => {
|
Path::TangentialArc { .. } | Path::TangentialArcTo { .. } | Path::Circle { .. } => {
|
||||||
let extrude_surface = ExtrudeSurface::ExtrudeArc(crate::executor::ExtrudeArc {
|
let extrude_surface = ExtrudeSurface::ExtrudeArc(crate::executor::ExtrudeArc {
|
||||||
face_id: *actual_face_id,
|
face_id: *actual_face_id,
|
||||||
tag: path.get_base().tag.clone(),
|
tag: path.get_base().tag.clone(),
|
||||||
|
@ -42,7 +42,7 @@ pub async fn helix(args: Args) -> Result<KclValue, KclError> {
|
|||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// const part001 = startSketchOn('XY')
|
/// const part001 = startSketchOn('XY')
|
||||||
/// |> circle([5, 5], 10, %)
|
/// |> circle({ center: [5, 5], radius: 10 }, %)
|
||||||
/// |> extrude(10, %)
|
/// |> extrude(10, %)
|
||||||
/// |> helix({
|
/// |> helix({
|
||||||
/// angleStart: 0,
|
/// angleStart: 0,
|
||||||
|
@ -91,10 +91,10 @@ pub async fn loft(args: Args) -> Result<KclValue, KclError> {
|
|||||||
/// |> close(%)
|
/// |> close(%)
|
||||||
///
|
///
|
||||||
/// const circleSketch0 = startSketchOn(offsetPlane('XY', 75))
|
/// const circleSketch0 = startSketchOn(offsetPlane('XY', 75))
|
||||||
/// |> circle([0, 100], 50, %)
|
/// |> circle({ center: [0, 100], radius: 50 }, %)
|
||||||
///
|
///
|
||||||
/// const circleSketch1 = startSketchOn(offsetPlane('XY', 150))
|
/// const circleSketch1 = startSketchOn(offsetPlane('XY', 150))
|
||||||
/// |> circle([0, 100], 20, %)
|
/// |> circle({ center: [0, 100], radius: 20 }, %)
|
||||||
///
|
///
|
||||||
/// loft([squareSketch, circleSketch0, circleSketch1])
|
/// loft([squareSketch, circleSketch0, circleSketch1])
|
||||||
/// ```
|
/// ```
|
||||||
@ -110,10 +110,10 @@ pub async fn loft(args: Args) -> Result<KclValue, KclError> {
|
|||||||
/// |> close(%)
|
/// |> close(%)
|
||||||
///
|
///
|
||||||
/// const circleSketch0 = startSketchOn(offsetPlane('XY', 75))
|
/// const circleSketch0 = startSketchOn(offsetPlane('XY', 75))
|
||||||
/// |> circle([0, 100], 50, %)
|
/// |> circle({ center: [0, 100], radius: 50 }, %)
|
||||||
///
|
///
|
||||||
/// const circleSketch1 = startSketchOn(offsetPlane('XY', 150))
|
/// const circleSketch1 = startSketchOn(offsetPlane('XY', 150))
|
||||||
/// |> circle([0, 100], 20, %)
|
/// |> circle({ center: [0, 100], radius: 20 }, %)
|
||||||
///
|
///
|
||||||
/// loft([squareSketch, circleSketch0, circleSketch1], {
|
/// loft([squareSketch, circleSketch0, circleSketch1], {
|
||||||
/// // This can be set to override the automatically determined
|
/// // 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 circumference = 70
|
||||||
///
|
///
|
||||||
/// const exampleSketch = startSketchOn("XZ")
|
/// const exampleSketch = startSketchOn("XZ")
|
||||||
/// |> circle([0, 0], circumference/ (2 * pi()), %)
|
/// |> circle({ center: [0, 0], radius: circumference/ (2 * pi()) }, %)
|
||||||
///
|
///
|
||||||
/// const example = extrude(5, exampleSketch)
|
/// const example = extrude(5, exampleSketch)
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -37,7 +37,7 @@ use crate::{
|
|||||||
ast::types::FunctionExpression,
|
ast::types::FunctionExpression,
|
||||||
docs::StdLibFn,
|
docs::StdLibFn,
|
||||||
errors::KclError,
|
errors::KclError,
|
||||||
executor::{KclValue, ProgramMemory, SketchGroup, SketchSurface},
|
executor::{KclValue, ProgramMemory},
|
||||||
std::kcl_stdlib::KclStdLibFn,
|
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.
|
/// // Each layer is just a pretty thin cylinder.
|
||||||
/// fn layer = () => {
|
/// fn layer = () => {
|
||||||
/// return startSketchOn("XY") // or some other plane idk
|
/// return startSketchOn("XY") // or some other plane idk
|
||||||
/// |> circle([0, 0], 1, %, $tag1)
|
/// |> circle({ center: [0, 0], radius: 1 }, %, $tag1)
|
||||||
/// |> extrude(h, %)
|
/// |> extrude(h, %)
|
||||||
/// }
|
/// }
|
||||||
/// // The vase is 100 layers tall.
|
/// // The vase is 100 layers tall.
|
||||||
@ -318,7 +318,7 @@ pub async fn pattern_linear_2d(args: Args) -> Result<KclValue, KclError> {
|
|||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// const exampleSketch = startSketchOn('XZ')
|
/// const exampleSketch = startSketchOn('XZ')
|
||||||
/// |> circle([0, 0], 1, %)
|
/// |> circle({ center: [0, 0], radius: 1 }, %)
|
||||||
/// |> patternLinear2d({
|
/// |> patternLinear2d({
|
||||||
/// axis: [1, 0],
|
/// axis: [1, 0],
|
||||||
/// repetitions: 6,
|
/// repetitions: 6,
|
||||||
@ -651,7 +651,7 @@ pub async fn pattern_circular_3d(args: Args) -> Result<KclValue, KclError> {
|
|||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// const exampleSketch = startSketchOn('XZ')
|
/// const exampleSketch = startSketchOn('XZ')
|
||||||
/// |> circle([0, 0], 1, %)
|
/// |> circle({ center: [0, 0], radius: 1 }, %)
|
||||||
///
|
///
|
||||||
/// const example = extrude(-5, exampleSketch)
|
/// const example = extrude(-5, exampleSketch)
|
||||||
/// |> patternCircular3d({
|
/// |> patternCircular3d({
|
||||||
|
@ -77,7 +77,7 @@ pub async fn offset_plane(args: Args) -> Result<KclValue, KclError> {
|
|||||||
/// |> close(%)
|
/// |> close(%)
|
||||||
///
|
///
|
||||||
/// const circleSketch = startSketchOn(offsetPlane('XY', 150))
|
/// const circleSketch = startSketchOn(offsetPlane('XY', 150))
|
||||||
/// |> circle([0, 100], 50, %)
|
/// |> circle({ center: [0, 100], radius: 50 }, %)
|
||||||
///
|
///
|
||||||
/// loft([squareSketch, circleSketch])
|
/// loft([squareSketch, circleSketch])
|
||||||
/// ```
|
/// ```
|
||||||
@ -93,7 +93,7 @@ pub async fn offset_plane(args: Args) -> Result<KclValue, KclError> {
|
|||||||
/// |> close(%)
|
/// |> close(%)
|
||||||
///
|
///
|
||||||
/// const circleSketch = startSketchOn(offsetPlane('XZ', 150))
|
/// const circleSketch = startSketchOn(offsetPlane('XZ', 150))
|
||||||
/// |> circle([0, 100], 50, %)
|
/// |> circle({ center: [0, 100], radius: 50 }, %)
|
||||||
///
|
///
|
||||||
/// loft([squareSketch, circleSketch])
|
/// loft([squareSketch, circleSketch])
|
||||||
/// ```
|
/// ```
|
||||||
@ -109,7 +109,7 @@ pub async fn offset_plane(args: Args) -> Result<KclValue, KclError> {
|
|||||||
/// |> close(%)
|
/// |> close(%)
|
||||||
///
|
///
|
||||||
/// const circleSketch = startSketchOn(offsetPlane('YZ', 150))
|
/// const circleSketch = startSketchOn(offsetPlane('YZ', 150))
|
||||||
/// |> circle([0, 100], 50, %)
|
/// |> circle({ center: [0, 100], radius: 50 }, %)
|
||||||
///
|
///
|
||||||
/// loft([squareSketch, circleSketch])
|
/// loft([squareSketch, circleSketch])
|
||||||
/// ```
|
/// ```
|
||||||
@ -125,7 +125,7 @@ pub async fn offset_plane(args: Args) -> Result<KclValue, KclError> {
|
|||||||
/// |> close(%)
|
/// |> close(%)
|
||||||
///
|
///
|
||||||
/// const circleSketch = startSketchOn(offsetPlane('-XZ', -150))
|
/// const circleSketch = startSketchOn(offsetPlane('-XZ', -150))
|
||||||
/// |> circle([0, 100], 50, %)
|
/// |> circle({ center: [0, 100], radius: 50 }, %)
|
||||||
///
|
///
|
||||||
/// loft([squareSketch, circleSketch])
|
/// loft([squareSketch, circleSketch])
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -133,7 +133,7 @@ pub async fn revolve(args: Args) -> Result<KclValue, KclError> {
|
|||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// // A donut shape.
|
/// // A donut shape.
|
||||||
/// const sketch001 = startSketchOn('XY')
|
/// const sketch001 = startSketchOn('XY')
|
||||||
/// |> circle([15, 0], 5, %)
|
/// |> circle({ center: [15, 0], radius: 5 }, %)
|
||||||
/// |> revolve({
|
/// |> revolve({
|
||||||
/// angle: 360,
|
/// angle: 360,
|
||||||
/// axis: 'y'
|
/// axis: 'y'
|
||||||
@ -185,7 +185,7 @@ pub async fn revolve(args: Args) -> Result<KclValue, KclError> {
|
|||||||
/// |> extrude(20, %)
|
/// |> extrude(20, %)
|
||||||
///
|
///
|
||||||
/// const sketch001 = startSketchOn(box, "END")
|
/// const sketch001 = startSketchOn(box, "END")
|
||||||
/// |> circle([10,10], 4, %)
|
/// |> circle({ center: [10,10], radius: 4 }, %)
|
||||||
/// |> revolve({
|
/// |> revolve({
|
||||||
/// angle: -90,
|
/// angle: -90,
|
||||||
/// axis: 'y'
|
/// axis: 'y'
|
||||||
@ -202,7 +202,7 @@ pub async fn revolve(args: Args) -> Result<KclValue, KclError> {
|
|||||||
/// |> extrude(20, %)
|
/// |> extrude(20, %)
|
||||||
///
|
///
|
||||||
/// const sketch001 = startSketchOn(box, "END")
|
/// const sketch001 = startSketchOn(box, "END")
|
||||||
/// |> circle([10,10], 4, %)
|
/// |> circle({ center: [10,10], radius: 4 }, %)
|
||||||
/// |> revolve({
|
/// |> revolve({
|
||||||
/// angle: 90,
|
/// angle: 90,
|
||||||
/// axis: getOppositeEdge(revolveAxis)
|
/// axis: getOppositeEdge(revolveAxis)
|
||||||
@ -219,7 +219,7 @@ pub async fn revolve(args: Args) -> Result<KclValue, KclError> {
|
|||||||
/// |> extrude(20, %)
|
/// |> extrude(20, %)
|
||||||
///
|
///
|
||||||
/// const sketch001 = startSketchOn(box, "END")
|
/// const sketch001 = startSketchOn(box, "END")
|
||||||
/// |> circle([10,10], 4, %)
|
/// |> circle({ center: [10,10], radius: 4 }, %)
|
||||||
/// |> revolve({
|
/// |> revolve({
|
||||||
/// angle: 90,
|
/// angle: 90,
|
||||||
/// axis: getOppositeEdge(revolveAxis),
|
/// axis: getOppositeEdge(revolveAxis),
|
||||||
|
@ -2,14 +2,15 @@
|
|||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use derive_docs::stdlib;
|
use derive_docs::stdlib;
|
||||||
|
use kittycad::types::{Angle, ModelingCmd};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::TagDeclarator,
|
ast::types::TagDeclarator,
|
||||||
errors::KclError,
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::KclValue,
|
executor::{BasePath, GeoMeta, KclValue, Path, SketchGroup, SketchSurface},
|
||||||
std::{Args, SketchGroup, SketchSurface},
|
std::Args,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A sketch surface or a sketch group.
|
/// A sketch surface or a sketch group.
|
||||||
@ -21,12 +22,24 @@ pub enum SketchSurfaceOrGroup {
|
|||||||
SketchGroup(Box<SketchGroup>),
|
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.
|
/// Sketch a circle.
|
||||||
pub async fn circle(args: Args) -> Result<KclValue, KclError> {
|
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()?;
|
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))
|
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
|
/// ```no_run
|
||||||
/// const exampleSketch = startSketchOn("-XZ")
|
/// const exampleSketch = startSketchOn("-XZ")
|
||||||
/// |> circle([0, 0], 10, %)
|
/// |> circle({ center: [0, 0], radius: 10 }, %)
|
||||||
///
|
///
|
||||||
/// const example = extrude(5, exampleSketch)
|
/// const example = extrude(5, exampleSketch)
|
||||||
/// ```
|
/// ```
|
||||||
@ -47,7 +60,7 @@ pub async fn circle(args: Args) -> Result<KclValue, KclError> {
|
|||||||
/// |> line([0, 30], %)
|
/// |> line([0, 30], %)
|
||||||
/// |> line([-30, 0], %)
|
/// |> line([-30, 0], %)
|
||||||
/// |> close(%)
|
/// |> close(%)
|
||||||
/// |> hole(circle([0, 15], 5, %), %)
|
/// |> hole(circle({ center: [0, 15], radius: 5 }, %), %)
|
||||||
///
|
///
|
||||||
/// const example = extrude(5, exampleSketch)
|
/// const example = extrude(5, exampleSketch)
|
||||||
/// ```
|
/// ```
|
||||||
@ -55,8 +68,7 @@ pub async fn circle(args: Args) -> Result<KclValue, KclError> {
|
|||||||
name = "circle",
|
name = "circle",
|
||||||
}]
|
}]
|
||||||
async fn inner_circle(
|
async fn inner_circle(
|
||||||
center: [f64; 2],
|
data: CircleData,
|
||||||
radius: f64,
|
|
||||||
sketch_surface_or_group: SketchSurfaceOrGroup,
|
sketch_surface_or_group: SketchSurfaceOrGroup,
|
||||||
tag: Option<TagDeclarator>,
|
tag: Option<TagDeclarator>,
|
||||||
args: Args,
|
args: Args,
|
||||||
@ -65,23 +77,70 @@ async fn inner_circle(
|
|||||||
SketchSurfaceOrGroup::SketchSurface(surface) => surface,
|
SketchSurfaceOrGroup::SketchSurface(surface) => surface,
|
||||||
SketchSurfaceOrGroup::SketchGroup(group) => group.on,
|
SketchSurfaceOrGroup::SketchGroup(group) => group.on,
|
||||||
};
|
};
|
||||||
let mut sketch_group =
|
let sketch_group = crate::std::sketch::inner_start_profile_at(
|
||||||
crate::std::sketch::inner_start_profile_at([center[0] + radius, center[1]], sketch_surface, None, args.clone())
|
[data.center[0] + data.radius, data.center[1]],
|
||||||
.await?;
|
sketch_surface,
|
||||||
|
None,
|
||||||
// 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,
|
|
||||||
args.clone(),
|
args.clone(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Call close.
|
let angle_start = Angle::from_degrees(0.0);
|
||||||
crate::std::sketch::inner_close(sketch_group, None, args).await
|
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([5, 0], %)
|
||||||
/// |> line([0, -5], %)
|
/// |> line([0, -5], %)
|
||||||
/// |> close(%)
|
/// |> close(%)
|
||||||
/// |> hole(circle([1, 1], .25, %), %)
|
/// |> hole(circle({ center: [1, 1], radius: .25 }, %), %)
|
||||||
/// |> hole(circle([1, 4], .25, %), %)
|
/// |> hole(circle({ center: [1, 4], radius: .25 }, %), %)
|
||||||
///
|
///
|
||||||
/// const example = extrude(1, exampleSketch)
|
/// const example = extrude(1, exampleSketch)
|
||||||
/// ```
|
/// ```
|
||||||
@ -2077,7 +2077,7 @@ pub async fn hole(args: Args) -> Result<KclValue, KclError> {
|
|||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// const exampleSketch = startSketchOn('-XZ')
|
/// const exampleSketch = startSketchOn('-XZ')
|
||||||
/// |> circle([0, 0], 3, %)
|
/// |> circle({ center: [0, 0], radius: 3 }, %)
|
||||||
/// |> hole(squareHoleSketch(), %)
|
/// |> hole(squareHoleSketch(), %)
|
||||||
/// const example = extrude(1, exampleSketch)
|
/// const example = extrude(1, exampleSketch)
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -1028,10 +1028,10 @@ const tabs_r = startSketchOn({
|
|||||||
|> line([0, -10], %)
|
|> line([0, -10], %)
|
||||||
|> line([-10, -5], %)
|
|> line([-10, -5], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> hole(circle([
|
|> hole(circle({ center: [
|
||||||
width / 2 + thk + hole_diam,
|
width / 2 + thk + hole_diam,
|
||||||
length / 2 - hole_diam
|
length / 2 - hole_diam
|
||||||
], hole_diam / 2, %), %)
|
], radius: hole_diam / 2 }, %), %)
|
||||||
|> extrude(-thk, %)
|
|> extrude(-thk, %)
|
||||||
|> patternLinear3d({
|
|> patternLinear3d({
|
||||||
axis: [0, -1, 0],
|
axis: [0, -1, 0],
|
||||||
@ -1052,10 +1052,10 @@ const tabs_l = startSketchOn({
|
|||||||
|> line([0, -10], %)
|
|> line([0, -10], %)
|
||||||
|> line([10, -5], %)
|
|> line([10, -5], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> hole(circle([
|
|> hole(circle({ center: [
|
||||||
-width / 2 - thk - hole_diam,
|
-width / 2 - thk - hole_diam,
|
||||||
length / 2 - hole_diam
|
length / 2 - hole_diam
|
||||||
], hole_diam / 2, %), %)
|
], radius: hole_diam / 2 }, %), %)
|
||||||
|> extrude(-thk, %)
|
|> extrude(-thk, %)
|
||||||
|> patternLinear3d({
|
|> patternLinear3d({
|
||||||
axis: [0, -1, 0],
|
axis: [0, -1, 0],
|
||||||
@ -1148,10 +1148,10 @@ const tabs_r = startSketchOn({
|
|||||||
|> line([0, -10], %)
|
|> line([0, -10], %)
|
||||||
|> line([-10, -5], %)
|
|> line([-10, -5], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> hole(circle([
|
|> hole(circle({ center: [
|
||||||
width / 2 + thk + hole_diam,
|
width / 2 + thk + hole_diam,
|
||||||
length / 2 - hole_diam
|
length / 2 - hole_diam
|
||||||
], hole_diam / 2, %), %)
|
], radius: hole_diam / 2 }, %), %)
|
||||||
|> extrude(-thk, %)
|
|> extrude(-thk, %)
|
||||||
|> patternLinear3d({
|
|> patternLinear3d({
|
||||||
axis: [0, -1, 0],
|
axis: [0, -1, 0],
|
||||||
@ -1172,10 +1172,10 @@ const tabs_l = startSketchOn({
|
|||||||
|> line([0, -10], %)
|
|> line([0, -10], %)
|
||||||
|> line([10, -5], %)
|
|> line([10, -5], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> hole(circle([
|
|> hole(circle({ center: [
|
||||||
-width / 2 - thk - hole_diam,
|
-width / 2 - thk - hole_diam,
|
||||||
length / 2 - hole_diam
|
length / 2 - hole_diam
|
||||||
], hole_diam / 2, %), %)
|
], radius: hole_diam / 2 }, %), %)
|
||||||
|> extrude(-thk, %)
|
|> extrude(-thk, %)
|
||||||
|> patternLinear3d({
|
|> patternLinear3d({
|
||||||
axis: [0, -1, 0],
|
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')
|
const cylinder = startSketchOn('XY')
|
||||||
|> circle([0,0], 22, %)
|
|> circle({ center: [0, 0], radius: 22 }, %)
|
||||||
|> extrude(14, %)
|
|> extrude(14, %)
|
||||||
|
@ -61,8 +61,8 @@ const case = startSketchOn('XY')
|
|||||||
fn m25Screw = (x, y, height) => {
|
fn m25Screw = (x, y, height) => {
|
||||||
const screw = startSketchOn("XY")
|
const screw = startSketchOn("XY")
|
||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
|> circle([x, y], 2.5, %)
|
|> circle({ center: [x, y], radius: 2.5 }, %)
|
||||||
|> hole(circle([x, y], 1.25, %), %)
|
|> hole(circle({ center: [x, y], radius: 1.25 }, %), %)
|
||||||
|> extrude(height, %)
|
|> extrude(height, %)
|
||||||
return screw
|
return screw
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// A mounting bracket for the Focusrite Scarlett Solo audio interface
|
// 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
|
// 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
|
// define constants in mm
|
||||||
const radius = 6.0
|
const radius = 6.0
|
||||||
const width = 144.0
|
const width = 144.0
|
||||||
@ -79,10 +80,13 @@ const tabsR = startSketchOn(tabPlane)
|
|||||||
|> line([0, -tabLength / 3 * 2], %, $edge12)
|
|> line([0, -tabLength / 3 * 2], %, $edge12)
|
||||||
|> line([-tabWidth, -tabLength / 3], %, $edge13)
|
|> line([-tabWidth, -tabLength / 3], %, $edge13)
|
||||||
|> close(%, $edge14)
|
|> close(%, $edge14)
|
||||||
|> hole(circle([
|
|> hole(circle({
|
||||||
width / 2 + thk + tabWidth / 2,
|
center: [
|
||||||
length / 2 + thk - (tabLength / (3 / 2))
|
width / 2 + thk + tabWidth / 2,
|
||||||
], holeDiam / 2, %), %)
|
length / 2 + thk - (tabLength / (3 / 2))
|
||||||
|
],
|
||||||
|
radius: holeDiam / 2
|
||||||
|
}, %), %)
|
||||||
|> extrude(-tabThk, %)
|
|> extrude(-tabThk, %)
|
||||||
|> fillet({
|
|> fillet({
|
||||||
radius: holeDiam / 2,
|
radius: holeDiam / 2,
|
||||||
@ -104,10 +108,13 @@ const tabsL = startSketchOn(tabPlane)
|
|||||||
|> line([0, -tabLength / 3 * 2], %, $edge22)
|
|> line([0, -tabLength / 3 * 2], %, $edge22)
|
||||||
|> line([tabWidth, -tabLength / 3], %, $edge23)
|
|> line([tabWidth, -tabLength / 3], %, $edge23)
|
||||||
|> close(%, $edge24)
|
|> close(%, $edge24)
|
||||||
|> hole(circle([
|
|> hole(circle({
|
||||||
-width / 2 - thk - (tabWidth / 2),
|
center: [
|
||||||
length / 2 + thk - (tabLength / (3 / 2))
|
-width / 2 - thk - (tabWidth / 2),
|
||||||
], holeDiam / 2, %), %)
|
length / 2 + thk - (tabLength / (3 / 2))
|
||||||
|
],
|
||||||
|
radius: holeDiam / 2
|
||||||
|
}, %), %)
|
||||||
|> extrude(-tabThk, %)
|
|> extrude(-tabThk, %)
|
||||||
|> fillet({
|
|> fillet({
|
||||||
radius: holeDiam / 2,
|
radius: holeDiam / 2,
|
||||||
|
@ -80,10 +80,13 @@ const tabsR = startSketchOn(tabPlane)
|
|||||||
|> line([0, -tabLength / 3 * 2], %, $edge12)
|
|> line([0, -tabLength / 3 * 2], %, $edge12)
|
||||||
|> line([-tabWidth, -tabLength / 3], %, $edge13)
|
|> line([-tabWidth, -tabLength / 3], %, $edge13)
|
||||||
|> close(%, $edge14)
|
|> close(%, $edge14)
|
||||||
|> hole(circle([
|
|> hole(circle({
|
||||||
width / 2 + thk + tabWidth / 2,
|
center: [
|
||||||
length / 2 + thk - (tabLength / (3 / 2))
|
width / 2 + thk + tabWidth / 2,
|
||||||
], holeDiam / 2, %), %)
|
length / 2 + thk - (tabLength / (3 / 2))
|
||||||
|
],
|
||||||
|
radius: holeDiam / 2
|
||||||
|
}, %), %)
|
||||||
|> extrude(-tabThk, %)
|
|> extrude(-tabThk, %)
|
||||||
|> fillet({
|
|> fillet({
|
||||||
radius: holeDiam / 2,
|
radius: holeDiam / 2,
|
||||||
@ -105,10 +108,13 @@ const tabsL = startSketchOn(tabPlane)
|
|||||||
|> line([0, -tabLength / 3 * 2], %, $edge22)
|
|> line([0, -tabLength / 3 * 2], %, $edge22)
|
||||||
|> line([tabWidth, -tabLength / 3], %, $edge23)
|
|> line([tabWidth, -tabLength / 3], %, $edge23)
|
||||||
|> close(%, $edge24)
|
|> close(%, $edge24)
|
||||||
|> hole(circle([
|
|> hole(circle({
|
||||||
-width / 2 - thk - (tabWidth / 2),
|
center: [
|
||||||
length / 2 + thk - (tabLength / (3 / 2))
|
-width / 2 - thk - (tabWidth / 2),
|
||||||
], holeDiam / 2, %), %)
|
length / 2 + thk - (tabLength / (3 / 2))
|
||||||
|
],
|
||||||
|
radius: holeDiam / 2
|
||||||
|
}, %), %)
|
||||||
|> extrude(-tabThk, %)
|
|> extrude(-tabThk, %)
|
||||||
|> fillet({
|
|> fillet({
|
||||||
radius: holeDiam / 2,
|
radius: holeDiam / 2,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
const part001 = startSketchOn('XY')
|
const part001 = startSketchOn('XY')
|
||||||
|> circle([5, 5], 10, %)
|
|> circle({ center: [5, 5], radius: 10 }, %)
|
||||||
|> extrude(10, %)
|
|> extrude(10, %)
|
||||||
|> helix({revolutions: 16, angle_start: 0, ccw: true}, %)
|
|> helix({revolutions: 16, angle_start: 0, ccw: true}, %)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
const part001 = startSketchOn('XY')
|
const part001 = startSketchOn('XY')
|
||||||
|> circle([5, 5], 10, %)
|
|> circle({ center: [5, 5], radius: 10 }, %)
|
||||||
|> extrude(10, %)
|
|> extrude(10, %)
|
||||||
|> helix({revolutions: 16, angle_start: 0}, %)
|
|> helix({revolutions: 16, angle_start: 0}, %)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
const part001 = startSketchOn('XY')
|
const part001 = startSketchOn('XY')
|
||||||
|> circle([5, 5], 10, %)
|
|> circle({ center: [5, 5], radius: 10 }, %)
|
||||||
|> extrude(-10, %)
|
|> extrude(-10, %)
|
||||||
|> helix({revolutions: 16, angle_start: 0}, %)
|
|> helix({revolutions: 16, angle_start: 0}, %)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
const part001 = startSketchOn('XY')
|
const part001 = startSketchOn('XY')
|
||||||
|> circle([5, 5], 10, %)
|
|> circle({ center: [5, 5], radius: 10 }, %)
|
||||||
|> extrude(10, %)
|
|> extrude(10, %)
|
||||||
|> helix({revolutions: 16, angle_start: 0, length: 3}, %)
|
|> helix({revolutions: 16, angle_start: 0, length: 3}, %)
|
||||||
|
@ -39,10 +39,10 @@ const shellExtrude = startSketchOn(s, "start")
|
|||||||
|> extrude(-(height - t), %)
|
|> extrude(-(height - t), %)
|
||||||
|
|
||||||
const peg = startSketchOn(s, "end")
|
const peg = startSketchOn(s, "end")
|
||||||
|> circle([
|
|> circle({ center: [
|
||||||
-(total_width / 2 - wSegments),
|
-(total_width / 2 - wSegments),
|
||||||
-(total_length / 2 - lSegments)
|
-(total_length / 2 - lSegments)
|
||||||
], bumpDiam / 2, %)
|
], radius: bumpDiam / 2 }, %)
|
||||||
|> patternLinear2d({
|
|> patternLinear2d({
|
||||||
axis: [1, 0],
|
axis: [1, 0],
|
||||||
repetitions: 5,
|
repetitions: 5,
|
||||||
|
@ -14,7 +14,7 @@ fn transform = (replicaId) => {
|
|||||||
// Each layer is just a pretty thin cylinder with a fillet.
|
// Each layer is just a pretty thin cylinder with a fillet.
|
||||||
fn layer = () => {
|
fn layer = () => {
|
||||||
return startSketchOn("XY") // or some other plane idk
|
return startSketchOn("XY") // or some other plane idk
|
||||||
|> circle([0, 0], 1, %, $tag1)
|
|> circle({ center: [0, 0], radius: 1 }, %, $tag1)
|
||||||
|> extrude(h, %)
|
|> extrude(h, %)
|
||||||
// |> fillet({
|
// |> fillet({
|
||||||
// radius: h / 2.01,
|
// radius: h / 2.01,
|
||||||
|
@ -30,22 +30,22 @@ fn caster = (originStart) => {
|
|||||||
|> xLine(-3.543, %)
|
|> xLine(-3.543, %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> hole(circle([
|
|> hole(circle({ center: [
|
||||||
(3.543 - 2.756) / 2,
|
(3.543 - 2.756) / 2,
|
||||||
(3.543 - 2.756) / 2
|
(3.543 - 2.756) / 2
|
||||||
], 8.8 / 2 / 25.4, %), %)
|
], radius: 8.8 / 2 / 25.4}, %), %)
|
||||||
|> hole(circle([
|
|> hole(circle({ center: [
|
||||||
(3.543 - 2.756) / 2 + 2.756,
|
(3.543 - 2.756) / 2 + 2.756,
|
||||||
(3.543 - 2.756) / 2
|
(3.543 - 2.756) / 2
|
||||||
], 8.8 / 2 / 25.4, %), %)
|
], radius: 8.8 / 2 / 25.4 }, %), %)
|
||||||
|> hole(circle([
|
|> hole(circle({ center: [
|
||||||
(3.543 - 2.756) / 2,
|
(3.543 - 2.756) / 2,
|
||||||
(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 }, %), %)
|
||||||
|> hole(circle([
|
|> hole(circle({ center: [
|
||||||
(3.543 - 2.756) / 2 + 2.756,
|
(3.543 - 2.756) / 2 + 2.756,
|
||||||
(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, %)
|
|> extrude(-.25, %)
|
||||||
|
|
||||||
const sketch002c = startSketchOn(sketch001c, 'START')
|
const sketch002c = startSketchOn(sketch001c, 'START')
|
||||||
@ -71,7 +71,7 @@ fn caster = (originStart) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const sketch003c = startSketchOn(plane002c)
|
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)
|
const examplec = extrude(-1 - (3 / 16), sketch003c)
|
||||||
return examplec
|
return examplec
|
||||||
}
|
}
|
||||||
|
@ -28,22 +28,22 @@ fn caster = (originStart) => {
|
|||||||
|> xLine(-3.543, %)
|
|> xLine(-3.543, %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> hole(circle([
|
|> hole(circle({ center: [
|
||||||
(3.543 - 2.756) / 2,
|
(3.543 - 2.756) / 2,
|
||||||
(3.543 - 2.756) / 2
|
(3.543 - 2.756) / 2
|
||||||
], 8.8 / 2 / 25.4, %), %)
|
], radius: 8.8 / 2 / 25.4 }, %), %)
|
||||||
|> hole(circle([
|
|> hole(circle({ center: [
|
||||||
(3.543 - 2.756) / 2 + 2.756,
|
(3.543 - 2.756) / 2 + 2.756,
|
||||||
(3.543 - 2.756) / 2
|
(3.543 - 2.756) / 2
|
||||||
], 8.8 / 2 / 25.4, %), %)
|
], radius: 8.8 / 2 / 25.4 }, %), %)
|
||||||
|> hole(circle([
|
|> hole(circle({ center: [
|
||||||
(3.543 - 2.756) / 2,
|
(3.543 - 2.756) / 2,
|
||||||
(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 }, %), %)
|
||||||
|> hole(circle([
|
|> hole(circle({ center: [
|
||||||
(3.543 - 2.756) / 2 + 2.756,
|
(3.543 - 2.756) / 2 + 2.756,
|
||||||
(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, %)
|
|> extrude(-.25, %)
|
||||||
|
|
||||||
const sketch002c = startSketchOn(sketch001c, 'START')
|
const sketch002c = startSketchOn(sketch001c, 'START')
|
||||||
@ -69,7 +69,7 @@ fn caster = (originStart) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const sketch003c = startSketchOn(plane002c)
|
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)
|
const examplec = extrude(-1 - (3 / 16), sketch003c)
|
||||||
return examplec
|
return examplec
|
||||||
}
|
}
|
||||||
|
@ -12,5 +12,5 @@ const part001 = cube([0,0], 20)
|
|||||||
|> extrude(20, %)
|
|> extrude(20, %)
|
||||||
|
|
||||||
const part002 = startSketchOn(part001, "end")
|
const part002 = startSketchOn(part001, "end")
|
||||||
|> circle([0, 0], 5, %, $myCircle)
|
|> circle({ center: [0, 0], radisu: 5 }, %, $myCircle)
|
||||||
|> extrude(5, %)
|
|> extrude(5, %)
|
||||||
|
@ -62,10 +62,10 @@ fn tr = (i) => {
|
|||||||
// Create the pegs on the top of the base
|
// Create the pegs on the top of the base
|
||||||
const totalBumps = (wbumps * lbumps)-1
|
const totalBumps = (wbumps * lbumps)-1
|
||||||
const peg = startSketchOn(s, 'end')
|
const peg = startSketchOn(s, 'end')
|
||||||
|> circle([
|
|> circle({ center: [
|
||||||
-(pitch*(wbumps-1)/2),
|
-(pitch*(wbumps-1)/2),
|
||||||
-(pitch*(lbumps-1)/2)
|
-(pitch*(lbumps-1)/2)
|
||||||
], bumpDiam / 2, %)
|
], radius: bumpDiam / 2 }, %)
|
||||||
|> patternLinear2d({
|
|> patternLinear2d({
|
||||||
axis: [1, 0],
|
axis: [1, 0],
|
||||||
repetitions: wbumps-1,
|
repetitions: wbumps-1,
|
||||||
|
@ -301,8 +301,8 @@ async fn kcl_test_holes() {
|
|||||||
|> line([10, 0], %)
|
|> line([10, 0], %)
|
||||||
|> line([0, -10], %)
|
|> line([0, -10], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> hole(circle([2, 2], .5, %), %)
|
|> hole(circle({ center: [2, 2], radius: .5 }, %), %)
|
||||||
|> hole(circle([2, 8], .5, %), %)
|
|> hole(circle({ center: [2, 8], radius: .5 }, %), %)
|
||||||
|> extrude(2, %)
|
|> extrude(2, %)
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
@ -354,10 +354,10 @@ const holeRadius = 1
|
|||||||
const holeIndex = 6
|
const holeIndex = 6
|
||||||
|
|
||||||
const part = roundedRectangle([0, 0], 20, 20, 4)
|
const part = roundedRectangle([0, 0], 20, 20, 4)
|
||||||
|> hole(circle([-holeIndex, holeIndex], holeRadius, %), %)
|
|> hole(circle({ center: [-holeIndex, holeIndex], radius: holeRadius }, %), %)
|
||||||
|> hole(circle([holeIndex, holeIndex], holeRadius, %), %)
|
|> hole(circle({ center: [holeIndex, holeIndex], radius: holeRadius }, %), %)
|
||||||
|> hole(circle([-holeIndex, -holeIndex], holeRadius, %), %)
|
|> hole(circle({ center: [-holeIndex, -holeIndex], radius: holeRadius }, %), %)
|
||||||
|> hole(circle([holeIndex, -holeIndex], holeRadius, %), %)
|
|> hole(circle({ center: [holeIndex, -holeIndex], radius: holeRadius }, %), %)
|
||||||
|> extrude(2, %)
|
|> extrude(2, %)
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
@ -367,7 +367,7 @@ const part = roundedRectangle([0, 0], 20, 20, 4)
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn kcl_test_top_level_expression() {
|
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();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
assert_out("top_level_expression", &result);
|
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
|
let code = r#"const num = 12
|
||||||
const distance = 5
|
const distance = 5
|
||||||
const part = startSketchOn('XY')
|
const part = startSketchOn('XY')
|
||||||
|> circle([0,0], 2, %)
|
|> circle({ center: [0,0], radius: 2 }, %)
|
||||||
|> patternLinear2d({axis: [0,1], repetitions: num -1, distance: distance - 1}, %)
|
|> patternLinear2d({axis: [0,1], repetitions: num -1, distance: distance - 1}, %)
|
||||||
|> extrude(1, %)
|
|> extrude(1, %)
|
||||||
"#;
|
"#;
|
||||||
@ -390,7 +390,7 @@ const part = startSketchOn('XY')
|
|||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn kcl_test_patterns_linear_basic() {
|
async fn kcl_test_patterns_linear_basic() {
|
||||||
let code = r#"const part = startSketchOn('XY')
|
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}, %)
|
|> patternLinear2d({axis: [0,1], repetitions: 12, distance: 4}, %)
|
||||||
|> extrude(1, %)
|
|> extrude(1, %)
|
||||||
"#;
|
"#;
|
||||||
@ -418,7 +418,7 @@ async fn kcl_test_patterns_linear_basic_3d() {
|
|||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn kcl_test_patterns_linear_basic_negative_distance() {
|
async fn kcl_test_patterns_linear_basic_negative_distance() {
|
||||||
let code = r#"const part = startSketchOn('XY')
|
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}, %)
|
|> patternLinear2d({axis: [0,1], repetitions: 12, distance: -2}, %)
|
||||||
|> extrude(1, %)
|
|> extrude(1, %)
|
||||||
"#;
|
"#;
|
||||||
@ -430,7 +430,7 @@ async fn kcl_test_patterns_linear_basic_negative_distance() {
|
|||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn kcl_test_patterns_linear_basic_negative_axis() {
|
async fn kcl_test_patterns_linear_basic_negative_axis() {
|
||||||
let code = r#"const part = startSketchOn('XY')
|
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}, %)
|
|> patternLinear2d({axis: [0,-1], repetitions: 12, distance: 2}, %)
|
||||||
|> extrude(1, %)
|
|> extrude(1, %)
|
||||||
"#;
|
"#;
|
||||||
@ -442,7 +442,7 @@ async fn kcl_test_patterns_linear_basic_negative_axis() {
|
|||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn kcl_test_patterns_linear_basic_holes() {
|
async fn kcl_test_patterns_linear_basic_holes() {
|
||||||
let code = r#"const circles = startSketchOn('XY')
|
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}, %)
|
|> patternLinear2d({axis: [1,1], repetitions: 12, distance: 3}, %)
|
||||||
|
|
||||||
const rectangle = startSketchOn('XY')
|
const rectangle = startSketchOn('XY')
|
||||||
@ -463,7 +463,7 @@ const rectangle = startSketchOn('XY')
|
|||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn kcl_test_patterns_circular_basic_2d() {
|
async fn kcl_test_patterns_circular_basic_2d() {
|
||||||
let code = r#"const part = startSketchOn('XY')
|
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}, %)
|
|> patternCircular2d({center: [20, 20], repetitions: 12, arcDegrees: 210, rotateDuplicates: true}, %)
|
||||||
|> extrude(1, %)
|
|> extrude(1, %)
|
||||||
"#;
|
"#;
|
||||||
@ -787,8 +787,8 @@ async fn kcl_test_stdlib_kcl_error_right_code_path() {
|
|||||||
|> line([10, 0], %)
|
|> line([10, 0], %)
|
||||||
|> line([0, -10], %)
|
|> line([0, -10], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> hole(circle([2, 2], .5), %)
|
|> hole(circle({ center: [2, 2], radius: .5 }), %)
|
||||||
|> hole(circle([2, 8], .5, %), %)
|
|> hole(circle({ center: [2, 8], radius: .5 }, %), %)
|
||||||
|> extrude(2, %)
|
|> extrude(2, %)
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
@ -816,7 +816,7 @@ const part001 = cube([0,0], 20)
|
|||||||
|> extrude(20, %)
|
|> extrude(20, %)
|
||||||
|
|
||||||
const part002 = startSketchOn(part001, "end")
|
const part002 = startSketchOn(part001, "end")
|
||||||
|> circle([0, 0], 5, %)
|
|> circle({ center: [0, 0], radius: 5 }, %)
|
||||||
|> extrude(5, %)
|
|> extrude(5, %)
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
@ -1085,7 +1085,7 @@ async fn kcl_test_revolve_on_face_circle_edge() {
|
|||||||
|> extrude(20, %)
|
|> extrude(20, %)
|
||||||
|
|
||||||
const sketch001 = startSketchOn(box, "END")
|
const sketch001 = startSketchOn(box, "END")
|
||||||
|> circle([10,10], 4, %)
|
|> circle({ center: [10,10], radius: 4 }, %)
|
||||||
|> revolve({
|
|> revolve({
|
||||||
angle: 90,
|
angle: 90,
|
||||||
axis: getOppositeEdge(revolveAxis)
|
axis: getOppositeEdge(revolveAxis)
|
||||||
@ -1107,7 +1107,7 @@ async fn kcl_test_revolve_on_face_circle() {
|
|||||||
|> extrude(20, %)
|
|> extrude(20, %)
|
||||||
|
|
||||||
const sketch001 = startSketchOn(box, "END")
|
const sketch001 = startSketchOn(box, "END")
|
||||||
|> circle([10,10], 4, %)
|
|> circle({ center: [10,10], radius: 4 }, %)
|
||||||
|> revolve({
|
|> revolve({
|
||||||
angle: -90,
|
angle: -90,
|
||||||
axis: 'y'
|
axis: 'y'
|
||||||
@ -1147,7 +1147,7 @@ const sketch001 = startSketchOn(box, "end")
|
|||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn kcl_test_basic_revolve_circle() {
|
async fn kcl_test_basic_revolve_circle() {
|
||||||
let code = r#"const sketch001 = startSketchOn('XY')
|
let code = r#"const sketch001 = startSketchOn('XY')
|
||||||
|> circle([15, 0], 5, %)
|
|> circle({ center: [15, 0], radius: 5 }, %)
|
||||||
|> revolve({
|
|> revolve({
|
||||||
angle: 360,
|
angle: 360,
|
||||||
axis: 'y'
|
axis: 'y'
|
||||||
@ -1271,10 +1271,10 @@ async fn kcl_test_member_expression_in_params() {
|
|||||||
z_axis: { x: 0, y: 1, z: 0 }
|
z_axis: { x: 0, y: 1, z: 0 }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|> circle([0, 0], capDia / 2, %)
|
|> circle({ center: [0, 0], radius: capDia / 2 }, %)
|
||||||
|> extrude(capHeadLength, %)
|
|> extrude(capHeadLength, %)
|
||||||
const screw = startSketchOn(screwHead, "start")
|
const screw = startSketchOn(screwHead, "start")
|
||||||
|> circle([0, 0], dia / 2, %)
|
|> circle({ center: [0, 0], radius: dia / 2 }, %)
|
||||||
|> extrude(length, %)
|
|> extrude(length, %)
|
||||||
return screw
|
return screw
|
||||||
}
|
}
|
||||||
@ -1343,7 +1343,7 @@ async fn kcl_test_error_empty_start_sketch_on_string() {
|
|||||||
|> extrude(100, %)
|
|> extrude(100, %)
|
||||||
|
|
||||||
const secondSketch = startSketchOn(part001, '')
|
const secondSketch = startSketchOn(part001, '')
|
||||||
|> circle([-20, 50], 40, %)
|
|> circle({ center: [-20, 50], radius: 40 }, %)
|
||||||
|> extrude(20, %)
|
|> extrude(20, %)
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
@ -1373,7 +1373,7 @@ fn squareHole = (l, w) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const extrusion = startSketchOn('XY')
|
const extrusion = startSketchOn('XY')
|
||||||
|> circle([0, 0], dia/2, %)
|
|> circle({ center: [0, 0], radius: dia/2 }, %)
|
||||||
|> hole(squareHole(length, width, height), %)
|
|> hole(squareHole(length, width, height), %)
|
||||||
|> extrude(height, %)
|
|> extrude(height, %)
|
||||||
"#;
|
"#;
|
||||||
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 0 B |