start of revolve (#1897)

* start of revolve

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

* revolve

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

* more tests

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

* updates

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

* updates

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

* tagged edge

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

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* bump lib

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

* custom axis

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

* updates

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

* updates

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

* updates

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

* updates

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

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* updates

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

* updates

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

* updates

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

* fixes

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

* add getEdge

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

* bigger

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

* updates

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

* updatres

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

* updates

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

* updates

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
2024-03-26 19:07:16 -07:00
committed by GitHub
parent f714c19890
commit dce5833d79
24 changed files with 3938 additions and 14 deletions

178
docs/kcl/getEdge.md Normal file
View File

@ -0,0 +1,178 @@
---
title: "getEdge"
excerpt: "Get an edge on a 3D solid."
layout: manual
---
Get an edge on a 3D solid.
```js
getEdge(tag: String, extrude_group: ExtrudeGroup) -> Uuid
```
### Examples
```js
const box = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0, 10], %)
|> line([10, 0], %)
|> line([0, -10], %, 'revolveAxis')
|> close(%)
|> extrude(10, %)
const sketch001 = startSketchOn(box, "revolveAxis")
|> startProfileAt([5, 10], %)
|> line([0, -10], %)
|> line([2, 0], %)
|> line([0, 10], %)
|> close(%)
|> revolve({
axis: getEdge('revolveAxis', box),
angle: 90
}, %)
```
### Arguments
* `tag`: `String` (REQUIRED)
* `extrude_group`: `ExtrudeGroup` - An extrude group is a collection of extrude surfaces. (REQUIRED)
```js
{
// 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 sketch group paths.
sketchGroupValues: [{
// 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: "TangentialArc",
} |
{
// 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 id of the extrusion start cap
startCapId: uuid,
// 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 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: "extrudeArc",
}],
// 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
`Uuid`

View File

@ -30,6 +30,7 @@ layout: manual
* [`extrude`](kcl/extrude)
* [`fillet`](kcl/fillet)
* [`floor`](kcl/floor)
* [`getEdge`](kcl/getEdge)
* [`getExtrudeWallTransform`](kcl/getExtrudeWallTransform)
* [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge)
* [`getOppositeEdge`](kcl/getOppositeEdge)
@ -56,6 +57,7 @@ layout: manual
* [`patternLinear3d`](kcl/patternLinear3d)
* [`pi`](kcl/pi)
* [`pow`](kcl/pow)
* [`revolve`](kcl/revolve)
* [`segAng`](kcl/segAng)
* [`segEndX`](kcl/segEndX)
* [`segEndY`](kcl/segEndY)

404
docs/kcl/revolve.md Normal file
View File

@ -0,0 +1,404 @@
---
title: "revolve"
excerpt: "Revolve a sketch around an axis."
layout: manual
---
Revolve a sketch around an axis.
```js
revolve(data: RevolveData, sketch_group: SketchGroup) -> ExtrudeGroup
```
### Examples
```js
const part001 = startSketchOn('XY')
|> startProfileAt([4, 12], %)
|> line([2, 0], %)
|> line([0, -6], %)
|> line([4, -6], %)
|> line([0, -6], %)
|> line([-3.75, -4.5], %)
|> line([0, -5.5], %)
|> line([-2, 0], %)
|> close(%)
|> revolve({ axis: 'y' }, %) // default angle is 360
```
```js
const part001 = startSketchOn('XY')
|> startProfileAt([4, 12], %)
|> line([2, 0], %)
|> line([0, -6], %)
|> line([4, -6], %)
|> line([0, -6], %)
|> line([-3.75, -4.5], %)
|> line([0, -5.5], %)
|> line([-2, 0], %)
|> close(%)
|> revolve({ axis: 'y', angle: 180 }, %)
```
```js
const box = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0, 20], %)
|> line([20, 0], %)
|> line([0, -20], %)
|> close(%)
|> extrude(20, %)
const sketch001 = startSketchOn(box, "END")
|> circle([10, 10], 4, %)
|> revolve({ angle: -90, axis: 'y' }, %)
```
```js
const box = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0, 20], %)
|> line([20, 0], %)
|> line([0, -20], %, 'revolveAxis')
|> close(%)
|> extrude(20, %)
const sketch001 = startSketchOn(box, "END")
|> circle([10, 10], 4, %)
|> revolve({
angle: 90,
axis: getOppositeEdge('revolveAxis', box)
}, %)
```
### Arguments
* `data`: `RevolveData` - Data for revolution surfaces. (REQUIRED)
```js
{
// Angle to revolve (in degrees). Default is 360.
angle: number,
// Axis of revolution.
axis: "x" |
"y" |
"z" |
"-X" |
"-Y" |
"-Z" |
{
custom: {
// The axis.
axis: [number, number, number],
// The origin.
origin: [number, number, number],
},
} |
uuid |
string,
}
```
* `sketch_group`: `SketchGroup` - A sketch group is a collection of paths. (REQUIRED)
```js
{
// The plane id or face id of the sketch group.
entityId: uuid,
// The id of the sketch group.
id: uuid,
// What the sketch is on (can be a plane or a face).
on: {
// The id of the plane.
id: uuid,
// Origin of the plane.
origin: {
x: number,
y: number,
z: number,
},
type: "plane",
// Type for a plane.
value: "XY" | "XZ" | "YZ" | "Custom",
// What should the planes X axis be?
xAxis: {
x: number,
y: number,
z: number,
},
// What should the planes Y axis be?
yAxis: {
x: number,
y: number,
z: number,
},
// The z-axis (normal).
zAxis: {
x: number,
y: number,
z: number,
},
} |
{
// the face id the sketch is on
faceId: uuid,
// The id of the face.
id: uuid,
// The original sketch group id of the object we are sketching on.
sketchGroupId: uuid,
type: "face",
// The tag of the face.
value: string,
// What should the faces X axis be?
xAxis: {
x: number,
y: number,
z: number,
},
// What should the faces Y axis be?
yAxis: {
x: number,
y: number,
z: number,
},
// The z-axis (normal).
zAxis: {
x: number,
y: number,
z: number,
},
},
// 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],
},
// 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: "TangentialArc",
} |
{
// 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,
},
}
```
### Returns
`ExtrudeGroup` - An extrude group is a collection of extrude surfaces.
```js
{
// 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 sketch group paths.
sketchGroupValues: [{
// 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: "TangentialArc",
} |
{
// 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 id of the extrusion start cap
startCapId: uuid,
// 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 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: "extrudeArc",
}],
// 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,
},
}
```

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -1896,9 +1896,9 @@ dependencies = [
[[package]]
name = "kittycad"
version = "0.2.62"
version = "0.2.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bb6a3e37a902c099f3f077f1e75814339bf5d548151a675544366841a882377"
checksum = "93a332250e08fd715ad3d5826e04d36da1c5bb42d0c1b1ff1f0598278b9ebf3c"
dependencies = [
"anyhow",
"async-trait",

View File

@ -59,7 +59,7 @@ members = [
]
[workspace.dependencies]
kittycad = { version = "0.2.62", default-features = false, features = ["js", "requests"] }
kittycad = { version = "0.2.63", default-features = false, features = ["js", "requests"] }
kittycad-execution-plan = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
kittycad-execution-plan-macros = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
kittycad-execution-plan-traits = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }

View File

@ -36,6 +36,7 @@ pub async fn extrude(args: Args) -> Result<MemoryItem, KclError> {
}]
async fn inner_extrude(length: f64, sketch_group: Box<SketchGroup>, args: Args) -> Result<Box<ExtrudeGroup>, KclError> {
let id = uuid::Uuid::new_v4();
// Extrude the element.
args.send_modeling_cmd(
id,
@ -47,6 +48,15 @@ async fn inner_extrude(length: f64, sketch_group: Box<SketchGroup>, args: Args)
)
.await?;
do_post_extrude(sketch_group, length, id, args).await
}
pub(crate) async fn do_post_extrude(
sketch_group: Box<SketchGroup>,
length: f64,
id: Uuid,
args: Args,
) -> Result<Box<ExtrudeGroup>, KclError> {
// We need to do this after extrude for sketch on face.
if let SketchSurface::Face(_) = sketch_group.on {
// Disable the sketch mode.

View File

@ -13,6 +13,8 @@ use crate::{
std::Args,
};
pub(crate) const DEFAULT_TOLERANCE: f64 = 0.0000001;
/// Data for fillets.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
@ -21,18 +23,18 @@ pub struct FilletData {
/// The radius of the fillet.
pub radius: f64,
/// The tags of the paths you want to fillet.
pub tags: Vec<StringOrUuid>,
pub tags: Vec<EdgeReference>,
}
/// A string or a uuid.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Ord, PartialOrd, Eq, Hash)]
#[ts(export)]
#[serde(untagged)]
pub enum StringOrUuid {
/// A uuid.
Uuid(Uuid),
/// A string.
String(String),
pub enum EdgeReference {
/// A uuid of an edge.
Uuid(uuid::Uuid),
/// A tag name of an edge.
Tag(String),
}
/// Create fillets on tagged paths.
@ -76,8 +78,8 @@ async fn inner_fillet(
for tag in data.tags {
let edge_id = match tag {
StringOrUuid::Uuid(uuid) => uuid,
StringOrUuid::String(tag) => {
EdgeReference::Uuid(uuid) => uuid,
EdgeReference::Tag(tag) => {
extrude_group
.sketch_group_values
.iter()
@ -100,7 +102,7 @@ async fn inner_fillet(
edge_id,
object_id: extrude_group.id,
radius: data.radius,
tolerance: 0.0000001, // We can let the user set this in the future.
tolerance: DEFAULT_TOLERANCE, // We can let the user set this in the future.
},
)
.await?;

View File

@ -7,6 +7,7 @@ pub mod import;
pub mod kcl_stdlib;
pub mod math;
pub mod patterns;
pub mod revolve;
pub mod segment;
pub mod shapes;
pub mod sketch;
@ -81,6 +82,8 @@ lazy_static! {
Box::new(crate::std::fillet::GetNextAdjacentEdge),
Box::new(crate::std::fillet::GetPreviousAdjacentEdge),
Box::new(crate::std::helix::Helix),
Box::new(crate::std::revolve::Revolve),
Box::new(crate::std::revolve::GetEdge),
Box::new(crate::std::import::Import),
Box::new(crate::std::math::Cos),
Box::new(crate::std::math::Sin),

View File

@ -0,0 +1,341 @@
//! Standard library revolution surfaces.
use anyhow::Result;
use derive_docs::stdlib;
use kittycad::types::ModelingCmd;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::{
errors::{KclError, KclErrorDetails},
executor::{ExtrudeGroup, MemoryItem, SketchGroup, UserVal},
std::{
extrude::do_post_extrude,
fillet::{EdgeReference, DEFAULT_TOLERANCE},
Args,
},
};
/// Data for revolution surfaces.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
pub struct RevolveData {
/// Angle to revolve (in degrees). Default is 360.
#[serde(default)]
pub angle: Option<f64>,
/// Axis of revolution.
pub axis: RevolveAxis,
}
/// Axis of revolution or tagged edge.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(untagged)]
pub enum RevolveAxis {
/// Axis of revolution.
Axis(RevolveAxisAndOrigin),
/// Tagged edge.
Edge(EdgeReference),
}
/// Axis of revolution.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(rename_all = "camelCase")]
pub enum RevolveAxisAndOrigin {
/// X-axis.
#[serde(alias = "X")]
X,
/// Y-axis.
#[serde(alias = "Y")]
Y,
/// Z-axis.
#[serde(alias = "Z")]
Z,
/// Flip the X-axis.
#[serde(rename = "-X", alias = "-x")]
NegX,
/// Flip the Y-axis.
#[serde(rename = "-Y", alias = "-y")]
NegY,
/// Flip the Z-axis.
#[serde(rename = "-Z", alias = "-z")]
NegZ,
Custom {
/// The axis.
axis: [f64; 3],
/// The origin.
origin: [f64; 3],
},
}
impl RevolveAxisAndOrigin {
/// Get the axis and origin.
pub fn axis_and_origin(&self) -> Result<(kittycad::types::Point3D, kittycad::types::Point3D), KclError> {
let (axis, origin) = match self {
RevolveAxisAndOrigin::X => ([1.0, 0.0, 0.0], [0.0, 0.0, 0.0]),
RevolveAxisAndOrigin::Y => ([0.0, 1.0, 0.0], [0.0, 0.0, 0.0]),
RevolveAxisAndOrigin::Z => ([0.0, 0.0, 1.0], [0.0, 0.0, 0.0]),
RevolveAxisAndOrigin::NegX => ([-1.0, 0.0, 0.0], [0.0, 0.0, 0.0]),
RevolveAxisAndOrigin::NegY => ([0.0, -1.0, 0.0], [0.0, 0.0, 0.0]),
RevolveAxisAndOrigin::NegZ => ([0.0, 0.0, -1.0], [0.0, 0.0, 0.0]),
RevolveAxisAndOrigin::Custom { axis, origin } => (*axis, *origin),
};
Ok((
kittycad::types::Point3D {
x: axis[0],
y: axis[1],
z: axis[2],
},
kittycad::types::Point3D {
x: origin[0],
y: origin[1],
z: origin[2],
},
))
}
}
/// Revolve a sketch around an axis.
pub async fn revolve(args: Args) -> Result<MemoryItem, KclError> {
let (data, sketch_group): (RevolveData, Box<SketchGroup>) = args.get_data_and_sketch_group()?;
let extrude_group = inner_revolve(data, sketch_group, args).await?;
Ok(MemoryItem::ExtrudeGroup(extrude_group))
}
/// Revolve a sketch around an axis.
///
/// ```no_run
/// const part001 = startSketchOn('XY')
/// |> startProfileAt([4, 12], %)
/// |> line([2, 0], %)
/// |> line([0, -6], %)
/// |> line([4, -6], %)
/// |> line([0, -6], %)
/// |> line([-3.75, -4.5], %)
/// |> line([0, -5.5], %)
/// |> line([-2, 0], %)
/// |> close(%)
/// |> revolve({axis: 'y'}, %) // default angle is 360
/// ```
///
/// ```no_run
/// const part001 = startSketchOn('XY')
/// |> startProfileAt([4, 12], %)
/// |> line([2, 0], %)
/// |> line([0, -6], %)
/// |> line([4, -6], %)
/// |> line([0, -6], %)
/// |> line([-3.75, -4.5], %)
/// |> line([0, -5.5], %)
/// |> line([-2, 0], %)
/// |> close(%)
/// |> revolve({axis: 'y', angle: 180}, %)
/// ```
///
/// ```no_run
/// const box = startSketchOn('XY')
/// |> startProfileAt([0, 0], %)
/// |> line([0, 20], %)
/// |> line([20, 0], %)
/// |> line([0, -20], %)
/// |> close(%)
/// |> extrude(20, %)
///
/// const sketch001 = startSketchOn(box, "END")
/// |> circle([10,10], 4, %)
/// |> revolve({
/// angle: -90,
/// axis: 'y'
/// }, %)
/// ```
///
/// ```no_run
/// const box = startSketchOn('XY')
/// |> startProfileAt([0, 0], %)
/// |> line([0, 20], %)
/// |> line([20, 0], %)
/// |> line([0, -20], %, 'revolveAxis')
/// |> close(%)
/// |> extrude(20, %)
///
/// const sketch001 = startSketchOn(box, "END")
/// |> circle([10,10], 4, %)
/// |> revolve({
/// angle: 90,
/// axis: getOppositeEdge('revolveAxis', box)
/// }, %)
///
/// ```
#[stdlib {
name = "revolve",
}]
async fn inner_revolve(
data: RevolveData,
sketch_group: Box<SketchGroup>,
args: Args,
) -> Result<Box<ExtrudeGroup>, KclError> {
if let Some(angle) = data.angle {
// Return an error if the angle is less than -360 or greater than 360.
if !(-360.0..=360.0).contains(&angle) {
return Err(KclError::Semantic(KclErrorDetails {
message: format!("Expected angle to be between -360 and 360, found `{}`", angle),
source_ranges: vec![args.source_range],
}));
}
}
let angle = kittycad::types::Angle::from_degrees(data.angle.unwrap_or(360.0));
let id = uuid::Uuid::new_v4();
match data.axis {
RevolveAxis::Axis(axis) => {
let (axis, origin) = axis.axis_and_origin()?;
args.send_modeling_cmd(
id,
ModelingCmd::Revolve {
angle,
target: sketch_group.id,
axis,
origin,
tolerance: DEFAULT_TOLERANCE,
axis_is_2d: true,
},
)
.await?;
}
RevolveAxis::Edge(edge) => {
let edge_id = match edge {
EdgeReference::Uuid(uuid) => uuid,
EdgeReference::Tag(tag) => {
sketch_group
.value
.iter()
.find(|p| p.get_name() == tag)
.ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: format!("No edge found with tag: `{}`", tag),
source_ranges: vec![args.source_range],
})
})?
.get_base()
.geo_meta
.id
}
};
args.send_modeling_cmd(
id,
ModelingCmd::RevolveAboutEdge {
angle,
target: sketch_group.id,
edge_id,
tolerance: DEFAULT_TOLERANCE,
},
)
.await?;
}
}
do_post_extrude(sketch_group, 0.0, id, args).await
}
/// Get an edge on a 3D solid.
pub async fn get_edge(args: Args) -> Result<MemoryItem, KclError> {
let (tag, extrude_group): (String, Box<ExtrudeGroup>) = args.get_data_and_extrude_group()?;
let edge = inner_get_edge(tag, extrude_group, args.clone()).await?;
Ok(MemoryItem::UserVal(UserVal {
value: serde_json::to_value(edge).map_err(|e| {
KclError::Type(KclErrorDetails {
message: format!("Failed to convert Uuid to json: {}", e),
source_ranges: vec![args.source_range],
})
})?,
meta: vec![args.source_range.into()],
}))
}
/// Get an edge on a 3D solid.
///
/// ```no_run
/// const box = startSketchOn('XY')
/// |> startProfileAt([0, 0], %)
/// |> line([0, 10], %)
/// |> line([10, 0], %)
/// |> line([0, -10], %, 'revolveAxis')
/// |> close(%)
/// |> extrude(10, %)
///
/// const sketch001 = startSketchOn(box, "revolveAxis")
/// |> startProfileAt([5, 10], %)
/// |> line([0, -10], %)
/// |> line([2, 0], %)
/// |> line([0, 10], %)
/// |> close(%)
/// |> revolve({ axis: getEdge('revolveAxis', box), angle: 90 }, %)
/// ```
#[stdlib {
name = "getEdge",
}]
async fn inner_get_edge(tag: String, extrude_group: Box<ExtrudeGroup>, args: Args) -> Result<Uuid, KclError> {
let tagged_path = extrude_group
.sketch_group_values
.iter()
.find(|p| p.get_name() == tag)
.ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: format!("No edge found with tag: `{}`", tag),
source_ranges: vec![args.source_range],
})
})?
.get_base();
Ok(tagged_path.geo_meta.id)
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use crate::std::revolve::{RevolveAxis, RevolveAxisAndOrigin};
#[test]
fn test_deserialize_revolve_axis() {
let data = RevolveAxis::Axis(RevolveAxisAndOrigin::X);
let mut str_json = serde_json::to_string(&data).unwrap();
assert_eq!(str_json, "\"x\"");
str_json = "\"Y\"".to_string();
let data: RevolveAxis = serde_json::from_str(&str_json).unwrap();
assert_eq!(data, RevolveAxis::Axis(RevolveAxisAndOrigin::Y));
str_json = "\"-Y\"".to_string();
let data: RevolveAxis = serde_json::from_str(&str_json).unwrap();
assert_eq!(data, RevolveAxis::Axis(RevolveAxisAndOrigin::NegY));
str_json = "\"-x\"".to_string();
let data: RevolveAxis = serde_json::from_str(&str_json).unwrap();
assert_eq!(data, RevolveAxis::Axis(RevolveAxisAndOrigin::NegX));
let data = RevolveAxis::Axis(RevolveAxisAndOrigin::Custom {
axis: [0.0, -1.0, 0.0],
origin: [1.0, 0.0, 2.0],
});
str_json = serde_json::to_string(&data).unwrap();
assert_eq!(str_json, r#"{"custom":{"axis":[0.0,-1.0,0.0],"origin":[1.0,0.0,2.0]}}"#);
str_json = r#"{"custom": {"axis": [0,-1,0], "origin": [1,0,2.0]}}"#.to_string();
let data: RevolveAxis = serde_json::from_str(&str_json).unwrap();
assert_eq!(
data,
RevolveAxis::Axis(RevolveAxisAndOrigin::Custom {
axis: [0.0, -1.0, 0.0],
origin: [1.0, 0.0, 2.0]
})
);
}
}

