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:
1767
docs/kcl/std.json
1767
docs/kcl/std.json
File diff suppressed because it is too large
Load Diff
189
docs/kcl/std.md
189
docs/kcl/std.md
@ -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.
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 |
Reference in New Issue
Block a user