Bidirectional extrude/revolve (#6154)
* extend extrude endpoint * revolve and mocks * add bounds check to revolve * kcl examples of new args * update to 110 * fix mock * move example to prelude * change to camelCase * new prelude tests * extend just file * missed change * change to XY * redo sim tests * review changes * redo markdown
This commit is contained in:
@ -10,6 +10,7 @@ use kcmc::{
|
||||
ok_response::OkModelingCmdResponse,
|
||||
output::ExtrusionFaceInfo,
|
||||
shared::ExtrusionFaceCapType,
|
||||
shared::Opposite,
|
||||
websocket::{ModelingCmdReq, OkWebSocketResponseData},
|
||||
ModelingCmd,
|
||||
};
|
||||
@ -30,10 +31,22 @@ use crate::{
|
||||
pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
|
||||
let length = args.get_kw_arg("length")?;
|
||||
let symmetric = args.get_kw_arg_opt("symmetric")?;
|
||||
let bidirectional_length = args.get_kw_arg_opt("bidirectionalLength")?;
|
||||
let tag_start = args.get_kw_arg_opt("tagStart")?;
|
||||
let tag_end = args.get_kw_arg_opt("tagEnd")?;
|
||||
|
||||
let result = inner_extrude(sketches, length, tag_start, tag_end, exec_state, args).await?;
|
||||
let result = inner_extrude(
|
||||
sketches,
|
||||
length,
|
||||
symmetric,
|
||||
bidirectional_length,
|
||||
tag_start,
|
||||
tag_end,
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(result.into())
|
||||
}
|
||||
@ -87,6 +100,50 @@ pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 10)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// exampleSketch = startSketchOn('XZ')
|
||||
/// |> startProfileAt([-10, 0], %)
|
||||
/// |> arc({
|
||||
/// angleStart = 120,
|
||||
/// angleEnd = -60,
|
||||
/// radius = 5,
|
||||
/// }, %)
|
||||
/// |> line(end = [10, 0])
|
||||
/// |> line(end = [5, 0])
|
||||
/// |> bezierCurve({
|
||||
/// control1 = [-3, 0],
|
||||
/// control2 = [2, 10],
|
||||
/// to = [-5, 10],
|
||||
/// }, %)
|
||||
/// |> line(end = [-4, 10])
|
||||
/// |> line(end = [-5, -2])
|
||||
/// |> close()
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 20, symmetric = true)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// exampleSketch = startSketchOn('XZ')
|
||||
/// |> startProfileAt([-10, 0], %)
|
||||
/// |> arc({
|
||||
/// angleStart = 120,
|
||||
/// angleEnd = -60,
|
||||
/// radius = 5,
|
||||
/// }, %)
|
||||
/// |> line(end = [10, 0])
|
||||
/// |> line(end = [5, 0])
|
||||
/// |> bezierCurve({
|
||||
/// control1 = [-3, 0],
|
||||
/// control2 = [2, 10],
|
||||
/// to = [-5, 10],
|
||||
/// }, %)
|
||||
/// |> line(end = [-4, 10])
|
||||
/// |> line(end = [-5, -2])
|
||||
/// |> close()
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 10, bidirectionalLength = 50)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "extrude",
|
||||
feature_tree_operation = true,
|
||||
@ -95,6 +152,9 @@ pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
args = {
|
||||
sketches = { docs = "Which sketch or sketches should be extruded"},
|
||||
length = { docs = "How far to extrude the given sketches"},
|
||||
symmetric = { docs = "If true, the extrusion will happen symmetrically around the sketch. Otherwise, the
|
||||
extrusion will happen on only one side of the sketch." },
|
||||
bidirectional_length = { docs = "If specified, will also extrude in the opposite direction to 'distance' to the specified distance. If 'symmetric' is true, this value is ignored."},
|
||||
tag_start = { docs = "A named tag for the face at the start of the extrusion, i.e. the original sketch" },
|
||||
tag_end = { docs = "A named tag for the face at the end of the extrusion, i.e. the new face created by extruding the original sketch" },
|
||||
}
|
||||
@ -103,6 +163,8 @@ pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
async fn inner_extrude(
|
||||
sketches: Vec<Sketch>,
|
||||
length: f64,
|
||||
symmetric: Option<bool>,
|
||||
bidirectional_length: Option<f64>,
|
||||
tag_start: Option<TagNode>,
|
||||
tag_end: Option<TagNode>,
|
||||
exec_state: &mut ExecState,
|
||||
@ -110,6 +172,25 @@ async fn inner_extrude(
|
||||
) -> Result<Vec<Solid>, KclError> {
|
||||
// Extrude the element(s).
|
||||
let mut solids = Vec::new();
|
||||
|
||||
if symmetric.unwrap_or(false) && bidirectional_length.is_some() {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
source_ranges: vec![args.source_range],
|
||||
message: "You cannot give both `symmetric` and `bidirectional` params, you have to choose one or the other"
|
||||
.to_owned(),
|
||||
}));
|
||||
}
|
||||
|
||||
let bidirection = bidirectional_length.map(LengthUnit);
|
||||
|
||||
let opposite = match (symmetric, bidirection) {
|
||||
(Some(true), _) => Opposite::Symmetric,
|
||||
(None, None) => Opposite::None,
|
||||
(Some(false), None) => Opposite::None,
|
||||
(None, Some(length)) => Opposite::Other(length),
|
||||
(Some(false), Some(length)) => Opposite::Other(length),
|
||||
};
|
||||
|
||||
for sketch in &sketches {
|
||||
let id = exec_state.next_uuid();
|
||||
args.batch_modeling_cmds(&sketch.build_sketch_mode_cmds(
|
||||
@ -120,6 +201,7 @@ async fn inner_extrude(
|
||||
target: sketch.id.into(),
|
||||
distance: LengthUnit(length),
|
||||
faces: Default::default(),
|
||||
opposite: opposite.clone(),
|
||||
}),
|
||||
},
|
||||
))
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Standard library revolution surfaces.
|
||||
|
||||
use anyhow::Result;
|
||||
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, shared::Angle, ModelingCmd};
|
||||
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, shared::Angle, shared::Opposite, ModelingCmd};
|
||||
use kittycad_modeling_cmds::{self as kcmc, shared::Point3d};
|
||||
|
||||
use super::DEFAULT_TOLERANCE;
|
||||
@ -30,8 +30,22 @@ pub async fn revolve(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
let tolerance = args.get_kw_arg_opt("tolerance")?;
|
||||
let tag_start = args.get_kw_arg_opt("tagStart")?;
|
||||
let tag_end = args.get_kw_arg_opt("tagEnd")?;
|
||||
let symmetric = args.get_kw_arg_opt("symmetric")?;
|
||||
let bidirectional_angle = args.get_kw_arg_opt("bidirectionalAngle")?;
|
||||
|
||||
let value = inner_revolve(sketches, axis, angle, tolerance, tag_start, tag_end, exec_state, args).await?;
|
||||
let value = inner_revolve(
|
||||
sketches,
|
||||
axis,
|
||||
angle,
|
||||
tolerance,
|
||||
tag_start,
|
||||
tag_end,
|
||||
symmetric,
|
||||
bidirectional_angle,
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
.await?;
|
||||
Ok(value.into())
|
||||
}
|
||||
|
||||
@ -43,6 +57,8 @@ async fn inner_revolve(
|
||||
tolerance: Option<f64>,
|
||||
tag_start: Option<TagNode>,
|
||||
tag_end: Option<TagNode>,
|
||||
symmetric: Option<bool>,
|
||||
bidirectional_angle: Option<f64>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Vec<Solid>, KclError> {
|
||||
@ -58,8 +74,54 @@ async fn inner_revolve(
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(bidirectional_angle) = bidirectional_angle {
|
||||
// Return an error if the angle is zero.
|
||||
// We don't use validate() here because we want to return a specific error message that is
|
||||
// nice and we use the other data in the docs, so we still need use the derive above for the json schema.
|
||||
if !(-360.0..=360.0).contains(&bidirectional_angle) || bidirectional_angle == 0.0 {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!(
|
||||
"Expected bidirectional angle to be between -360 and 360 and not 0, found `{}`",
|
||||
bidirectional_angle
|
||||
),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
}
|
||||
|
||||
if let Some(angle) = angle {
|
||||
let ang = angle.signum() * bidirectional_angle + angle;
|
||||
if !(-360.0..=360.0).contains(&ang) {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!(
|
||||
"Combined angle and bidirectional must be between -360 and 360, found '{}'",
|
||||
ang
|
||||
),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if symmetric.unwrap_or(false) && bidirectional_angle.is_some() {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
source_ranges: vec![args.source_range],
|
||||
message: "You cannot give both `symmetric` and `bidirectional` params, you have to choose one or the other"
|
||||
.to_owned(),
|
||||
}));
|
||||
}
|
||||
|
||||
let angle = Angle::from_degrees(angle.unwrap_or(360.0));
|
||||
|
||||
let bidirectional_angle = bidirectional_angle.map(Angle::from_degrees);
|
||||
|
||||
let opposite = match (symmetric, bidirectional_angle) {
|
||||
(Some(true), _) => Opposite::Symmetric,
|
||||
(None, None) => Opposite::None,
|
||||
(Some(false), None) => Opposite::None,
|
||||
(None, Some(angle)) => Opposite::Other(angle),
|
||||
(Some(false), Some(angle)) => Opposite::Other(angle),
|
||||
};
|
||||
|
||||
let mut solids = Vec::new();
|
||||
for sketch in &sketches {
|
||||
let id = exec_state.next_uuid();
|
||||
@ -83,6 +145,7 @@ async fn inner_revolve(
|
||||
},
|
||||
tolerance: LengthUnit(tolerance.unwrap_or(DEFAULT_TOLERANCE)),
|
||||
axis_is_2d: true,
|
||||
opposite: opposite.clone(),
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
@ -96,6 +159,7 @@ async fn inner_revolve(
|
||||
target: sketch.id.into(),
|
||||
edge_id,
|
||||
tolerance: LengthUnit(tolerance.unwrap_or(DEFAULT_TOLERANCE)),
|
||||
opposite: opposite.clone(),
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
Reference in New Issue
Block a user