Import geometry work w transforms (#5757)

* make work with imported geometry

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* iupdates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* update known issues

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Jess Frazelle
2025-03-11 18:23:21 -07:00
committed by GitHub
parent e5c05e1980
commit f31c2c6f81
24 changed files with 5732521 additions and 453 deletions

View File

@ -13,9 +13,7 @@ once fixed in engine will just start working here with no language changes.
If you see a red line around your model, it means this is happening.
- **Import**: Right now you can import a file, even if that file has brep data
you cannot edit it, after v1, the engine will account for this. You also cannot
currently move or transform the imported objects at all, once we have assemblies
this will work.
you cannot edit it, after v1, the engine will account for this.
- **Fillets**: Fillets cannot intersect, you will get an error. Only simple fillet
cases work currently.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,62 @@
---
title: "SolidOrImportedGeometry"
excerpt: "Data for a solid or an imported geometry."
layout: manual
---
Data for a solid or an imported geometry.
**This schema accepts exactly one of the following:**
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `type` |enum: `solid`| | No |
| `id` |[`string`](/docs/kcl/types/string)| The id of the solid. | No |
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID of the solid. Unlike `id`, this doesn't change. | No |
| `value` |`[` [`ExtrudeSurface`](/docs/kcl/types/ExtrudeSurface) `]`| The extrude surfaces. | No |
| `sketch` |[`Sketch`](/docs/kcl/types/Sketch)| The sketch. | No |
| `height` |[`number`](/docs/kcl/types/number)| The height of the solid. | No |
| `startCapId` |[`string`](/docs/kcl/types/string)| The id of the extrusion start cap | No |
| `endCapId` |[`string`](/docs/kcl/types/string)| The id of the extrusion end cap | No |
| `edgeCuts` |`[` [`EdgeCut`](/docs/kcl/types/EdgeCut) `]`| Chamfers or fillets on this solid. | No |
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| Metadata. | No |
----
Data for an imported geometry.
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `type` |enum: `importedGeometry`| | No |
| `id` |[`string`](/docs/kcl/types/string)| The ID of the imported geometry. | No |
| `value` |`[` [`string`](/docs/kcl/types/string) `]`| The original file paths. | No |
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
----

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

View File

@ -133,6 +133,7 @@ impl StdLibFnArg {
|| self.type_ == "SolidSet"
|| self.type_ == "SketchSurface"
|| self.type_ == "SketchOrSurface"
|| self.type_ == "SolidOrImportedGeometry"
{
return Ok(Some((index, format!("{label}${{{}:{}}}", index, "%"))));
} else if (self.type_ == "TagDeclarator" || self.type_ == "TagNode") && self.required {

View File

@ -223,6 +223,33 @@ pub struct ImportedGeometry {
pub meta: Vec<Metadata>,
}
/// Data for a solid or an imported geometry.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum SolidOrImportedGeometry {
Solid(Box<Solid>),
ImportedGeometry(Box<ImportedGeometry>),
}
impl SolidOrImportedGeometry {
pub fn id(&self) -> uuid::Uuid {
match self {
SolidOrImportedGeometry::Solid(s) => s.id,
SolidOrImportedGeometry::ImportedGeometry(s) => s.id,
}
}
}
impl From<SolidOrImportedGeometry> for crate::execution::KclValue {
fn from(value: SolidOrImportedGeometry) -> Self {
match value {
SolidOrImportedGeometry::Solid(s) => crate::execution::KclValue::Solid { value: s },
SolidOrImportedGeometry::ImportedGeometry(s) => crate::execution::KclValue::ImportedGeometry(*s),
}
}
}
/// A helix.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]

View File

@ -77,7 +77,7 @@ fn read<P>(filename: &str, dir: P) -> String
where
P: AsRef<Path>,
{
std::fs::read_to_string(dir.as_ref().join(filename)).unwrap()
std::fs::read_to_string(dir.as_ref().join(filename)).expect("Failed to read file: {filename}")
}
fn parse(test_name: &str) {
@ -2136,3 +2136,25 @@ mod flush_batch_on_end {
super::execute(TEST_NAME, true).await
}
}
mod import_transform {
const TEST_NAME: &str = "import_transform";
/// Test parsing KCL.
#[test]
fn parse() {
super::parse(TEST_NAME);
}
/// Test that parsing and unparsing KCL produces the original KCL input.
#[test]
fn unparse() {
super::unparse(TEST_NAME)
}
/// Test that KCL is executed correctly.
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_execute() {
super::execute(TEST_NAME, true).await
}
}

View File

@ -1287,6 +1287,16 @@ impl<'a> FromKclValue<'a> for crate::execution::Solid {
}
}
impl<'a> FromKclValue<'a> for crate::execution::SolidOrImportedGeometry {
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
match arg {
KclValue::Solid { value } => Some(Self::Solid(value.clone())),
KclValue::ImportedGeometry(value) => Some(Self::ImportedGeometry(Box::new(value.clone()))),
_ => None,
}
}
}
impl<'a> FromKclValue<'a> for super::sketch::SketchData {
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
// Order is critical since PlaneData is a subset of Plane.

View File

@ -13,7 +13,7 @@ use kittycad_modeling_cmds as kcmc;
use crate::{
errors::{KclError, KclErrorDetails},
execution::{ExecState, KclValue, Solid},
execution::{ExecState, KclValue, SolidOrImportedGeometry},
std::Args,
};
@ -24,7 +24,7 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
let global = args.get_kw_arg_opt("global")?;
let solid = inner_scale(solid, scale, global, exec_state, args).await?;
Ok(KclValue::Solid { value: solid })
Ok(solid.into())
}
/// Scale a solid.
@ -73,6 +73,17 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
/// scale = [1.0, 1.0, 2.5],
/// )
/// ```
///
/// ```no_run
/// // Scale an imported model.
///
/// import "tests/inputs/cube.sldprt" as cube
///
/// cube
/// |> scale(
/// scale = [1.0, 1.0, 2.5],
/// )
/// ```
#[stdlib {
name = "scale",
feature_tree_operation = false,
@ -85,18 +96,18 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
}
}]
async fn inner_scale(
solid: Box<Solid>,
solid: SolidOrImportedGeometry,
scale: [f64; 3],
global: Option<bool>,
exec_state: &mut ExecState,
args: Args,
) -> Result<Box<Solid>, KclError> {
) -> Result<SolidOrImportedGeometry, KclError> {
let id = exec_state.next_uuid();
args.batch_modeling_cmd(
id,
ModelingCmd::from(mcmd::SetObjectTransform {
object_id: solid.id,
object_id: solid.id(),
transforms: vec![shared::ComponentTransform {
scale: Some(shared::TransformBy::<Point3d<f64>> {
property: Point3d {
@ -125,7 +136,7 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
let global = args.get_kw_arg_opt("global")?;
let solid = inner_translate(solid, translate, global, exec_state, args).await?;
Ok(KclValue::Solid { value: solid })
Ok(solid.into())
}
/// Move a solid.
@ -166,6 +177,17 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
/// translate = [1.0, 1.0, 2.5],
/// )
/// ```
///
/// ```no_run
/// // Move an imported model.
///
/// import "tests/inputs/cube.sldprt" as cube
///
/// cube
/// |> translate(
/// translate = [1.0, 1.0, 2.5],
/// )
/// ```
#[stdlib {
name = "translate",
feature_tree_operation = false,
@ -178,18 +200,18 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
}
}]
async fn inner_translate(
solid: Box<Solid>,
solid: SolidOrImportedGeometry,
translate: [f64; 3],
global: Option<bool>,
exec_state: &mut ExecState,
args: Args,
) -> Result<Box<Solid>, KclError> {
) -> Result<SolidOrImportedGeometry, KclError> {
let id = exec_state.next_uuid();
args.batch_modeling_cmd(
id,
ModelingCmd::from(mcmd::SetObjectTransform {
object_id: solid.id,
object_id: solid.id(),
transforms: vec![shared::ComponentTransform {
translate: Some(shared::TransformBy::<Point3d<LengthUnit>> {
property: shared::Point3d {
@ -322,7 +344,7 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
}
let solid = inner_rotate(solid, roll, pitch, yaw, axis, angle, global, exec_state, args).await?;
Ok(KclValue::Solid { value: solid })
Ok(solid.into())
}
/// Rotate a solid.
@ -425,6 +447,18 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
/// angle = 90,
/// )
/// ```
///
/// ```no_run
/// // Rotate an imported model.
///
/// import "tests/inputs/cube.sldprt" as cube
///
/// cube
/// |> rotate(
/// axis = [0, 0, 1.0],
/// angle = 90,
/// )
/// ```
#[stdlib {
name = "rotate",
feature_tree_operation = false,
@ -442,7 +476,7 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
}]
#[allow(clippy::too_many_arguments)]
async fn inner_rotate(
solid: Box<Solid>,
solid: SolidOrImportedGeometry,
roll: Option<f64>,
pitch: Option<f64>,
yaw: Option<f64>,
@ -451,14 +485,14 @@ async fn inner_rotate(
global: Option<bool>,
exec_state: &mut ExecState,
args: Args,
) -> Result<Box<Solid>, KclError> {
) -> Result<SolidOrImportedGeometry, KclError> {
let id = exec_state.next_uuid();
if let (Some(roll), Some(pitch), Some(yaw)) = (roll, pitch, yaw) {
args.batch_modeling_cmd(
id,
ModelingCmd::from(mcmd::SetObjectTransform {
object_id: solid.id,
object_id: solid.id(),
transforms: vec![shared::ComponentTransform {
rotate_rpy: Some(shared::TransformBy::<Point3d<f64>> {
property: shared::Point3d {
@ -482,7 +516,7 @@ async fn inner_rotate(
args.batch_modeling_cmd(
id,
ModelingCmd::from(mcmd::SetObjectTransform {
object_id: solid.id,
object_id: solid.id(),
transforms: vec![shared::ComponentTransform {
rotate_angle_axis: Some(shared::TransformBy::<Point4d<f64>> {
property: shared::Point4d {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Artifact graph flowchart import_transform.kcl
extension: md
snapshot_kind: binary
---

View File

@ -0,0 +1,3 @@
```mermaid
flowchart LR
```

View File

@ -0,0 +1,287 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of parsing import_transform.kcl
---
{
"Ok": {
"body": [
{
"end": 43,
"path": {
"type": "Foreign",
"path": "2-5-long-m8-chc-screw.stl"
},
"selector": {
"type": "None",
"alias": {
"end": 43,
"name": "screw",
"start": 38,
"type": "Identifier"
}
},
"start": 0,
"type": "ImportStatement",
"type": "ImportStatement"
},
{
"end": 232,
"expression": {
"body": [
{
"end": 50,
"name": "screw",
"start": 45,
"type": "Identifier",
"type": "Identifier"
},
{
"arguments": [
{
"type": "LabeledArg",
"label": {
"end": 85,
"name": "roll",
"start": 81,
"type": "Identifier"
},
"arg": {
"end": 92,
"raw": "3.14",
"start": 88,
"type": "Literal",
"type": "Literal",
"value": {
"value": 3.14,
"suffix": "None"
}
}
},
{
"type": "LabeledArg",
"label": {
"end": 106,
"name": "pitch",
"start": 101,
"type": "Identifier"
},
"arg": {
"end": 113,
"raw": "3.14",
"start": 109,
"type": "Literal",
"type": "Literal",
"value": {
"value": 3.14,
"suffix": "None"
}
}
},
{
"type": "LabeledArg",
"label": {
"end": 125,
"name": "yaw",
"start": 122,
"type": "Identifier"
},
"arg": {
"end": 132,
"raw": "3.14",
"start": 128,
"type": "Literal",
"type": "Literal",
"value": {
"value": 3.14,
"suffix": "None"
}
}
}
],
"callee": {
"end": 62,
"name": "rotate",
"start": 56,
"type": "Identifier"
},
"end": 140,
"start": 56,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"end": 72,
"start": 71,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
},
{
"arguments": [
{
"type": "LabeledArg",
"label": {
"end": 168,
"name": "translate",
"start": 159,
"type": "Identifier"
},
"arg": {
"elements": [
{
"end": 176,
"raw": "3.14",
"start": 172,
"type": "Literal",
"type": "Literal",
"value": {
"value": 3.14,
"suffix": "None"
}
},
{
"end": 182,
"raw": "3.14",
"start": 178,
"type": "Literal",
"type": "Literal",
"value": {
"value": 3.14,
"suffix": "None"
}
},
{
"end": 188,
"raw": "3.14",
"start": 184,
"type": "Literal",
"type": "Literal",
"value": {
"value": 3.14,
"suffix": "None"
}
}
],
"end": 189,
"start": 171,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
}
],
"callee": {
"end": 155,
"name": "translate",
"start": 146,
"type": "Identifier"
},
"end": 190,
"start": 146,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"end": 157,
"start": 156,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
},
{
"arguments": [
{
"type": "LabeledArg",
"label": {
"end": 210,
"name": "scale",
"start": 205,
"type": "Identifier"
},
"arg": {
"elements": [
{
"end": 218,
"raw": "3.14",
"start": 214,
"type": "Literal",
"type": "Literal",
"value": {
"value": 3.14,
"suffix": "None"
}
},
{
"end": 224,
"raw": "3.14",
"start": 220,
"type": "Literal",
"type": "Literal",
"value": {
"value": 3.14,
"suffix": "None"
}
},
{
"end": 230,
"raw": "3.14",
"start": 226,
"type": "Literal",
"type": "Literal",
"value": {
"value": 3.14,
"suffix": "None"
}
}
],
"end": 231,
"start": 213,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
}
],
"callee": {
"end": 201,
"name": "scale",
"start": 196,
"type": "Identifier"
},
"end": 232,
"start": 196,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"end": 203,
"start": 202,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
}
],
"end": 232,
"start": 45,
"type": "PipeExpression",
"type": "PipeExpression"
},
"start": 45,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
}
],
"end": 233,
"nonCodeMeta": {
"nonCodeNodes": {
"0": [
{
"end": 45,
"start": 43,
"type": "NonCodeNode",
"value": {
"type": "newLine"
}
}
]
},
"startNodes": []
},
"start": 0
}
}

View File

@ -0,0 +1,11 @@
import "2-5-long-m8-chc-screw.stl" as screw
screw
|> rotate(
%,
roll = 3.14,
pitch = 3.14,
yaw = 3.14,
)
|> translate(%, translate = [3.14, 3.14, 3.14])
|> scale(%, scale = [3.14, 3.14, 3.14])

View File

@ -0,0 +1,5 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Operations executed import_transform.kcl
---
[]

View File

@ -0,0 +1,19 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Variables in memory after executing import_transform.kcl
---
{
"screw": {
"type": "Module",
"value": 3,
"__meta": [
{
"sourceRange": [
0,
43,
0
]
}
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB