* start 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> * more Signed-off-by: Jess Frazelle <github@jessfraz.com> * enhancements 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> * get plane data 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> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * cleanup Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> * more Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * fmt 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> * fixes 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> * fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> * add lint rule 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> * fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com>
321 lines
9.2 KiB
Rust
321 lines
9.2 KiB
Rust
//! Standard library revolution surfaces.
|
|
|
|
use anyhow::Result;
|
|
use derive_docs::stdlib;
|
|
use kittycad::types::ModelingCmd;
|
|
use schemars::JsonSchema;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::{
|
|
errors::{KclError, KclErrorDetails},
|
|
executor::{ExtrudeGroup, MemoryItem, SketchGroup},
|
|
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(rename = "X", alias = "x")]
|
|
X,
|
|
/// Y-axis.
|
|
#[serde(rename = "Y", alias = "y")]
|
|
Y,
|
|
/// Z-axis.
|
|
#[serde(rename = "Z", 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
|
|
/// // A donut shape.
|
|
/// const sketch001 = startSketchOn('XY')
|
|
/// |> circle([15, 0], 5, %)
|
|
/// |> revolve({
|
|
/// angle: 360,
|
|
/// axis: 'y'
|
|
/// }, %)
|
|
/// ```
|
|
///
|
|
/// ```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 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, %)
|
|
/// ```
|
|
///
|
|
/// ```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)
|
|
/// }, %)
|
|
/// ```
|
|
///
|
|
/// ```no_run
|
|
/// const sketch001 = startSketchOn('XY')
|
|
/// |> startProfileAt([10, 0], %)
|
|
/// |> line([5, -5], %)
|
|
/// |> line([5, 5], %)
|
|
/// |> lineTo([profileStartX(%), profileStartY(%)], %)
|
|
/// |> close(%)
|
|
///
|
|
/// const part001 = revolve({
|
|
/// axis: {
|
|
/// custom: {
|
|
/// axis: [0.0, 1.0, 0.0],
|
|
/// origin: [0.0, 0.0, 0.0]
|
|
/// }
|
|
/// }
|
|
/// }, sketch001)
|
|
/// ```
|
|
#[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.batch_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) => args.get_tag_engine_info(&tag)?.id,
|
|
};
|
|
args.batch_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
|
|
}
|
|
|
|
#[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]
|
|
})
|
|
);
|
|
}
|
|
}
|