Patterning a pattern should always work (#2680)

* patterning a pattern should alwayus work

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

* add images;

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

* std lib

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

* bu,mp

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

* fix tests

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

* update lock

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

* bump

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

* fixes

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

* fixes

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
Jess Frazelle
2024-06-17 13:35:44 -07:00
committed by GitHub
parent c66f851a3f
commit b54fc534c2
15 changed files with 5970 additions and 2883 deletions

View File

@ -9,7 +9,7 @@ A circular pattern on a 2D sketch.
```js ```js
patternCircular2d(data: CircularPattern2dData, sketch_group: SketchGroup) -> [SketchGroup] patternCircular2d(data: CircularPattern2dData, sketch_group_set: SketchGroupSet) -> [SketchGroup]
``` ```
### Examples ### Examples
@ -48,7 +48,7 @@ const example = extrude(1, exampleSketch)
rotateDuplicates: string, rotateDuplicates: string,
} }
``` ```
* `sketch_group`: `SketchGroup` - A sketch group is a collection of paths. (REQUIRED) * `sketch_group_set`: `SketchGroupSet` - A sketch group or a group of sketch groups. (REQUIRED)
```js ```js
{ {
// The plane id or face id of the sketch group. // The plane id or face id of the sketch group.
@ -129,6 +129,7 @@ const example = extrude(1, exampleSketch)
// The to point. // The to point.
to: [number, number], to: [number, number],
}, },
type: "sketchGroup",
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
// The from point. // The from point.
@ -212,6 +213,9 @@ const example = extrude(1, exampleSketch)
y: number, y: number,
z: number, z: number,
}, },
} |
{
type: "sketchGroups",
} }
``` ```

View File

@ -9,7 +9,7 @@ A circular pattern on a 3D model.
```js ```js
patternCircular3d(data: CircularPattern3dData, extrude_group: ExtrudeGroup) -> [ExtrudeGroup] patternCircular3d(data: CircularPattern3dData, extrude_group_set: ExtrudeGroupSet) -> [ExtrudeGroup]
``` ```
### Examples ### Examples
@ -47,7 +47,7 @@ const example = extrude(-5, exampleSketch)
rotateDuplicates: string, rotateDuplicates: string,
} }
``` ```
* `extrude_group`: `ExtrudeGroup` - An extrude group is a collection of extrude surfaces. (REQUIRED) * `extrude_group_set`: `ExtrudeGroupSet` - A extrude group or a group of extrude groups. (REQUIRED)
```js ```js
{ {
// The id of the extrusion end cap // The id of the extrusion end cap
@ -127,6 +127,7 @@ const example = extrude(-5, exampleSketch)
}], }],
// The id of the extrusion start cap // The id of the extrusion start cap
startCapId: uuid, startCapId: uuid,
type: "extrudeGroup",
// The extrude surfaces. // The extrude surfaces.
value: [{ value: [{
// The face id for the extrude plane. // The face id for the extrude plane.
@ -176,6 +177,9 @@ const example = extrude(-5, exampleSketch)
y: number, y: number,
z: number, z: number,
}, },
} |
{
type: "extrudeGroups",
} }
``` ```

View File

@ -9,7 +9,7 @@ A linear pattern on a 3D model.
```js ```js
patternLinear3d(data: LinearPattern3dData, extrude_group: ExtrudeGroup) -> [ExtrudeGroup] patternLinear3d(data: LinearPattern3dData, extrude_group_set: ExtrudeGroupSet) -> [ExtrudeGroup]
``` ```
### Examples ### Examples
@ -45,7 +45,7 @@ const example = extrude(1, exampleSketch)
repetitions: number, repetitions: number,
} }
``` ```
* `extrude_group`: `ExtrudeGroup` - An extrude group is a collection of extrude surfaces. (REQUIRED) * `extrude_group_set`: `ExtrudeGroupSet` - A extrude group or a group of extrude groups. (REQUIRED)
```js ```js
{ {
// The id of the extrusion end cap // The id of the extrusion end cap
@ -125,6 +125,7 @@ const example = extrude(1, exampleSketch)
}], }],
// The id of the extrusion start cap // The id of the extrusion start cap
startCapId: uuid, startCapId: uuid,
type: "extrudeGroup",
// The extrude surfaces. // The extrude surfaces.
value: [{ value: [{
// The face id for the extrude plane. // The face id for the extrude plane.
@ -174,6 +175,9 @@ const example = extrude(1, exampleSketch)
y: number, y: number,
z: number, z: number,
}, },
} |
{
type: "extrudeGroups",
} }
``` ```