View File

@ -1487,3 +1487,337 @@ async fn serial_test_big_number_angle_to_match_length_y() {
1.0,
);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_simple_revolve() {
let code = r#"const part001 = startSketchOn('XY')
|> startProfileAt([4, 12], %)
|> line([2, 0], %)
|> line([0, -6], %)
|> line([4, -6], %)
|> line([0, -6], %)
|> line([-3.75, -4.5], %)
|> line([0, -5.5], %)
|> line([-2, 0], %)
|> close(%)
|> revolve({axis: 'y'}, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
.await
.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve.png", &result, 1.0);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_simple_revolve_uppercase() {
let code = r#"const part001 = startSketchOn('XY')
|> startProfileAt([4, 12], %)
|> line([2, 0], %)
|> line([0, -6], %)
|> line([4, -6], %)
|> line([0, -6], %)
|> line([-3.75, -4.5], %)
|> line([0, -5.5], %)
|> line([-2, 0], %)
|> close(%)
|> revolve({axis: 'Y'}, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
.await
.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_uppercase.png", &result, 1.0);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_simple_revolve_negative() {
let code = r#"const part001 = startSketchOn('XY')
|> startProfileAt([4, 12], %)
|> line([2, 0], %)
|> line([0, -6], %)
|> line([4, -6], %)
|> line([0, -6], %)
|> line([-3.75, -4.5], %)
|> line([0, -5.5], %)
|> line([-2, 0], %)
|> close(%)
|> revolve({axis: '-Y', angle: 180}, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
.await
.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_negative.png", &result, 1.0);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_revolve_bad_angle_low() {
let code = r#"const part001 = startSketchOn('XY')
|> startProfileAt([4, 12], %)
|> line([2, 0], %)
|> line([0, -6], %)
|> line([4, -6], %)
|> line([0, -6], %)
|> line([-3.75, -4.5], %)
|> line([0, -5.5], %)
|> line([-2, 0], %)
|> close(%)
|> revolve({axis: 'y', angle: -455}, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm).await;
assert!(result.is_err());
assert_eq!(
result.err().unwrap().to_string(),
r#"semantic: KclErrorDetails { source_ranges: [SourceRange([278, 314])], message: "Expected angle to be between -360 and 360, found `-455`" }"#
);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_revolve_bad_angle_high() {
let code = r#"const part001 = startSketchOn('XY')
|> startProfileAt([4, 12], %)
|> line([2, 0], %)
|> line([0, -6], %)
|> line([4, -6], %)
|> line([0, -6], %)
|> line([-3.75, -4.5], %)
|> line([0, -5.5], %)
|> line([-2, 0], %)
|> close(%)
|> revolve({axis: 'y', angle: 455}, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm).await;
assert!(result.is_err());
assert_eq!(
result.err().unwrap().to_string(),
r#"semantic: KclErrorDetails { source_ranges: [SourceRange([278, 313])], message: "Expected angle to be between -360 and 360, found `455`" }"#
);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_simple_revolve_custom_angle() {
let code = r#"const part001 = startSketchOn('XY')
|> startProfileAt([4, 12], %)
|> line([2, 0], %)
|> line([0, -6], %)
|> line([4, -6], %)
|> line([0, -6], %)
|> line([-3.75, -4.5], %)
|> line([0, -5.5], %)
|> line([-2, 0], %)
|> close(%)
|> revolve({axis: 'y', angle: 180}, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
.await
.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_custom_angle.png", &result, 1.0);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_simple_revolve_custom_axis() {
let code = r#"const part001 = startSketchOn('XY')
|> startProfileAt([4, 12], %)
|> line([2, 0], %)
|> line([0, -6], %)
|> line([4, -6], %)
|> line([0, -6], %)
|> line([-3.75, -4.5], %)
|> line([0, -5.5], %)
|> line([-2, 0], %)
|> close(%)
|> revolve({axis: {custom: {axis: [0, -1, 0], origin: [0,0,0]}}, angle: 180}, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
.await
.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_custom_axis.png", &result, 1.0);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_revolve_on_edge() {
let code = r#"const box = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0, 10], %)
|> line([10, 0], %)
|> line([0, -10], %, 'revolveAxis')
|> close(%)
|> extrude(10, %)
const sketch001 = startSketchOn(box, "end")
|> startProfileAt([5, 10], %)
|> line([0, -10], %)
|> line([2, 0], %)
|> line([0, 10], %)
|> close(%)
|> revolve({ axis: getOppositeEdge('revolveAxis', box), angle: 90 }, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
.await
.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/revolve_on_edge.png", &result, 1.0);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_revolve_on_edge_get_edge() {
let code = r#"const box = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0, 10], %)
|> line([10, 0], %)
|> line([0, -10], %, 'revolveAxis')
|> close(%)
|> extrude(10, %)
const sketch001 = startSketchOn(box, "revolveAxis")
|> startProfileAt([5, 10], %)
|> line([0, -10], %)
|> line([2, 0], %)
|> line([0, 10], %)
|> close(%)
|> revolve({ axis: getEdge('revolveAxis', box), angle: 90 }, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
.await
.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/revolve_on_edge_get_edge.png", &result, 1.0);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_revolve_on_face_circle_edge() {
let code = r#"const box = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0, 20], %)
|> line([20, 0], %)
|> line([0, -20], %, 'revolveAxis')
|> close(%)
|> extrude(20, %)
const sketch001 = startSketchOn(box, "END")
|> circle([10,10], 4, %)
|> revolve({
angle: 90,
axis: getOppositeEdge('revolveAxis', box)
}, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
.await
.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/revolve_on_face_circle_edge.png", &result, 1.0);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_revolve_on_face_circle() {
let code = r#"const box = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0, 20], %)
|> line([20, 0], %, 'revolveAxis')
|> line([0, -20], %)
|> close(%)
|> extrude(20, %)
const sketch001 = startSketchOn(box, "END")
|> circle([10,10], 4, %)
|> revolve({
angle: -90,
axis: 'y'
}, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
.await
.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/revolve_on_face_circle.png", &result, 1.0);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_revolve_on_face() {
let code = r#"const box = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0, 10], %)
|> line([10, 0], %)
|> line([0, -10], %)
|> close(%, 'revolveAxis')
|> extrude(10, %)
const sketch001 = startSketchOn(box, "end")
|> startProfileAt([5, 10], %)
|> line([0, -10], %)
|> line([2, 0], %)
|> line([0, 10], %)
|> close(%)
|> revolve({
axis: 'y',
angle: -90,
}, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
.await
.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/revolve_on_face.png", &result, 1.0);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_basic_revolve_circle() {
let code = r#"const sketch001 = startSketchOn('XY')
|> circle([15, 0], 5, %)
|> revolve({
angle: 360,
axis: 'y'
}, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
.await
.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/basic_revolve_circle.png", &result, 1.0);
}
#[tokio::test(flavor = "multi_thread")]
#[ignore] // Ignore this test until https://github.com/KittyCAD/engine/pull/1930 is fixed
async fn serial_test_simple_revolve_sketch_on_edge() {
let code = r#"const part001 = startSketchOn('XY')
|> startProfileAt([4, 12], %)
|> line([2, 0], %)
|> line([0, -6], %)
|> line([4, -6], %)
|> line([0, -6], %)
|> line([-3.75, -4.5], %)
|> line([0, -5.5], %)
|> line([-2, 0], %)
|> close(%)
|> revolve({axis: 'y', angle: 180}, %)
const part002 = startSketchOn(part001, 'end')
|> startProfileAt([4.5, -5], %)
|> line([0, 5], %)
|> line([5, 0], %)
|> line([0, -5], %)
|> close(%)
|> extrude(5, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
.await
.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_sketch_on_edge.png", &result, 1.0);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB