2024-03-26 19:07:16 -07:00
|
|
|
//! Standard library revolution surfaces.
|
|
|
|
|
|
|
|
use anyhow::Result;
|
2025-04-11 14:17:20 -04:00
|
|
|
use kcmc::{
|
|
|
|
each_cmd as mcmd,
|
|
|
|
length_unit::LengthUnit,
|
|
|
|
shared::{Angle, Opposite},
|
|
|
|
ModelingCmd,
|
|
|
|
};
|
2025-04-03 22:44:52 +13:00
|
|
|
use kittycad_modeling_cmds::{self as kcmc, shared::Point3d};
|
2024-03-26 19:07:16 -07:00
|
|
|
|
2025-04-14 05:58:19 -04:00
|
|
|
use super::{args::TyF64, DEFAULT_TOLERANCE};
|
2024-03-26 19:07:16 -07:00
|
|
|
use crate::{
|
|
|
|
errors::{KclError, KclErrorDetails},
|
2025-04-03 22:44:52 +13:00
|
|
|
execution::{
|
2025-04-23 10:58:35 +12:00
|
|
|
types::{NumericType, PrimitiveType, RuntimeType},
|
2025-04-03 22:44:52 +13:00
|
|
|
ExecState, KclValue, Sketch, Solid,
|
|
|
|
},
|
2025-03-19 12:18:19 -07:00
|
|
|
parsing::ast::types::TagNode,
|
2025-03-31 15:28:15 -04:00
|
|
|
std::{axis_or_reference::Axis2dOrEdgeReference, extrude::do_post_extrude, Args},
|
2024-03-26 19:07:16 -07:00
|
|
|
};
|
|
|
|
|
2025-03-13 09:38:22 -07:00
|
|
|
/// Revolve a sketch or set of sketches around an axis.
|
2024-09-16 15:10:33 -04:00
|
|
|
pub async fn revolve(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
2025-03-21 10:56:55 +13:00
|
|
|
let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
|
2025-04-03 22:44:52 +13:00
|
|
|
let axis = args.get_kw_arg_typed(
|
|
|
|
"axis",
|
|
|
|
&RuntimeType::Union(vec![
|
|
|
|
RuntimeType::Primitive(PrimitiveType::Edge),
|
|
|
|
RuntimeType::Primitive(PrimitiveType::Axis2d),
|
|
|
|
]),
|
|
|
|
exec_state,
|
|
|
|
)?;
|
2025-04-23 10:58:35 +12:00
|
|
|
let angle: Option<TyF64> = args.get_kw_arg_opt_typed("angle", &RuntimeType::degrees(), exec_state)?;
|
|
|
|
let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::length(), exec_state)?;
|
2025-03-19 12:18:19 -07:00
|
|
|
let tag_start = args.get_kw_arg_opt("tagStart")?;
|
|
|
|
let tag_end = args.get_kw_arg_opt("tagEnd")?;
|
2025-04-10 15:46:10 +01:00
|
|
|
let symmetric = args.get_kw_arg_opt("symmetric")?;
|
2025-04-14 05:58:19 -04:00
|
|
|
let bidirectional_angle: Option<TyF64> =
|
|
|
|
args.get_kw_arg_opt_typed("bidirectionalAngle", &RuntimeType::angle(), exec_state)?;
|
2024-03-26 19:07:16 -07:00
|
|
|
|
2025-04-10 15:46:10 +01:00
|
|
|
let value = inner_revolve(
|
|
|
|
sketches,
|
|
|
|
axis,
|
2025-04-14 05:58:19 -04:00
|
|
|
angle.map(|t| t.n),
|
2025-04-23 10:58:35 +12:00
|
|
|
tolerance,
|
2025-04-10 15:46:10 +01:00
|
|
|
tag_start,
|
|
|
|
tag_end,
|
|
|
|
symmetric,
|
2025-04-14 05:58:19 -04:00
|
|
|
bidirectional_angle.map(|t| t.n),
|
2025-04-10 15:46:10 +01:00
|
|
|
exec_state,
|
|
|
|
args,
|
|
|
|
)
|
|
|
|
.await?;
|
2025-03-13 09:38:22 -07:00
|
|
|
Ok(value.into())
|
2024-03-26 19:07:16 -07:00
|
|
|
}
|
|
|
|
|
2025-03-19 12:18:19 -07:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2024-03-26 19:07:16 -07:00
|
|
|
async fn inner_revolve(
|
2025-03-17 17:57:26 +13:00
|
|
|
sketches: Vec<Sketch>,
|
2025-03-18 20:34:44 -07:00
|
|
|
axis: Axis2dOrEdgeReference,
|
|
|
|
angle: Option<f64>,
|
2025-04-23 10:58:35 +12:00
|
|
|
tolerance: Option<TyF64>,
|
2025-03-19 12:18:19 -07:00
|
|
|
tag_start: Option<TagNode>,
|
|
|
|
tag_end: Option<TagNode>,
|
2025-04-10 15:46:10 +01:00
|
|
|
symmetric: Option<bool>,
|
|
|
|
bidirectional_angle: Option<f64>,
|
2024-09-16 15:10:33 -04:00
|
|
|
exec_state: &mut ExecState,
|
2024-03-26 19:07:16 -07:00
|
|
|
args: Args,
|
2025-03-17 17:57:26 +13:00
|
|
|
) -> Result<Vec<Solid>, KclError> {
|
2025-03-18 20:34:44 -07:00
|
|
|
if let Some(angle) = angle {
|
2024-09-26 16:33:49 -07:00
|
|
|
// 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(&angle) || angle == 0.0 {
|
2024-03-26 19:07:16 -07:00
|
|
|
return Err(KclError::Semantic(KclErrorDetails {
|
2024-09-26 16:33:49 -07:00
|
|
|
message: format!("Expected angle to be between -360 and 360 and not 0, found `{}`", angle),
|
2024-03-26 19:07:16 -07:00
|
|
|
source_ranges: vec![args.source_range],
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-04-10 15:46:10 +01:00
|
|
|
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(),
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2025-03-18 20:34:44 -07:00
|
|
|
let angle = Angle::from_degrees(angle.unwrap_or(360.0));
|
2024-03-26 19:07:16 -07:00
|
|
|
|
2025-04-10 15:46:10 +01:00
|
|
|
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),
|
|
|
|
};
|
|
|
|
|
2025-03-13 09:38:22 -07:00
|
|
|
let mut solids = Vec::new();
|
|
|
|
for sketch in &sketches {
|
|
|
|
let id = exec_state.next_uuid();
|
|
|
|
|
2025-03-18 20:34:44 -07:00
|
|
|
match &axis {
|
2025-04-03 22:44:52 +13:00
|
|
|
Axis2dOrEdgeReference::Axis { direction, origin } => {
|
2025-03-13 09:38:22 -07:00
|
|
|
args.batch_modeling_cmd(
|
|
|
|
id,
|
|
|
|
ModelingCmd::from(mcmd::Revolve {
|
|
|
|
angle,
|
|
|
|
target: sketch.id.into(),
|
2025-04-03 22:44:52 +13:00
|
|
|
axis: Point3d {
|
2025-04-23 10:58:35 +12:00
|
|
|
x: direction[0].to_mm(),
|
|
|
|
y: direction[1].to_mm(),
|
2025-04-03 22:44:52 +13:00
|
|
|
z: 0.0,
|
|
|
|
},
|
|
|
|
origin: Point3d {
|
2025-04-23 10:58:35 +12:00
|
|
|
x: LengthUnit(origin[0].to_mm()),
|
|
|
|
y: LengthUnit(origin[1].to_mm()),
|
2025-04-03 22:44:52 +13:00
|
|
|
z: LengthUnit(0.0),
|
|
|
|
},
|
2025-04-23 10:58:35 +12:00
|
|
|
tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE)),
|
2025-03-13 09:38:22 -07:00
|
|
|
axis_is_2d: true,
|
2025-04-10 15:46:10 +01:00
|
|
|
opposite: opposite.clone(),
|
2025-03-13 09:38:22 -07:00
|
|
|
}),
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
Axis2dOrEdgeReference::Edge(edge) => {
|
|
|
|
let edge_id = edge.get_engine_id(exec_state, &args)?;
|
|
|
|
args.batch_modeling_cmd(
|
|
|
|
id,
|
|
|
|
ModelingCmd::from(mcmd::RevolveAboutEdge {
|
|
|
|
angle,
|
|
|
|
target: sketch.id.into(),
|
|
|
|
edge_id,
|
2025-04-23 10:58:35 +12:00
|
|
|
tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE)),
|
2025-04-10 15:46:10 +01:00
|
|
|
opposite: opposite.clone(),
|
2025-03-13 09:38:22 -07:00
|
|
|
}),
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
}
|
2024-03-26 19:07:16 -07:00
|
|
|
}
|
2025-03-13 09:38:22 -07:00
|
|
|
|
2025-03-19 12:18:19 -07:00
|
|
|
solids.push(
|
|
|
|
do_post_extrude(
|
|
|
|
sketch,
|
2025-04-26 21:21:26 -07:00
|
|
|
#[cfg(feature = "artifact-graph")]
|
2025-03-19 12:18:19 -07:00
|
|
|
id.into(),
|
2025-04-23 10:58:35 +12:00
|
|
|
TyF64::new(0.0, NumericType::mm()),
|
2025-03-20 20:42:41 -04:00
|
|
|
false,
|
2025-03-19 12:18:19 -07:00
|
|
|
&super::extrude::NamedCapTags {
|
|
|
|
start: tag_start.as_ref(),
|
|
|
|
end: tag_end.as_ref(),
|
|
|
|
},
|
|
|
|
exec_state,
|
|
|
|
&args,
|
|
|
|
)
|
|
|
|
.await?,
|
|
|
|
);
|
2024-03-26 19:07:16 -07:00
|
|
|
}
|
|
|
|
|
2025-03-17 17:57:26 +13:00
|
|
|
Ok(solids)
|
2024-03-26 19:07:16 -07:00
|
|
|
}
|