File diff suppressed because it is too large Load Diff

1639
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -20,7 +20,7 @@ kittycad = "0.3.5"
log = "0.4.21" log = "0.4.21"
oauth2 = "4.4.2" oauth2 = "4.4.2"
serde_json = "1.0" serde_json = "1.0"
tauri = { version = "2.0.0-beta.15", features = [ "devtools", "unstable"] } tauri = { version = "2.0.0-beta.22", features = [ "devtools", "unstable"] }
tauri-plugin-cli = { version = "2.0.0-beta.3" } tauri-plugin-cli = { version = "2.0.0-beta.3" }
tauri-plugin-deep-link = { version = "2.0.0-beta.3" } tauri-plugin-deep-link = { version = "2.0.0-beta.3" }
tauri-plugin-dialog = { version = "2.0.0-beta.6" } tauri-plugin-dialog = { version = "2.0.0-beta.6" }

View File

@ -63,11 +63,12 @@
"subcommands": {} "subcommands": {}
}, },
"deep-link": { "deep-link": {
"domains": [ "mobile": [],
{ "desktop": {
"host": "app.zoo.dev" "schemes": [
} "app.zoo.dev"
] ]
}
}, },
"shell": { "shell": {
"open": true "open": true

View File

@ -1369,7 +1369,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-lib" name = "kcl-lib"
version = "0.1.59" version = "0.1.60"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"approx", "approx",

View File

@ -1,7 +1,7 @@
[package] [package]
name = "kcl-lib" name = "kcl-lib"
description = "KittyCAD Language implementation and tools" description = "KittyCAD Language implementation and tools"
version = "0.1.59" version = "0.1.60"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app" repository = "https://github.com/KittyCAD/modeling-app"

View File

@ -63,9 +63,10 @@ impl StdLibFnArg {
pub fn get_autocomplete_snippet(&self, index: usize) -> Result<Option<(usize, String)>> { pub fn get_autocomplete_snippet(&self, index: usize) -> Result<Option<(usize, String)>> {
if self.type_ == "SketchGroup" if self.type_ == "SketchGroup"
|| self.type_ == "ExtrudeGroup"
|| self.type_ == "SketchSurface"
|| self.type_ == "SketchGroupSet" || self.type_ == "SketchGroupSet"
|| self.type_ == "ExtrudeGroup"
|| self.type_ == "ExtrudeGroupSet"
|| self.type_ == "SketchSurface"
{ {
return Ok(Some((index, format!("${{{}:{}}}", index, "%")))); return Ok(Some((index, format!("${{{}:{}}}", index, "%"))));
} }

View File

@ -31,7 +31,8 @@ use crate::{
docs::StdLibFn, docs::StdLibFn,
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
executor::{ executor::{
ExecutorContext, ExtrudeGroup, MemoryItem, Metadata, SketchGroup, SketchGroupSet, SketchSurface, SourceRange, ExecutorContext, ExtrudeGroup, ExtrudeGroupSet, MemoryItem, Metadata, SketchGroup, SketchGroupSet,
SketchSurface, SourceRange,
}, },
std::{kcl_stdlib::KclStdLibFn, sketch::SketchOnFaceTag}, std::{kcl_stdlib::KclStdLibFn, sketch::SketchOnFaceTag},
}; };
@ -773,6 +774,52 @@ impl Args {
Ok((data, sketch_surface, tag)) Ok((data, sketch_surface, tag))
} }
fn get_data_and_extrude_group_set<T: serde::de::DeserializeOwned>(&self) -> Result<(T, ExtrudeGroupSet), KclError> {
let first_value = self
.args
.first()
.ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: format!("Expected a struct as the first argument, found `{:?}`", self.args),
source_ranges: vec![self.source_range],
})
})?
.get_json_value()?;
let data: T = serde_json::from_value(first_value).map_err(|e| {
KclError::Type(KclErrorDetails {
message: format!("Failed to deserialize struct from JSON: {}", e),
source_ranges: vec![self.source_range],
})
})?;
let second_value = self.args.get(1).ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: format!(
"Expected an ExtrudeGroup as the second argument, found `{:?}`",
self.args
),
source_ranges: vec![self.source_range],
})
})?;
let extrude_set = if let MemoryItem::ExtrudeGroup(eg) = second_value {
ExtrudeGroupSet::ExtrudeGroup(eg.clone())
} else if let MemoryItem::ExtrudeGroups { value } = second_value {
ExtrudeGroupSet::ExtrudeGroups(value.clone())
} else {
return Err(KclError::Type(KclErrorDetails {
message: format!(
"Expected a ExtrudeGroup or Vector of ExtrudeGroups as the second argument, found `{:?}`",
self.args
),
source_ranges: vec![self.source_range],
}));
};
Ok((data, extrude_set))
}
fn get_data_and_extrude_group<T: serde::de::DeserializeOwned>(&self) -> Result<(T, Box<ExtrudeGroup>), KclError> { fn get_data_and_extrude_group<T: serde::de::DeserializeOwned>(&self) -> Result<(T, Box<ExtrudeGroup>), KclError> {
let first_value = self let first_value = self
.args .args

View File

@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
executor::{ExtrudeGroup, Geometries, Geometry, MemoryItem, SketchGroup, SketchGroupSet}, executor::{ExtrudeGroup, ExtrudeGroupSet, Geometries, Geometry, MemoryItem, SketchGroup, SketchGroupSet},
std::{types::Uint, Args}, std::{types::Uint, Args},
}; };
@ -141,7 +141,7 @@ async fn inner_pattern_linear_2d(
/// A linear pattern on a 3D model. /// A linear pattern on a 3D model.
pub async fn pattern_linear_3d(args: Args) -> Result<MemoryItem, KclError> { pub async fn pattern_linear_3d(args: Args) -> Result<MemoryItem, KclError> {
let (data, extrude_group): (LinearPattern3dData, Box<ExtrudeGroup>) = args.get_data_and_extrude_group()?; let (data, extrude_group_set): (LinearPattern3dData, ExtrudeGroupSet) = args.get_data_and_extrude_group_set()?;
if data.axis == [0.0, 0.0, 0.0] { if data.axis == [0.0, 0.0, 0.0] {
return Err(KclError::Semantic(KclErrorDetails { return Err(KclError::Semantic(KclErrorDetails {
@ -152,7 +152,7 @@ pub async fn pattern_linear_3d(args: Args) -> Result<MemoryItem, KclError> {
})); }));
} }
let extrude_groups = inner_pattern_linear_3d(data, extrude_group, args).await?; let extrude_groups = inner_pattern_linear_3d(data, extrude_group_set, args).await?;
Ok(MemoryItem::ExtrudeGroups { value: extrude_groups }) Ok(MemoryItem::ExtrudeGroups { value: extrude_groups })
} }
@ -178,27 +178,37 @@ pub async fn pattern_linear_3d(args: Args) -> Result<MemoryItem, KclError> {
}] }]
async fn inner_pattern_linear_3d( async fn inner_pattern_linear_3d(
data: LinearPattern3dData, data: LinearPattern3dData,
extrude_group: Box<ExtrudeGroup>, extrude_group_set: ExtrudeGroupSet,
args: Args, args: Args,
) -> Result<Vec<Box<ExtrudeGroup>>, KclError> { ) -> Result<Vec<Box<ExtrudeGroup>>, KclError> {
let starting_extrude_groups = match extrude_group_set {
ExtrudeGroupSet::ExtrudeGroup(extrude_group) => vec![extrude_group],
ExtrudeGroupSet::ExtrudeGroups(extrude_groups) => extrude_groups,
};
if args.ctx.is_mock { if args.ctx.is_mock {
return Ok(vec![extrude_group.clone()]); return Ok(starting_extrude_groups);
} }
let mut extrude_groups = Vec::new();
for extrude_group in starting_extrude_groups.iter() {
let geometries = pattern_linear( let geometries = pattern_linear(
LinearPattern::ThreeD(data), LinearPattern::ThreeD(data.clone()),
Geometry::ExtrudeGroup(extrude_group), Geometry::ExtrudeGroup(extrude_group.clone()),
args.clone(), args.clone(),
) )
.await?; .await?;
let Geometries::ExtrudeGroups(extrude_groups) = geometries else { let Geometries::ExtrudeGroups(new_extrude_groups) = geometries else {
return Err(KclError::Semantic(KclErrorDetails { return Err(KclError::Semantic(KclErrorDetails {
message: "Expected a vec of extrude groups".to_string(), message: "Expected a vec of extrude groups".to_string(),
source_ranges: vec![args.source_range], source_ranges: vec![args.source_range],
})); }));
}; };
extrude_groups.extend(new_extrude_groups);
}
Ok(extrude_groups) Ok(extrude_groups)
} }
@ -335,9 +345,9 @@ impl CircularPattern {
/// A circular pattern on a 2D sketch. /// A circular pattern on a 2D sketch.
pub async fn pattern_circular_2d(args: Args) -> Result<MemoryItem, KclError> { pub async fn pattern_circular_2d(args: Args) -> Result<MemoryItem, KclError> {
let (data, sketch_group): (CircularPattern2dData, Box<SketchGroup>) = args.get_data_and_sketch_group()?; let (data, sketch_group_set): (CircularPattern2dData, SketchGroupSet) = args.get_data_and_sketch_group_set()?;
let sketch_groups = inner_pattern_circular_2d(data, sketch_group, args).await?; let sketch_groups = inner_pattern_circular_2d(data, sketch_group_set, args).await?;
Ok(MemoryItem::SketchGroups { value: sketch_groups }) Ok(MemoryItem::SketchGroups { value: sketch_groups })
} }
@ -364,35 +374,45 @@ pub async fn pattern_circular_2d(args: Args) -> Result<MemoryItem, KclError> {
}] }]
async fn inner_pattern_circular_2d( async fn inner_pattern_circular_2d(
data: CircularPattern2dData, data: CircularPattern2dData,
sketch_group: Box<SketchGroup>, sketch_group_set: SketchGroupSet,
args: Args, args: Args,
) -> Result<Vec<Box<SketchGroup>>, KclError> { ) -> Result<Vec<Box<SketchGroup>>, KclError> {
let starting_sketch_groups = match sketch_group_set {
SketchGroupSet::SketchGroup(sketch_group) => vec![sketch_group],
SketchGroupSet::SketchGroups(sketch_groups) => sketch_groups,
};
if args.ctx.is_mock { if args.ctx.is_mock {
return Ok(vec![sketch_group]); return Ok(starting_sketch_groups);
} }
let mut sketch_groups = Vec::new();
for sketch_group in starting_sketch_groups.iter() {
let geometries = pattern_circular( let geometries = pattern_circular(
CircularPattern::TwoD(data), CircularPattern::TwoD(data.clone()),
Geometry::SketchGroup(sketch_group), Geometry::SketchGroup(sketch_group.clone()),
args.clone(), args.clone(),
) )
.await?; .await?;
let Geometries::SketchGroups(sketch_groups) = geometries else { let Geometries::SketchGroups(new_sketch_groups) = geometries else {
return Err(KclError::Semantic(KclErrorDetails { return Err(KclError::Semantic(KclErrorDetails {
message: "Expected a vec of sketch groups".to_string(), message: "Expected a vec of sketch groups".to_string(),
source_ranges: vec![args.source_range], source_ranges: vec![args.source_range],
})); }));
}; };
sketch_groups.extend(new_sketch_groups);
}
Ok(sketch_groups) Ok(sketch_groups)
} }
/// A circular pattern on a 3D model. /// A circular pattern on a 3D model.
pub async fn pattern_circular_3d(args: Args) -> Result<MemoryItem, KclError> { pub async fn pattern_circular_3d(args: Args) -> Result<MemoryItem, KclError> {
let (data, extrude_group): (CircularPattern3dData, Box<ExtrudeGroup>) = args.get_data_and_extrude_group()?; let (data, extrude_group_set): (CircularPattern3dData, ExtrudeGroupSet) = args.get_data_and_extrude_group_set()?;
let extrude_groups = inner_pattern_circular_3d(data, extrude_group, args).await?; let extrude_groups = inner_pattern_circular_3d(data, extrude_group_set, args).await?;
Ok(MemoryItem::ExtrudeGroups { value: extrude_groups }) Ok(MemoryItem::ExtrudeGroups { value: extrude_groups })
} }
@ -416,27 +436,37 @@ pub async fn pattern_circular_3d(args: Args) -> Result<MemoryItem, KclError> {
}] }]
async fn inner_pattern_circular_3d( async fn inner_pattern_circular_3d(
data: CircularPattern3dData, data: CircularPattern3dData,
extrude_group: Box<ExtrudeGroup>, extrude_group_set: ExtrudeGroupSet,
args: Args, args: Args,
) -> Result<Vec<Box<ExtrudeGroup>>, KclError> { ) -> Result<Vec<Box<ExtrudeGroup>>, KclError> {
let starting_extrude_groups = match extrude_group_set {
ExtrudeGroupSet::ExtrudeGroup(extrude_group) => vec![extrude_group],
ExtrudeGroupSet::ExtrudeGroups(extrude_groups) => extrude_groups,
};
if args.ctx.is_mock { if args.ctx.is_mock {
return Ok(vec![extrude_group]); return Ok(starting_extrude_groups);
} }
let mut extrude_groups = Vec::new();
for extrude_group in starting_extrude_groups.iter() {
let geometries = pattern_circular( let geometries = pattern_circular(
CircularPattern::ThreeD(data), CircularPattern::ThreeD(data.clone()),
Geometry::ExtrudeGroup(extrude_group), Geometry::ExtrudeGroup(extrude_group.clone()),
args.clone(), args.clone(),
) )
.await?; .await?;
let Geometries::ExtrudeGroups(extrude_groups) = geometries else { let Geometries::ExtrudeGroups(new_extrude_groups) = geometries else {
return Err(KclError::Semantic(KclErrorDetails { return Err(KclError::Semantic(KclErrorDetails {
message: "Expected a vec of extrude groups".to_string(), message: "Expected a vec of extrude groups".to_string(),
source_ranges: vec![args.source_range], source_ranges: vec![args.source_range],
})); }));
}; };
extrude_groups.extend(new_extrude_groups);
}
Ok(extrude_groups) Ok(extrude_groups)
} }

