Pull Circular patterns through to App (#1405)

* debugging steps

* add testing

* Update src/wasm-lib/tests/executor/main.rs

* generate docs and fmt

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: gserena <serena@zoo.dev>
Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
Co-authored-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
Serena Gandhi
2024-02-13 13:20:49 -06:00
committed by GitHub
parent 00ede7ec1a
commit c699611f5b
8 changed files with 2137 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -41,6 +41,7 @@
* [`log2`](#log2)
* [`max`](#max)
* [`min`](#min)
* [`patternCircular`](#patternCircular)
* [`patternLinear`](#patternLinear)
* [`pi`](#pi)
* [`pow`](#pow)
@ -3998,6 +3999,194 @@ min(args: [number]) -> number
### patternCircular
A Circular pattern.
```
patternCircular(data: CircularPatternData, geometry: Geometry) -> Geometries
```
#### Arguments
* `data`: `CircularPatternData` - Data for a circular pattern.
```
{
// The arc angle (in degrees) to place the repetitions. Must be greater than 0.
arcDegrees: number,
// The axis around which to make the pattern. This is a 3D vector.
axis: [number, number, number],
// The center about which to make th pattern. This is a 3D vector.
center: [number, number, number],
// The number of repetitions. Must be greater than 0. This excludes the original entity. For example, if `repetitions` is 1, the original entity will be copied once.
repetitions: number,
// Whether or not to rotate the duplicates as they are copied.
rotateDuplicates: string,
}
```
* `geometry`: `Geometry` - A geometry.
```
{
// The plane id or face id of the sketch group.
entityId: uuid,
// The id of the sketch group.
id: uuid,
// The position of the sketch group.
position: [number, number, number],
// The rotation of the sketch group base plane.
rotation: [number, number, number, number],
// The starting path.
start: {
// The from point.
from: [number, number],
// The name of the path.
name: string,
// The to point.
to: [number, number],
},
type: "SketchGroup",
// The paths in the sketch group.
value: [{
// The from point.
from: [number, number],
// The name of the path.
name: string,
// The to point.
to: [number, number],
type: "ToPoint",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The name of the path.
name: string,
// The to point.
to: [number, number],
type: "TangentialArcTo",
} |
{
// The from point.
from: [number, number],
// The name of the path.
name: string,
// The to point.
to: [number, number],
type: "Horizontal",
// The x coordinate.
x: number,
} |
{
// The from point.
from: [number, number],
// The name of the path.
name: string,
// The to point.
to: [number, number],
type: "AngledLineTo",
// The x coordinate.
x: number,
// The y coordinate.
y: number,
} |
{
// The from point.
from: [number, number],
// The name of the path.
name: string,
// The to point.
to: [number, number],
type: "Base",
}],
// The x-axis of the sketch group base plane in the 3D space
xAxis: {
x: number,
y: number,
z: number,
},
// The y-axis of the sketch group base plane in the 3D space
yAxis: {
x: number,
y: number,
z: number,
},
// The z-axis of the sketch group base plane in the 3D space
zAxis: {
x: number,
y: number,
z: number,
},
} |
{
// The id of the extrusion end cap
endCapId: uuid,
// The height of the extrude group.
height: number,
// The id of the extrude group.
id: uuid,
// The position of the extrude group.
position: [number, number, number],
// The rotation of the extrude group.
rotation: [number, number, number, number],
// The id of the extrusion start cap
startCapId: uuid,
type: "ExtrudeGroup",
// The extrude surfaces.
value: [{
// The face id for the extrude plane.
faceId: uuid,
// The id of the geometry.
id: uuid,
// The name.
name: string,
// The position.
position: [number, number, number],
// The rotation.
rotation: [number, number, number, number],
// The source range.
sourceRange: [number, number],
type: "extrudePlane",
}],
// The x-axis of the extrude group base plane in the 3D space
xAxis: {
x: number,
y: number,
z: number,
},
// The y-axis of the extrude group base plane in the 3D space
yAxis: {
x: number,
y: number,
z: number,
},
// The z-axis of the extrude group base plane in the 3D space
zAxis: {
x: number,
y: number,
z: number,
},
}
```
#### Returns
* `Geometries` - A set of geometry.
```
{
type: "SketchGroups",
} |
{
type: "ExtrudeGroups",
}
```
### patternLinear
A linear pattern.

View File

@ -73,6 +73,7 @@ lazy_static! {
Box::new(crate::std::sketch::BezierCurve),
Box::new(crate::std::sketch::Hole),
Box::new(crate::std::patterns::PatternLinear),
Box::new(crate::std::patterns::PatternCircular),
Box::new(crate::std::import::Import),
Box::new(crate::std::math::Cos),
Box::new(crate::std::math::Sin),

View File

@ -27,6 +27,25 @@ pub struct LinearPatternData {
pub axis: [f64; 3],
}
/// Data for a circular pattern.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(rename_all = "camelCase")]
pub struct CircularPatternData {
/// The number of repetitions. Must be greater than 0.
/// This excludes the original entity. For example, if `repetitions` is 1,
/// the original entity will be copied once.
pub repetitions: usize,
/// The axis around which to make the pattern. This is a 3D vector.
pub axis: [f64; 3],
/// The center about which to make th pattern. This is a 3D vector.
pub center: [f64; 3],
/// The arc angle (in degrees) to place the repetitions. Must be greater than 0.
pub arc_degrees: f64,
/// Whether or not to rotate the duplicates as they are copied.
pub rotate_duplicates: bool,
}
/// A linear pattern.
pub async fn pattern_linear(args: Args) -> Result<MemoryItem, KclError> {
let (data, geometry): (LinearPatternData, Geometry) = args.get_data_and_geometry()?;
@ -47,6 +66,26 @@ pub async fn pattern_linear(args: Args) -> Result<MemoryItem, KclError> {
}
}
/// A circular pattern.
pub async fn pattern_circular(args: Args) -> Result<MemoryItem, KclError> {
let (data, geometry): (CircularPatternData, Geometry) = args.get_data_and_geometry()?;
if data.axis == [0.0, 0.0, 0.0] {
return Err(KclError::Semantic(KclErrorDetails {
message:
"The axis of the circular pattern cannot be the zero vector. Otherwise they will just duplicate in place."
.to_string(),
source_ranges: vec![args.source_range],
}));
}
let new_geometries = inner_pattern_circular(data, geometry, args).await?;
match new_geometries {
Geometries::SketchGroups(sketch_groups) => Ok(MemoryItem::SketchGroups { value: sketch_groups }),
Geometries::ExtrudeGroups(extrude_groups) => Ok(MemoryItem::ExtrudeGroups { value: extrude_groups }),
}
}
/// A linear pattern.
#[stdlib {
name = "patternLinear",
@ -99,3 +138,62 @@ async fn inner_pattern_linear(data: LinearPatternData, geometry: Geometry, args:
Ok(geometries)
}
/// A Circular pattern.
#[stdlib {
name = "patternCircular",
}]
async fn inner_pattern_circular(
data: CircularPatternData,
geometry: Geometry,
args: Args,
) -> Result<Geometries, KclError> {
let id = uuid::Uuid::new_v4();
let resp = args
.send_modeling_cmd(
id,
ModelingCmd::EntityCircularPattern {
axis: data.axis.into(),
entity_id: geometry.id(),
center: data.center.into(),
num_repetitions: data.repetitions as u32,
arc_degrees: data.arc_degrees,
rotate_duplicates: data.rotate_duplicates,
},
)
.await?;
let kittycad::types::OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::EntityCircularPattern { data: pattern_info },
} = &resp
else {
return Err(KclError::Engine(KclErrorDetails {
message: format!("EntityCircularPattern response was not as expected: {:?}", resp),
source_ranges: vec![args.source_range],
}));
};
let geometries = match geometry {
Geometry::SketchGroup(sketch_group) => {
let mut geometries = vec![sketch_group.clone()];
for id in pattern_info.entity_ids.iter() {
let mut new_sketch_group = sketch_group.clone();
new_sketch_group.id = *id;
geometries.push(new_sketch_group);
}
Geometries::SketchGroups(geometries)
}
Geometry::ExtrudeGroup(extrude_group) => {
let mut geometries = vec![extrude_group.clone()];
for id in pattern_info.entity_ids.iter() {
let mut new_extrude_group = extrude_group.clone();
new_extrude_group.id = *id;
geometries.push(new_extrude_group);
}
Geometries::ExtrudeGroups(geometries)
}
};
Ok(geometries)
}

View File

@ -768,6 +768,88 @@ const rectangle = startSketchOn('XY')
twenty_twenty::assert_image("tests/executor/outputs/patterns_linear_basic_holes.png", &result, 0.999);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_patterns_circular_basic_2d() {
let code = r#"fn circle = (pos, radius) => {
const sg = startSketchOn('XY')
|> startProfileAt([pos[0] + radius, pos[1]], %)
|> arc({
angle_end: 360,
angle_start: 0,
radius: radius
}, %)
|> close(%)
return sg
}
const part = circle([0,0], 2)
|> patternCircular({axis: [0,0,1], center: [20, 20, 20], repetitions: 12, arcDegrees: 210, rotateDuplicates: true}, %)
"#;
let result = execute_and_snapshot(code).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/patterns_circular_basic_2d.png", &result, 0.999);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_patterns_circular_basic_3d() {
let code = r#"fn circle = (pos, radius) => {
const sg = startSketchOn('XY')
|> startProfileAt([pos[0] + radius, pos[1]], %)
|> arc({
angle_end: 360,
angle_start: 0,
radius: radius
}, %)
|> close(%)
return sg
}
const part = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0,1], %)
|> line([1, 0], %)
|> line([0, -1], %)
|> close(%)
|> extrude(1, %)
|> patternCircular({axis: [0,1,0], center: [-20, -20, -20], repetitions: 40, arcDegrees: 360, rotateDuplicates: false}, %)
"#;
let result = execute_and_snapshot(code).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/patterns_circular_basic_3d.png", &result, 0.999);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_patterns_circular_3d_tilted_axis() {
let code = r#"fn circle = (pos, radius) => {
const sg = startSketchOn('XY')
|> startProfileAt([pos[0] + radius, pos[1]], %)
|> arc({
angle_end: 360,
angle_start: 0,
radius: radius
}, %)
|> close(%)
return sg
}
const part = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0,1], %)
|> line([1, 0], %)
|> line([0, -1], %)
|> close(%)
|> extrude(1, %)
|> patternCircular({axis: [1,1,-1], center: [10, 0, 10], repetitions: 10, arcDegrees: 360, rotateDuplicates: true}, %)
"#;
let result = execute_and_snapshot(code).await.unwrap();
twenty_twenty::assert_image(
"tests/executor/outputs/patterns_circular_3d_tilted_axis.png",
&result,
0.999,
);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_import_file_doesnt_exist() {
let code = r#"const model = import("thing.obj")"#;

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB