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)
|
* [`log2`](#log2)
|
||||||
* [`max`](#max)
|
* [`max`](#max)
|
||||||
* [`min`](#min)
|
* [`min`](#min)
|
||||||
|
* [`patternCircular`](#patternCircular)
|
||||||
* [`patternLinear`](#patternLinear)
|
* [`patternLinear`](#patternLinear)
|
||||||
* [`pi`](#pi)
|
* [`pi`](#pi)
|
||||||
* [`pow`](#pow)
|
* [`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
|
### patternLinear
|
||||||
|
|
||||||
A linear pattern.
|
A linear pattern.
|
||||||
|
@ -73,6 +73,7 @@ lazy_static! {
|
|||||||
Box::new(crate::std::sketch::BezierCurve),
|
Box::new(crate::std::sketch::BezierCurve),
|
||||||
Box::new(crate::std::sketch::Hole),
|
Box::new(crate::std::sketch::Hole),
|
||||||
Box::new(crate::std::patterns::PatternLinear),
|
Box::new(crate::std::patterns::PatternLinear),
|
||||||
|
Box::new(crate::std::patterns::PatternCircular),
|
||||||
Box::new(crate::std::import::Import),
|
Box::new(crate::std::import::Import),
|
||||||
Box::new(crate::std::math::Cos),
|
Box::new(crate::std::math::Cos),
|
||||||
Box::new(crate::std::math::Sin),
|
Box::new(crate::std::math::Sin),
|
||||||
|
@ -27,6 +27,25 @@ pub struct LinearPatternData {
|
|||||||
pub axis: [f64; 3],
|
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.
|
/// A linear pattern.
|
||||||
pub async fn pattern_linear(args: Args) -> Result<MemoryItem, KclError> {
|
pub async fn pattern_linear(args: Args) -> Result<MemoryItem, KclError> {
|
||||||
let (data, geometry): (LinearPatternData, Geometry) = args.get_data_and_geometry()?;
|
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.
|
/// A linear pattern.
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "patternLinear",
|
name = "patternLinear",
|
||||||
@ -99,3 +138,62 @@ async fn inner_pattern_linear(data: LinearPatternData, geometry: Geometry, args:
|
|||||||
|
|
||||||
Ok(geometries)
|
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);
|
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")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn serial_test_import_file_doesnt_exist() {
|
async fn serial_test_import_file_doesnt_exist() {
|
||||||
let code = r#"const model = import("thing.obj")"#;
|
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