View File

@ -1959,3 +1959,53 @@ async fn serial_test_neg_xz_plane() {
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap(); let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/neg_xz_plane.png", &result, 1.0); twenty_twenty::assert_image("tests/executor/outputs/neg_xz_plane.png", &result, 1.0);
} }
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_linear_pattern3d_a_pattern() {
let code = r#"const exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> line([0, 2], %)
|> line([3, 1], %)
|> line([0, -4], %)
|> close(%)
|> extrude(1, %)
const pattn1 = patternLinear3d({
axis: [1, 0, 0],
repetitions: 6,
distance: 6
}, exampleSketch)
const pattn2 = patternLinear3d({
axis: [0, 0, 1],
distance: 1,
repetitions: 6
}, pattn1)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/linear_pattern3d_a_pattern.png", &result, 1.0);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_circular_pattern3d_a_pattern() {
let code = r#"const exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> line([0, 2], %)
|> line([3, 1], %)
|> line([0, -4], %)
|> close(%)
|> extrude(1, %)
const pattn1 = patternLinear3d({
axis: [1, 0, 0],
repetitions: 6,
distance: 6
}, exampleSketch)
const pattn2 = patternCircular3d({axis: [0,0, 1], center: [-20, -20, -20], repetitions: 40, arcDegrees: 360, rotateDuplicates: false}, pattn1)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/circular_pattern3d_a_pattern.png", &result, 1.0);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB