Show offset planes in the scene, let user select them (#4481)
* Update offset_plane to actually create and show the plane in-engine * Fix broken ability to use offsetPlanes in startSketchOn * Make the newly-visible offset planes usable for sketching via UI * Add a playwright test for sketching on an offset plane via point-and-click * cargo clippy & cargo fmt * Make `PlaneData` the first item of `SketchData` so autocomplete continues to work well for `startSketchOn` * @nadr0 feedback re: `offsetIndex` * From @jtran: "Need to call the ID generator so that IDs are stable." * More feedback from @jtran and fix incomplete use of `id_generator` in last commit * Oops I missed saving `isPathToNodeNumber` earlier 🤦🏻 * Make the distinction between `Plane` and `PlaneOrientationData` more clear per @nadr0 and @lf94's feedback * Make `newPathToNode` less hardcoded, per @lf94's feedback * Don't need to unbox and rebox `plane` * A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores) * A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest-8-cores) * Rearranging of enums and structs, but the offsetPlanes are still not used by their sketches * A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores) * A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest-8-cores) * Revert all my little newtype fiddling it's a waste of time. * Update docs * cargo fmt * Remove log * Print the unexpected diagnostics * Undo renaming of `PlaneData` * Remove generated PlaneRientationData docs page * Redo doc generation after undoing `PlaneData` rename * Impl FromKclValue for the new plane datatypes * Clippy lint * When starting a sketch, only hide the plane if it's a custom plane * Fix FromKclValue and macro use since merge * Fix to not convert Plane to PlaneData * Make sure offset planes are `Custom` type * SketchData actually doesn't need to be in a certain order This avoids the autocompletion issue I was having. --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Adam Chalmers <adam.chalmers@zoo.dev> Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
This commit is contained in:
@ -9,7 +9,7 @@ Offset a plane by a distance along its normal.
|
|||||||
For example, if you offset the 'XZ' plane by 10, the new plane will be parallel to the 'XZ' plane and 10 units away from it.
|
For example, if you offset the 'XZ' plane by 10, the new plane will be parallel to the 'XZ' plane and 10 units away from it.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
offsetPlane(std_plane: StandardPlane, offset: number) -> PlaneData
|
offsetPlane(std_plane: StandardPlane, offset: number) -> Plane
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ offsetPlane(std_plane: StandardPlane, offset: number) -> PlaneData
|
|||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
[`PlaneData`](/docs/kcl/types/PlaneData) - Data for a plane.
|
[`Plane`](/docs/kcl/types/Plane) - A plane.
|
||||||
|
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
@ -105747,70 +105747,30 @@
|
|||||||
],
|
],
|
||||||
"returnValue": {
|
"returnValue": {
|
||||||
"name": "",
|
"name": "",
|
||||||
"type": "PlaneData",
|
"type": "Plane",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema",
|
"$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema",
|
||||||
"title": "PlaneData",
|
"title": "Plane",
|
||||||
"description": "Data for a plane.",
|
"description": "A plane.",
|
||||||
"oneOf": [
|
|
||||||
{
|
|
||||||
"description": "The XY plane.",
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"XY"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "The opposite side of the XY plane.",
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"-XY"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "The XZ plane.",
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"XZ"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "The opposite side of the XZ plane.",
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"-XZ"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "The YZ plane.",
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"YZ"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "The opposite side of the YZ plane.",
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"-YZ"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "A defined plane.",
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"plane"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"plane": {
|
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
"__meta",
|
||||||
|
"id",
|
||||||
"origin",
|
"origin",
|
||||||
|
"value",
|
||||||
"xAxis",
|
"xAxis",
|
||||||
"yAxis",
|
"yAxis",
|
||||||
"zAxis"
|
"zAxis"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"description": "The id of the plane.",
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"$ref": "#/components/schemas/PlaneType"
|
||||||
|
},
|
||||||
"origin": {
|
"origin": {
|
||||||
"description": "Origin of the plane.",
|
"description": "Origin of the plane.",
|
||||||
"allOf": [
|
"allOf": [
|
||||||
@ -105842,14 +105802,35 @@
|
|||||||
"$ref": "#/components/schemas/Point3d"
|
"$ref": "#/components/schemas/Point3d"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
"__meta": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/Metadata"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
"PlaneType": {
|
||||||
|
"description": "Type for a plane.",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"XY",
|
||||||
|
"XZ",
|
||||||
|
"YZ"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "A custom plane.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"Custom"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"Point3d": {
|
"Point3d": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -105871,6 +105852,33 @@
|
|||||||
"format": "double"
|
"format": "double"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Metadata": {
|
||||||
|
"description": "Metadata.",
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"sourceRange"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"sourceRange": {
|
||||||
|
"description": "The source range.",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/SourceRange"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SourceRange": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"maxItems": 3,
|
||||||
|
"minItems": 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -179036,13 +179044,16 @@
|
|||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/PlaneData"
|
"$ref": "#/components/schemas/PlaneData"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Plane"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/Solid"
|
"$ref": "#/components/schemas/Solid"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"definitions": {
|
"definitions": {
|
||||||
"PlaneData": {
|
"PlaneData": {
|
||||||
"description": "Data for a plane.",
|
"description": "Orientation data that can be used to construct a plane, not a plane in itself.",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
"description": "The XY plane.",
|
"description": "The XY plane.",
|
||||||
@ -179163,6 +179174,114 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Plane": {
|
||||||
|
"description": "A plane.",
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"__meta",
|
||||||
|
"id",
|
||||||
|
"origin",
|
||||||
|
"value",
|
||||||
|
"xAxis",
|
||||||
|
"yAxis",
|
||||||
|
"zAxis"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"description": "The id of the plane.",
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"$ref": "#/components/schemas/PlaneType"
|
||||||
|
},
|
||||||
|
"origin": {
|
||||||
|
"description": "Origin of the plane.",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Point3d"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"xAxis": {
|
||||||
|
"description": "What should the plane’s X axis be?",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Point3d"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"yAxis": {
|
||||||
|
"description": "What should the plane’s Y axis be?",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Point3d"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"zAxis": {
|
||||||
|
"description": "The z-axis (normal).",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Point3d"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"__meta": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/Metadata"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"PlaneType": {
|
||||||
|
"description": "Type for a plane.",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"XY",
|
||||||
|
"XZ",
|
||||||
|
"YZ"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "A custom plane.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"Custom"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Metadata": {
|
||||||
|
"description": "Metadata.",
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"sourceRange"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"sourceRange": {
|
||||||
|
"description": "The source range.",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/SourceRange"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SourceRange": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"maxItems": 3,
|
||||||
|
"minItems": 3
|
||||||
|
},
|
||||||
"Solid": {
|
"Solid": {
|
||||||
"description": "An solid is a collection of extrude surfaces.",
|
"description": "An solid is a collection of extrude surfaces.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@ -179444,16 +179563,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"SourceRange": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "uint",
|
|
||||||
"minimum": 0.0
|
|
||||||
},
|
|
||||||
"maxItems": 3,
|
|
||||||
"minItems": 3
|
|
||||||
},
|
|
||||||
"Sketch": {
|
"Sketch": {
|
||||||
"description": "A sketch is a collection of paths.",
|
"description": "A sketch is a collection of paths.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@ -180208,43 +180317,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"PlaneType": {
|
|
||||||
"description": "Type for a plane.",
|
|
||||||
"oneOf": [
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"XY",
|
|
||||||
"XZ",
|
|
||||||
"YZ"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "A custom plane.",
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"Custom"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Metadata": {
|
|
||||||
"description": "Metadata.",
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"sourceRange"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"sourceRange": {
|
|
||||||
"description": "The source range.",
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/SourceRange"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"BasePath": {
|
"BasePath": {
|
||||||
"description": "A base path.",
|
"description": "A base path.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@ -180460,7 +180532,7 @@
|
|||||||
"nullable": true,
|
"nullable": true,
|
||||||
"definitions": {
|
"definitions": {
|
||||||
"PlaneData": {
|
"PlaneData": {
|
||||||
"description": "Data for a plane.",
|
"description": "Orientation data that can be used to construct a plane, not a plane in itself.",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
"description": "The XY plane.",
|
"description": "The XY plane.",
|
||||||
@ -180581,6 +180653,114 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Plane": {
|
||||||
|
"description": "A plane.",
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"__meta",
|
||||||
|
"id",
|
||||||
|
"origin",
|
||||||
|
"value",
|
||||||
|
"xAxis",
|
||||||
|
"yAxis",
|
||||||
|
"zAxis"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"description": "The id of the plane.",
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"$ref": "#/components/schemas/PlaneType"
|
||||||
|
},
|
||||||
|
"origin": {
|
||||||
|
"description": "Origin of the plane.",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Point3d"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"xAxis": {
|
||||||
|
"description": "What should the plane’s X axis be?",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Point3d"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"yAxis": {
|
||||||
|
"description": "What should the plane’s Y axis be?",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Point3d"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"zAxis": {
|
||||||
|
"description": "The z-axis (normal).",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Point3d"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"__meta": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/Metadata"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"PlaneType": {
|
||||||
|
"description": "Type for a plane.",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"XY",
|
||||||
|
"XZ",
|
||||||
|
"YZ"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "A custom plane.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"Custom"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Metadata": {
|
||||||
|
"description": "Metadata.",
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"sourceRange"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"sourceRange": {
|
||||||
|
"description": "The source range.",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/SourceRange"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SourceRange": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"maxItems": 3,
|
||||||
|
"minItems": 3
|
||||||
|
},
|
||||||
"Solid": {
|
"Solid": {
|
||||||
"description": "An solid is a collection of extrude surfaces.",
|
"description": "An solid is a collection of extrude surfaces.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@ -180862,16 +181042,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"SourceRange": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "uint",
|
|
||||||
"minimum": 0.0
|
|
||||||
},
|
|
||||||
"maxItems": 3,
|
|
||||||
"minItems": 3
|
|
||||||
},
|
|
||||||
"Sketch": {
|
"Sketch": {
|
||||||
"description": "A sketch is a collection of paths.",
|
"description": "A sketch is a collection of paths.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@ -181626,43 +181796,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"PlaneType": {
|
|
||||||
"description": "Type for a plane.",
|
|
||||||
"oneOf": [
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"XY",
|
|
||||||
"XZ",
|
|
||||||
"YZ"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "A custom plane.",
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"Custom"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Metadata": {
|
|
||||||
"description": "Metadata.",
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"sourceRange"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"sourceRange": {
|
|
||||||
"description": "The source range.",
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/SourceRange"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"BasePath": {
|
"BasePath": {
|
||||||
"description": "A base path.",
|
"description": "A base path.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
@ -180,7 +180,7 @@ A plane.
|
|||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `Plane`| | No |
|
| `type` |enum: [`Plane`](/docs/kcl/types/Plane)| | No |
|
||||||
| `id` |`string`| The id of the plane. | No |
|
| `id` |`string`| The id of the plane. | No |
|
||||||
| `value` |[`PlaneType`](/docs/kcl/types/PlaneType)| Any KCL value. | No |
|
| `value` |[`PlaneType`](/docs/kcl/types/PlaneType)| Any KCL value. | No |
|
||||||
| `origin` |[`Point3d`](/docs/kcl/types/Point3d)| Origin of the plane. | No |
|
| `origin` |[`Point3d`](/docs/kcl/types/Point3d)| Origin of the plane. | No |
|
||||||
|
27
docs/kcl/types/Plane.md
Normal file
27
docs/kcl/types/Plane.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
title: "Plane"
|
||||||
|
excerpt: "A plane."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A plane.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `id` |`string`| The id of the plane. | No |
|
||||||
|
| `value` |[`PlaneType`](/docs/kcl/types/PlaneType)| A plane. | No |
|
||||||
|
| `origin` |[`Point3d`](/docs/kcl/types/Point3d)| Origin of the plane. | No |
|
||||||
|
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane’s X axis be? | No |
|
||||||
|
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane’s Y axis be? | No |
|
||||||
|
| `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No |
|
||||||
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "PlaneData"
|
title: "PlaneData"
|
||||||
excerpt: "Data for a plane."
|
excerpt: "Orientation data that can be used to construct a plane, not a plane in itself."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
Data for a plane.
|
Orientation data that can be used to construct a plane, not a plane in itself.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,6 +22,18 @@ Data for start sketch on. You can start a sketch on a plane or an solid.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Data for start sketch on. You can start a sketch on a plane or an solid.
|
||||||
|
|
||||||
|
[`Plane`](/docs/kcl/types/Plane)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
Data for start sketch on. You can start a sketch on a plane or an solid.
|
Data for start sketch on. You can start a sketch on a plane or an solid.
|
||||||
|
|
||||||
|
@ -1274,3 +1274,44 @@ test2.describe('Sketch mode should be toleratant to syntax errors', () => {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test2.describe(`Sketching with offset planes`, () => {
|
||||||
|
test2(
|
||||||
|
`Can select an offset plane to sketch on`,
|
||||||
|
async ({ app, scene, toolbar, editor }) => {
|
||||||
|
// We seed the scene with a single offset plane
|
||||||
|
await app.initialise(`offsetPlane001 = offsetPlane("XY", 10)`)
|
||||||
|
|
||||||
|
const [planeClick, planeHover] = scene.makeMouseHelpers(650, 200)
|
||||||
|
|
||||||
|
await test2.step(`Start sketching on the offset plane`, async () => {
|
||||||
|
await toolbar.startSketchPlaneSelection()
|
||||||
|
|
||||||
|
await test2.step(`Hovering should highlight code`, async () => {
|
||||||
|
await planeHover()
|
||||||
|
await editor.expectState({
|
||||||
|
activeLines: [`offsetPlane001=offsetPlane("XY",10)`],
|
||||||
|
diagnostics: [],
|
||||||
|
highlightedCode: 'offsetPlane("XY", 10)',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await test2.step(
|
||||||
|
`Clicking should select the plane and enter sketch mode`,
|
||||||
|
async () => {
|
||||||
|
await planeClick()
|
||||||
|
// Have to wait for engine-side animation to finish
|
||||||
|
await app.page.waitForTimeout(600)
|
||||||
|
await expect2(toolbar.lineBtn).toBeEnabled()
|
||||||
|
await editor.expectEditor.toContain('startSketchOn(offsetPlane001)')
|
||||||
|
await editor.expectState({
|
||||||
|
activeLines: [`offsetPlane001=offsetPlane("XY",10)`],
|
||||||
|
diagnostics: [],
|
||||||
|
highlightedCode: '',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
Binary file not shown.
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
@ -63,6 +63,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
moveValueIntoNewVariablePath,
|
moveValueIntoNewVariablePath,
|
||||||
sketchOnExtrudedFace,
|
sketchOnExtrudedFace,
|
||||||
|
sketchOnOffsetPlane,
|
||||||
startSketchOnDefault,
|
startSketchOnDefault,
|
||||||
} from 'lang/modifyAst'
|
} from 'lang/modifyAst'
|
||||||
import { Program, parse, recast } from 'lang/wasm'
|
import { Program, parse, recast } from 'lang/wasm'
|
||||||
@ -636,13 +637,16 @@ export const ModelingMachineProvider = ({
|
|||||||
),
|
),
|
||||||
'animate-to-face': fromPromise(async ({ input }) => {
|
'animate-to-face': fromPromise(async ({ input }) => {
|
||||||
if (!input) return undefined
|
if (!input) return undefined
|
||||||
if (input.type === 'extrudeFace') {
|
if (input.type === 'extrudeFace' || input.type === 'offsetPlane') {
|
||||||
const sketched = sketchOnExtrudedFace(
|
const sketched =
|
||||||
|
input.type === 'extrudeFace'
|
||||||
|
? sketchOnExtrudedFace(
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
input.sketchPathToNode,
|
input.sketchPathToNode,
|
||||||
input.extrudePathToNode,
|
input.extrudePathToNode,
|
||||||
input.faceInfo
|
input.faceInfo
|
||||||
)
|
)
|
||||||
|
: sketchOnOffsetPlane(kclManager.ast, input.pathToNode)
|
||||||
if (err(sketched)) {
|
if (err(sketched)) {
|
||||||
const sketchedError = new Error(
|
const sketchedError = new Error(
|
||||||
'Incompatible face, please try another'
|
'Incompatible face, please try another'
|
||||||
@ -654,10 +658,9 @@ export const ModelingMachineProvider = ({
|
|||||||
|
|
||||||
await kclManager.executeAstMock(modifiedAst)
|
await kclManager.executeAstMock(modifiedAst)
|
||||||
|
|
||||||
await letEngineAnimateAndSyncCamAfter(
|
const id =
|
||||||
engineCommandManager,
|
input.type === 'extrudeFace' ? input.faceId : input.planeId
|
||||||
input.faceId
|
await letEngineAnimateAndSyncCamAfter(engineCommandManager, id)
|
||||||
)
|
|
||||||
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
||||||
return {
|
return {
|
||||||
sketchPathToNode: pathToNewSketchNode,
|
sketchPathToNode: pathToNewSketchNode,
|
||||||
|
@ -88,6 +88,10 @@ export function useEngineConnectionSubscriptions() {
|
|||||||
? [codeRef.range]
|
? [codeRef.range]
|
||||||
: [codeRef.range, consumedCodeRef.range]
|
: [codeRef.range, consumedCodeRef.range]
|
||||||
)
|
)
|
||||||
|
} else if (artifact?.type === 'plane') {
|
||||||
|
const codeRef = artifact.codeRef
|
||||||
|
if (err(codeRef)) return
|
||||||
|
editorManager.setHighlightRange([codeRef.range])
|
||||||
} else {
|
} else {
|
||||||
editorManager.setHighlightRange([[0, 0]])
|
editorManager.setHighlightRange([[0, 0]])
|
||||||
}
|
}
|
||||||
@ -186,8 +190,42 @@ export function useEngineConnectionSubscriptions() {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
const artifact =
|
||||||
|
engineCommandManager.artifactGraph.get(planeOrFaceId)
|
||||||
|
|
||||||
|
if (artifact?.type === 'plane') {
|
||||||
|
const planeInfo = await getFaceDetails(planeOrFaceId)
|
||||||
|
sceneInfra.modelingSend({
|
||||||
|
type: 'Select default plane',
|
||||||
|
data: {
|
||||||
|
type: 'offsetPlane',
|
||||||
|
zAxis: [
|
||||||
|
planeInfo.z_axis.x,
|
||||||
|
planeInfo.z_axis.y,
|
||||||
|
planeInfo.z_axis.z,
|
||||||
|
],
|
||||||
|
yAxis: [
|
||||||
|
planeInfo.y_axis.x,
|
||||||
|
planeInfo.y_axis.y,
|
||||||
|
planeInfo.y_axis.z,
|
||||||
|
],
|
||||||
|
position: [
|
||||||
|
planeInfo.origin.x,
|
||||||
|
planeInfo.origin.y,
|
||||||
|
planeInfo.origin.z,
|
||||||
|
].map((num) => num / sceneInfra._baseUnitMultiplier) as [
|
||||||
|
number,
|
||||||
|
number,
|
||||||
|
number
|
||||||
|
],
|
||||||
|
planeId: planeOrFaceId,
|
||||||
|
pathToNode: artifact.codeRef.pathToNode,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Artifact is likely an extrusion face
|
||||||
const faceId = planeOrFaceId
|
const faceId = planeOrFaceId
|
||||||
const artifact = engineCommandManager.artifactGraph.get(faceId)
|
|
||||||
const extrusion = getSweepFromSuspectedSweepSurface(
|
const extrusion = getSweepFromSuspectedSweepSurface(
|
||||||
faceId,
|
faceId,
|
||||||
engineCommandManager.artifactGraph
|
engineCommandManager.artifactGraph
|
||||||
|
@ -19,6 +19,7 @@ import {
|
|||||||
ProgramMemory,
|
ProgramMemory,
|
||||||
SourceRange,
|
SourceRange,
|
||||||
sketchFromKclValue,
|
sketchFromKclValue,
|
||||||
|
isPathToNodeNumber,
|
||||||
} from './wasm'
|
} from './wasm'
|
||||||
import {
|
import {
|
||||||
isNodeSafeToReplacePath,
|
isNodeSafeToReplacePath,
|
||||||
@ -526,6 +527,60 @@ export function sketchOnExtrudedFace(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modify the AST to create a new sketch using the variable declaration
|
||||||
|
* of an offset plane. The new sketch just has to come after the offset
|
||||||
|
* plane declaration.
|
||||||
|
*/
|
||||||
|
export function sketchOnOffsetPlane(
|
||||||
|
node: Node<Program>,
|
||||||
|
offsetPathToNode: PathToNode
|
||||||
|
) {
|
||||||
|
let _node = { ...node }
|
||||||
|
|
||||||
|
// Find the offset plane declaration
|
||||||
|
const offsetPlaneDeclarator = getNodeFromPath<VariableDeclarator>(
|
||||||
|
_node,
|
||||||
|
offsetPathToNode,
|
||||||
|
'VariableDeclarator',
|
||||||
|
true
|
||||||
|
)
|
||||||
|
if (err(offsetPlaneDeclarator)) return offsetPlaneDeclarator
|
||||||
|
const { node: offsetPlaneNode } = offsetPlaneDeclarator
|
||||||
|
const offsetPlaneName = offsetPlaneNode.id.name
|
||||||
|
|
||||||
|
// Create a new sketch declaration
|
||||||
|
const newSketchName = findUniqueName(
|
||||||
|
node,
|
||||||
|
KCL_DEFAULT_CONSTANT_PREFIXES.SKETCH
|
||||||
|
)
|
||||||
|
const newSketch = createVariableDeclaration(
|
||||||
|
newSketchName,
|
||||||
|
createCallExpressionStdLib('startSketchOn', [
|
||||||
|
createIdentifier(offsetPlaneName),
|
||||||
|
]),
|
||||||
|
undefined,
|
||||||
|
'const'
|
||||||
|
)
|
||||||
|
|
||||||
|
// Decide where to insert the new sketch declaration
|
||||||
|
const offsetIndex = offsetPathToNode[1][0]
|
||||||
|
|
||||||
|
if (!isPathToNodeNumber(offsetIndex)) {
|
||||||
|
return new Error('Expected offsetIndex to be a number')
|
||||||
|
}
|
||||||
|
// and insert it
|
||||||
|
_node.body.splice(offsetIndex + 1, 0, newSketch)
|
||||||
|
const newPathToNode = structuredClone(offsetPathToNode)
|
||||||
|
newPathToNode[1][0] = offsetIndex + 1
|
||||||
|
|
||||||
|
// Return the modified AST and the path to the new sketch declaration
|
||||||
|
return {
|
||||||
|
modifiedAst: _node,
|
||||||
|
pathToNode: newPathToNode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const getLastIndex = (pathToNode: PathToNode): number =>
|
export const getLastIndex = (pathToNode: PathToNode): number =>
|
||||||
splitPathAtLastIndex(pathToNode).index
|
splitPathAtLastIndex(pathToNode).index
|
||||||
|
|
||||||
|
@ -98,12 +98,22 @@ sketch004 = startSketchOn(extrude003, seg02)
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
extrude004 = extrude(3, sketch004)
|
extrude004 = extrude(3, sketch004)
|
||||||
`
|
`
|
||||||
|
const exampleCodeOffsetPlanes = `
|
||||||
|
offsetPlane001 = offsetPlane("XY", 20)
|
||||||
|
offsetPlane002 = offsetPlane("XZ", -50)
|
||||||
|
offsetPlane003 = offsetPlane("YZ", 10)
|
||||||
|
|
||||||
|
sketch002 = startSketchOn(offsetPlane001)
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|
|> line([6.78, 15.01], %)
|
||||||
|
`
|
||||||
|
|
||||||
// add more code snippets here and use `getCommands` to get the orderedCommands and responseMap for more tests
|
// add more code snippets here and use `getCommands` to get the orderedCommands and responseMap for more tests
|
||||||
const codeToWriteCacheFor = {
|
const codeToWriteCacheFor = {
|
||||||
exampleCode1,
|
exampleCode1,
|
||||||
sketchOnFaceOnFaceEtc,
|
sketchOnFaceOnFaceEtc,
|
||||||
exampleCodeNo3D,
|
exampleCodeNo3D,
|
||||||
|
exampleCodeOffsetPlanes,
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
type CodeKey = keyof typeof codeToWriteCacheFor
|
type CodeKey = keyof typeof codeToWriteCacheFor
|
||||||
@ -165,6 +175,52 @@ afterAll(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('testing createArtifactGraph', () => {
|
describe('testing createArtifactGraph', () => {
|
||||||
|
describe('code with offset planes and a sketch:', () => {
|
||||||
|
let ast: Program
|
||||||
|
let theMap: ReturnType<typeof createArtifactGraph>
|
||||||
|
|
||||||
|
it('setup', () => {
|
||||||
|
// putting this logic in here because describe blocks runs before beforeAll has finished
|
||||||
|
const {
|
||||||
|
orderedCommands,
|
||||||
|
responseMap,
|
||||||
|
ast: _ast,
|
||||||
|
} = getCommands('exampleCodeOffsetPlanes')
|
||||||
|
ast = _ast
|
||||||
|
theMap = createArtifactGraph({ orderedCommands, responseMap, ast })
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`there should be one sketch`, () => {
|
||||||
|
const sketches = [...filterArtifacts({ types: ['path'] }, theMap)].map(
|
||||||
|
(path) => expandPath(path[1], theMap)
|
||||||
|
)
|
||||||
|
expect(sketches).toHaveLength(1)
|
||||||
|
sketches.forEach((path) => {
|
||||||
|
if (err(path)) throw path
|
||||||
|
expect(path.type).toBe('path')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`there should be three offsetPlanes`, () => {
|
||||||
|
const offsetPlanes = [
|
||||||
|
...filterArtifacts({ types: ['plane'] }, theMap),
|
||||||
|
].map((plane) => expandPlane(plane[1], theMap))
|
||||||
|
expect(offsetPlanes).toHaveLength(3)
|
||||||
|
offsetPlanes.forEach((path) => {
|
||||||
|
expect(path.type).toBe('plane')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`Only one offset plane should have a path`, () => {
|
||||||
|
const offsetPlanes = [
|
||||||
|
...filterArtifacts({ types: ['plane'] }, theMap),
|
||||||
|
].map((plane) => expandPlane(plane[1], theMap))
|
||||||
|
const offsetPlaneWithPaths = offsetPlanes.filter(
|
||||||
|
(plane) => plane.paths.length
|
||||||
|
)
|
||||||
|
expect(offsetPlaneWithPaths).toHaveLength(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
describe('code with an extrusion, fillet and sketch of face:', () => {
|
describe('code with an extrusion, fillet and sketch of face:', () => {
|
||||||
let ast: Program
|
let ast: Program
|
||||||
let theMap: ReturnType<typeof createArtifactGraph>
|
let theMap: ReturnType<typeof createArtifactGraph>
|
||||||
|
@ -249,7 +249,20 @@ export function getArtifactsToUpdate({
|
|||||||
const cmd = command.cmd
|
const cmd = command.cmd
|
||||||
const returnArr: ReturnType<typeof getArtifactsToUpdate> = []
|
const returnArr: ReturnType<typeof getArtifactsToUpdate> = []
|
||||||
if (!response) return returnArr
|
if (!response) return returnArr
|
||||||
if (cmd.type === 'enable_sketch_mode') {
|
if (cmd.type === 'make_plane' && range[1] !== 0) {
|
||||||
|
// If we're calling `make_plane` and the code range doesn't end at `0`
|
||||||
|
// it's not a default plane, but a custom one from the offsetPlane standard library function
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
artifact: {
|
||||||
|
type: 'plane',
|
||||||
|
pathIds: [],
|
||||||
|
codeRef: { range, pathToNode },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
} else if (cmd.type === 'enable_sketch_mode') {
|
||||||
const plane = getArtifact(currentPlaneId)
|
const plane = getArtifact(currentPlaneId)
|
||||||
const pathIds = plane?.type === 'plane' ? plane?.pathIds : []
|
const pathIds = plane?.type === 'plane' ? plane?.pathIds : []
|
||||||
const codeRef =
|
const codeRef =
|
||||||
|
@ -144,6 +144,12 @@ export const parse = (code: string | Error): Node<Program> | Error => {
|
|||||||
|
|
||||||
export type PathToNode = [string | number, string][]
|
export type PathToNode = [string | number, string][]
|
||||||
|
|
||||||
|
export const isPathToNodeNumber = (
|
||||||
|
pathToNode: string | number
|
||||||
|
): pathToNode is number => {
|
||||||
|
return typeof pathToNode === 'number'
|
||||||
|
}
|
||||||
|
|
||||||
export interface ExecState {
|
export interface ExecState {
|
||||||
memory: ProgramMemory
|
memory: ProgramMemory
|
||||||
idGenerator: IdGenerator
|
idGenerator: IdGenerator
|
||||||
|
@ -159,6 +159,15 @@ export type DefaultPlane = {
|
|||||||
yAxis: [number, number, number]
|
yAxis: [number, number, number]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type OffsetPlane = {
|
||||||
|
type: 'offsetPlane'
|
||||||
|
position: [number, number, number]
|
||||||
|
planeId: string
|
||||||
|
pathToNode: PathToNode
|
||||||
|
zAxis: [number, number, number]
|
||||||
|
yAxis: [number, number, number]
|
||||||
|
}
|
||||||
|
|
||||||
export type SegmentOverlayPayload =
|
export type SegmentOverlayPayload =
|
||||||
| {
|
| {
|
||||||
type: 'set-one'
|
type: 'set-one'
|
||||||
@ -198,7 +207,7 @@ export type ModelingMachineEvent =
|
|||||||
| { type: 'Sketch On Face' }
|
| { type: 'Sketch On Face' }
|
||||||
| {
|
| {
|
||||||
type: 'Select default plane'
|
type: 'Select default plane'
|
||||||
data: DefaultPlane | ExtrudeFacePlane
|
data: DefaultPlane | ExtrudeFacePlane | OffsetPlane
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'Set selection'
|
type: 'Set selection'
|
||||||
@ -1394,7 +1403,7 @@ export const modelingMachine = setup({
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
'animate-to-face': fromPromise(
|
'animate-to-face': fromPromise(
|
||||||
async (_: { input?: ExtrudeFacePlane | DefaultPlane }) => {
|
async (_: { input?: ExtrudeFacePlane | DefaultPlane | OffsetPlane }) => {
|
||||||
return {} as
|
return {} as
|
||||||
| undefined
|
| undefined
|
||||||
| {
|
| {
|
||||||
|
@ -801,6 +801,17 @@ impl Plane {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The standard planes are XY, YZ and XZ (in both positive and negative)
|
||||||
|
pub fn is_standard(&self) -> bool {
|
||||||
|
!self.is_custom()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The standard planes are XY, YZ and XZ (in both positive and negative)
|
||||||
|
/// Custom planes are any other plane that the user might specify.
|
||||||
|
pub fn is_custom(&self) -> bool {
|
||||||
|
matches!(self.value, PlaneType::Custom)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
@ -1049,6 +1060,14 @@ impl KclValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_plane(&self) -> Option<&Plane> {
|
||||||
|
if let KclValue::Plane(value) = &self {
|
||||||
|
Some(value)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_solid(&self) -> Option<&Solid> {
|
pub fn as_solid(&self) -> Option<&Solid> {
|
||||||
if let KclValue::Solid(value) = &self {
|
if let KclValue::Solid(value) = &self {
|
||||||
Some(value)
|
Some(value)
|
||||||
|
@ -2,7 +2,7 @@ use std::collections::BTreeMap;
|
|||||||
|
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use tower_lsp::{
|
use tower_lsp::{
|
||||||
lsp_types::{SemanticTokenModifier, SemanticTokenType},
|
lsp_types::{Diagnostic, SemanticTokenModifier, SemanticTokenType},
|
||||||
LanguageServer,
|
LanguageServer,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2369,7 +2369,14 @@ async fn kcl_test_kcl_lsp_diagnostics_on_execution_error() {
|
|||||||
|
|
||||||
// Get the diagnostics.
|
// Get the diagnostics.
|
||||||
let diagnostics = server.diagnostics_map.get("file:///test.kcl");
|
let diagnostics = server.diagnostics_map.get("file:///test.kcl");
|
||||||
assert!(diagnostics.is_none());
|
if let Some(diagnostics) = diagnostics {
|
||||||
|
let ds: Vec<Diagnostic> = diagnostics.to_owned();
|
||||||
|
eprintln!("Expected no diagnostics, but found some.");
|
||||||
|
for d in ds {
|
||||||
|
eprintln!("{:?}: {}", d.severity, d.message);
|
||||||
|
}
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
@ -794,6 +794,45 @@ impl<'a> FromKclValue<'a> for crate::std::planes::StandardPlane {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> FromKclValue<'a> for crate::executor::Plane {
|
||||||
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||||
|
if let Some(plane) = arg.as_plane() {
|
||||||
|
return Some(plane.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let obj = arg.as_object()?;
|
||||||
|
let_field_of!(obj, id);
|
||||||
|
let_field_of!(obj, value);
|
||||||
|
let_field_of!(obj, origin);
|
||||||
|
let_field_of!(obj, x_axis "xAxis");
|
||||||
|
let_field_of!(obj, y_axis "yAxis");
|
||||||
|
let_field_of!(obj, z_axis "zAxis");
|
||||||
|
let_field_of!(obj, meta "__meta");
|
||||||
|
Some(Self {
|
||||||
|
id,
|
||||||
|
value,
|
||||||
|
origin,
|
||||||
|
x_axis,
|
||||||
|
y_axis,
|
||||||
|
z_axis,
|
||||||
|
meta,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FromKclValue<'a> for crate::executor::PlaneType {
|
||||||
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||||
|
let plane_type = match arg.as_str()? {
|
||||||
|
"XY" | "xy" => Self::XY,
|
||||||
|
"XZ" | "xz" => Self::XZ,
|
||||||
|
"YZ" | "yz" => Self::YZ,
|
||||||
|
"Custom" => Self::Custom,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
Some(plane_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::units::UnitLength {
|
impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::units::UnitLength {
|
||||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||||
let s = arg.as_str()?;
|
let s = arg.as_str()?;
|
||||||
@ -1264,11 +1303,15 @@ impl<'a> FromKclValue<'a> for crate::executor::Solid {
|
|||||||
|
|
||||||
impl<'a> FromKclValue<'a> for super::sketch::SketchData {
|
impl<'a> FromKclValue<'a> for super::sketch::SketchData {
|
||||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||||
let case1 = super::sketch::PlaneData::from_kcl_val;
|
// Order is critical since PlaneData is a subset of Plane.
|
||||||
let case2 = crate::executor::Solid::from_kcl_val;
|
let case1 = crate::executor::Plane::from_kcl_val;
|
||||||
|
let case2 = super::sketch::PlaneData::from_kcl_val;
|
||||||
|
let case3 = crate::executor::Solid::from_kcl_val;
|
||||||
case1(arg)
|
case1(arg)
|
||||||
|
.map(Box::new)
|
||||||
.map(Self::Plane)
|
.map(Self::Plane)
|
||||||
.or_else(|| case2(arg).map(Box::new).map(Self::Solid))
|
.or_else(|| case2(arg).map(Self::PlaneOrientation))
|
||||||
|
.or_else(|| case3(arg).map(Box::new).map(Self::Solid))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
//! Standard library plane helpers.
|
//! Standard library plane helpers.
|
||||||
|
|
||||||
use derive_docs::stdlib;
|
use derive_docs::stdlib;
|
||||||
|
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, shared::Color, ModelingCmd};
|
||||||
|
use kittycad_modeling_cmds as kcmc;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::KclError,
|
errors::KclError,
|
||||||
executor::{ExecState, KclValue, Plane},
|
executor::{ExecState, KclValue, Plane, PlaneType},
|
||||||
std::{sketch::PlaneData, Args},
|
std::{sketch::PlaneData, Args},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// One of the standard planes.
|
/// One of the standard planes.
|
||||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, JsonSchema)]
|
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub enum StandardPlane {
|
pub enum StandardPlane {
|
||||||
/// The XY plane.
|
/// The XY plane.
|
||||||
@ -50,8 +53,8 @@ impl From<StandardPlane> for PlaneData {
|
|||||||
/// Offset a plane by a distance along its normal.
|
/// Offset a plane by a distance along its normal.
|
||||||
pub async fn offset_plane(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn offset_plane(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let (std_plane, offset): (StandardPlane, f64) = args.get_data_and_float()?;
|
let (std_plane, offset): (StandardPlane, f64) = args.get_data_and_float()?;
|
||||||
let plane_data = inner_offset_plane(std_plane, offset, exec_state).await?;
|
let plane = inner_offset_plane(std_plane, offset, exec_state).await?;
|
||||||
let plane = Plane::from_plane_data(plane_data, exec_state);
|
make_offset_plane_in_engine(&plane, exec_state, &args).await?;
|
||||||
Ok(KclValue::Plane(Box::new(plane)))
|
Ok(KclValue::Plane(Box::new(plane)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,11 +147,14 @@ async fn inner_offset_plane(
|
|||||||
std_plane: StandardPlane,
|
std_plane: StandardPlane,
|
||||||
offset: f64,
|
offset: f64,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
) -> Result<PlaneData, KclError> {
|
) -> Result<Plane, KclError> {
|
||||||
// Convert to the plane type.
|
// Convert to the plane type.
|
||||||
let plane_data: PlaneData = std_plane.into();
|
let plane_data: PlaneData = std_plane.into();
|
||||||
// Convert to a plane.
|
// Convert to a plane.
|
||||||
let mut plane = Plane::from_plane_data(plane_data, exec_state);
|
let mut plane = Plane::from_plane_data(plane_data, exec_state);
|
||||||
|
// Though offset planes are derived from standard planes, they are not
|
||||||
|
// standard planes themselves.
|
||||||
|
plane.value = PlaneType::Custom;
|
||||||
|
|
||||||
match std_plane {
|
match std_plane {
|
||||||
StandardPlane::XY => {
|
StandardPlane::XY => {
|
||||||
@ -171,10 +177,44 @@ async fn inner_offset_plane(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(PlaneData::Plane {
|
Ok(plane)
|
||||||
origin: Box::new(plane.origin),
|
}
|
||||||
x_axis: Box::new(plane.x_axis),
|
|
||||||
y_axis: Box::new(plane.y_axis),
|
// Engine-side effectful creation of an actual plane object.
|
||||||
z_axis: Box::new(plane.z_axis),
|
// offset planes are shown by default, and hidden by default if they
|
||||||
})
|
// are used as a sketch plane. That hiding command is sent within inner_start_profile_at
|
||||||
|
async fn make_offset_plane_in_engine(plane: &Plane, exec_state: &mut ExecState, args: &Args) -> Result<(), KclError> {
|
||||||
|
// Create new default planes.
|
||||||
|
let default_size = 100.0;
|
||||||
|
let color = Color {
|
||||||
|
r: 0.6,
|
||||||
|
g: 0.6,
|
||||||
|
b: 0.6,
|
||||||
|
a: 0.3,
|
||||||
|
};
|
||||||
|
|
||||||
|
args.batch_modeling_cmd(
|
||||||
|
plane.id,
|
||||||
|
ModelingCmd::from(mcmd::MakePlane {
|
||||||
|
clobber: false,
|
||||||
|
origin: plane.origin.into(),
|
||||||
|
size: LengthUnit(default_size),
|
||||||
|
x_axis: plane.x_axis.into(),
|
||||||
|
y_axis: plane.y_axis.into(),
|
||||||
|
hide: Some(false),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Set the color.
|
||||||
|
args.batch_modeling_cmd(
|
||||||
|
exec_state.id_generator.next_uuid(),
|
||||||
|
ModelingCmd::from(mcmd::PlaneSetColor {
|
||||||
|
color,
|
||||||
|
plane_id: plane.id,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -894,7 +894,7 @@ pub async fn start_sketch_at(exec_state: &mut ExecState, args: Args) -> Result<K
|
|||||||
async fn inner_start_sketch_at(data: [f64; 2], exec_state: &mut ExecState, args: Args) -> Result<Sketch, KclError> {
|
async fn inner_start_sketch_at(data: [f64; 2], exec_state: &mut ExecState, args: Args) -> Result<Sketch, KclError> {
|
||||||
// Let's assume it's the XY plane for now, this is just for backwards compatibility.
|
// Let's assume it's the XY plane for now, this is just for backwards compatibility.
|
||||||
let xy_plane = PlaneData::XY;
|
let xy_plane = PlaneData::XY;
|
||||||
let sketch_surface = inner_start_sketch_on(SketchData::Plane(xy_plane), None, exec_state, &args).await?;
|
let sketch_surface = inner_start_sketch_on(SketchData::PlaneOrientation(xy_plane), None, exec_state, &args).await?;
|
||||||
let sketch = inner_start_profile_at(data, sketch_surface, None, exec_state, args).await?;
|
let sketch = inner_start_profile_at(data, sketch_surface, None, exec_state, args).await?;
|
||||||
Ok(sketch)
|
Ok(sketch)
|
||||||
}
|
}
|
||||||
@ -905,11 +905,12 @@ async fn inner_start_sketch_at(data: [f64; 2], exec_state: &mut ExecState, args:
|
|||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase", untagged)]
|
#[serde(rename_all = "camelCase", untagged)]
|
||||||
pub enum SketchData {
|
pub enum SketchData {
|
||||||
Plane(PlaneData),
|
PlaneOrientation(PlaneData),
|
||||||
|
Plane(Box<Plane>),
|
||||||
Solid(Box<Solid>),
|
Solid(Box<Solid>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Data for a plane.
|
/// Orientation data that can be used to construct a plane, not a plane in itself.
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
@ -1069,10 +1070,11 @@ async fn inner_start_sketch_on(
|
|||||||
args: &Args,
|
args: &Args,
|
||||||
) -> Result<SketchSurface, KclError> {
|
) -> Result<SketchSurface, KclError> {
|
||||||
match data {
|
match data {
|
||||||
SketchData::Plane(plane_data) => {
|
SketchData::PlaneOrientation(plane_data) => {
|
||||||
let plane = start_sketch_on_plane(plane_data, exec_state, args).await?;
|
let plane = make_sketch_plane_from_orientation(plane_data, exec_state, args).await?;
|
||||||
Ok(SketchSurface::Plane(plane))
|
Ok(SketchSurface::Plane(plane))
|
||||||
}
|
}
|
||||||
|
SketchData::Plane(plane) => Ok(SketchSurface::Plane(plane)),
|
||||||
SketchData::Solid(solid) => {
|
SketchData::Solid(solid) => {
|
||||||
let Some(tag) = tag else {
|
let Some(tag) = tag else {
|
||||||
return Err(KclError::Type(KclErrorDetails {
|
return Err(KclError::Type(KclErrorDetails {
|
||||||
@ -1106,7 +1108,7 @@ async fn start_sketch_on_face(
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start_sketch_on_plane(
|
async fn make_sketch_plane_from_orientation(
|
||||||
data: PlaneData,
|
data: PlaneData,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: &Args,
|
args: &Args,
|
||||||
@ -1122,10 +1124,10 @@ async fn start_sketch_on_plane(
|
|||||||
|
|
||||||
plane.id = match data {
|
plane.id = match data {
|
||||||
PlaneData::XY => default_planes.xy,
|
PlaneData::XY => default_planes.xy,
|
||||||
PlaneData::XZ => default_planes.xz,
|
|
||||||
PlaneData::YZ => default_planes.yz,
|
|
||||||
PlaneData::NegXY => default_planes.neg_xy,
|
PlaneData::NegXY => default_planes.neg_xy,
|
||||||
|
PlaneData::XZ => default_planes.xz,
|
||||||
PlaneData::NegXZ => default_planes.neg_xz,
|
PlaneData::NegXZ => default_planes.neg_xz,
|
||||||
|
PlaneData::YZ => default_planes.yz,
|
||||||
PlaneData::NegYZ => default_planes.neg_yz,
|
PlaneData::NegYZ => default_planes.neg_yz,
|
||||||
PlaneData::Plane {
|
PlaneData::Plane {
|
||||||
origin,
|
origin,
|
||||||
@ -1210,12 +1212,27 @@ pub(crate) async fn inner_start_profile_at(
|
|||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<Sketch, KclError> {
|
) -> Result<Sketch, KclError> {
|
||||||
if let SketchSurface::Face(face) = &sketch_surface {
|
match &sketch_surface {
|
||||||
|
SketchSurface::Face(face) => {
|
||||||
// Flush the batch for our fillets/chamfers if there are any.
|
// Flush the batch for our fillets/chamfers if there are any.
|
||||||
// If we do not do these for sketch on face, things will fail with face does not exist.
|
// If we do not do these for sketch on face, things will fail with face does not exist.
|
||||||
args.flush_batch_for_solid_set(exec_state, face.solid.clone().into())
|
args.flush_batch_for_solid_set(exec_state, face.solid.clone().into())
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
SketchSurface::Plane(plane) if !plane.is_standard() => {
|
||||||
|
// Hide whatever plane we are sketching on.
|
||||||
|
// This is especially helpful for offset planes, which would be visible otherwise.
|
||||||
|
args.batch_end_cmd(
|
||||||
|
exec_state.id_generator.next_uuid(),
|
||||||
|
ModelingCmd::from(mcmd::ObjectVisible {
|
||||||
|
object_id: plane.id,
|
||||||
|
hidden: true,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
// Enter sketch mode on the surface.
|
// Enter sketch mode on the surface.
|
||||||
// We call this here so you can reuse the sketch surface for multiple sketches.
|
// We call this here so you can reuse the sketch surface for multiple sketches.
|
||||||
|
Reference in New Issue
Block a user