//! Standard library revolution surfaces. use anyhow::Result; use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, shared::Angle, ModelingCmd}; use kittycad_modeling_cmds::{self as kcmc, shared::Point3d}; use super::DEFAULT_TOLERANCE; use crate::{ errors::{KclError, KclErrorDetails}, execution::{ types::{PrimitiveType, RuntimeType}, ExecState, KclValue, Sketch, Solid, }, parsing::ast::types::TagNode, std::{axis_or_reference::Axis2dOrEdgeReference, extrude::do_post_extrude, Args}, }; /// Revolve a sketch or set of sketches around an axis. pub async fn revolve(exec_state: &mut ExecState, args: Args) -> Result { let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?; let axis = args.get_kw_arg_typed( "axis", &RuntimeType::Union(vec![ RuntimeType::Primitive(PrimitiveType::Edge), RuntimeType::Primitive(PrimitiveType::Axis2d), ]), exec_state, )?; let angle = args.get_kw_arg_opt("angle")?; 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 value = inner_revolve(sketches, axis, angle, tolerance, tag_start, tag_end, exec_state, args).await?; Ok(value.into()) } #[allow(clippy::too_many_arguments)] async fn inner_revolve( sketches: Vec, axis: Axis2dOrEdgeReference, angle: Option, tolerance: Option, tag_start: Option, tag_end: Option, exec_state: &mut ExecState, args: Args, ) -> Result, KclError> { if let Some(angle) = 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(&angle) || angle == 0.0 { return Err(KclError::Semantic(KclErrorDetails { message: format!("Expected angle to be between -360 and 360 and not 0, found `{}`", angle), source_ranges: vec![args.source_range], })); } } let angle = Angle::from_degrees(angle.unwrap_or(360.0)); let mut solids = Vec::new(); for sketch in &sketches { let id = exec_state.next_uuid(); match &axis { Axis2dOrEdgeReference::Axis { direction, origin } => { args.batch_modeling_cmd( id, ModelingCmd::from(mcmd::Revolve { angle, target: sketch.id.into(), axis: Point3d { x: direction[0], y: direction[1], z: 0.0, }, origin: Point3d { x: LengthUnit(origin[0]), y: LengthUnit(origin[1]), z: LengthUnit(0.0), }, tolerance: LengthUnit(tolerance.unwrap_or(DEFAULT_TOLERANCE)), axis_is_2d: true, }), ) .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, tolerance: LengthUnit(tolerance.unwrap_or(DEFAULT_TOLERANCE)), }), ) .await?; } } solids.push( do_post_extrude( sketch, id.into(), 0.0, false, &super::extrude::NamedCapTags { start: tag_start.as_ref(), end: tag_end.as_ref(), }, exec_state, &args, ) .await?, ); } Ok(solids) }