Use arrays for multiple geometry (#5770)
* Parse [T] instead of T[] for array types Signed-off-by: Nick Cameron <nrc@ncameron.org> * homogenous arrays, type coercion, remove solid set and sketch set, etc Signed-off-by: Nick Cameron <nrc@ncameron.org> --------- Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
@ -12,7 +12,10 @@ use validator::Validate;
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{ExecState, KclValue, Solid, SolidSet},
|
||||
execution::{
|
||||
kcl_value::{ArrayLen, RuntimeType},
|
||||
ExecState, KclValue, PrimitiveType, Solid,
|
||||
},
|
||||
std::Args,
|
||||
};
|
||||
|
||||
@ -38,8 +41,12 @@ struct AppearanceData {
|
||||
}
|
||||
|
||||
/// Set the appearance of a solid. This only works on solids, not sketches or individual paths.
|
||||
pub async fn appearance(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let solid_set: SolidSet = args.get_unlabeled_kw_arg("solidSet")?;
|
||||
pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let solids = args.get_unlabeled_kw_arg_typed(
|
||||
"solids",
|
||||
&RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
|
||||
exec_state,
|
||||
)?;
|
||||
|
||||
let color: String = args.get_kw_arg("color")?;
|
||||
let metalness: Option<f64> = args.get_kw_arg_opt("metalness")?;
|
||||
@ -66,7 +73,7 @@ pub async fn appearance(_exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
||||
}));
|
||||
}
|
||||
|
||||
let result = inner_appearance(solid_set, data.color, data.metalness, data.roughness, args).await?;
|
||||
let result = inner_appearance(solids, data.color, data.metalness, data.roughness, args).await?;
|
||||
Ok(result.into())
|
||||
}
|
||||
|
||||
@ -276,21 +283,19 @@ pub async fn appearance(_exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
solid_set = { docs = "The solid(s) whose appearance is being set" },
|
||||
solids = { docs = "The solid(s) whose appearance is being set" },
|
||||
color = { docs = "Color of the new material, a hex string like '#ff0000'"},
|
||||
metalness = { docs = "Metalness of the new material, a percentage like 95.7." },
|
||||
roughness = { docs = "Roughness of the new material, a percentage like 95.7." },
|
||||
}
|
||||
}]
|
||||
async fn inner_appearance(
|
||||
solid_set: SolidSet,
|
||||
solids: Vec<Solid>,
|
||||
color: String,
|
||||
metalness: Option<f64>,
|
||||
roughness: Option<f64>,
|
||||
args: Args,
|
||||
) -> Result<SolidSet, KclError> {
|
||||
let solids: Vec<Box<Solid>> = solid_set.into();
|
||||
|
||||
) -> Result<Vec<Solid>, KclError> {
|
||||
for solid in &solids {
|
||||
// Set the material properties.
|
||||
let rgb = rgba_simple::RGB::<f32>::from_hex(&color).map_err(|err| {
|
||||
@ -323,5 +328,5 @@ async fn inner_appearance(
|
||||
// I can't think of a use case for it.
|
||||
}
|
||||
|
||||
Ok(SolidSet::from(solids))
|
||||
Ok(solids)
|
||||
}
|
||||
|
@ -12,9 +12,9 @@ use serde::{Deserialize, Serialize};
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
kcl_value::{FunctionSource, NumericType},
|
||||
ExecState, ExecutorContext, ExtrudeSurface, Helix, KclObjectFields, KclValue, Metadata, Sketch, SketchSet,
|
||||
SketchSurface, Solid, SolidSet, TagIdentifier,
|
||||
kcl_value::{ArrayLen, FunctionSource, NumericType, RuntimeType},
|
||||
ExecState, ExecutorContext, ExtrudeSurface, Helix, KclObjectFields, KclValue, Metadata, PrimitiveType, Sketch,
|
||||
SketchSurface, Solid, TagIdentifier,
|
||||
},
|
||||
parsing::ast::types::TagNode,
|
||||
source_range::SourceRange,
|
||||
@ -233,9 +233,41 @@ impl Args {
|
||||
T::from_kcl_val(&arg.value).ok_or_else(|| {
|
||||
let expected_type_name = tynm::type_name::<T>();
|
||||
let actual_type_name = arg.value.human_friendly_type();
|
||||
let msg_base = format!("This function expected the input argument to be of type {expected_type_name} but it's actually of type {actual_type_name}");
|
||||
let suggestion = match (expected_type_name.as_str(), actual_type_name) {
|
||||
("SolidSet", "Sketch") => Some(
|
||||
let message = format!("This function expected the input argument to be of type {expected_type_name} but it's actually of type {actual_type_name}");
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
source_ranges: arg.source_ranges(),
|
||||
message,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the unlabeled keyword argument. If not set, returns Err. If it
|
||||
/// can't be converted to the given type, returns Err.
|
||||
pub(crate) fn get_unlabeled_kw_arg_typed<T>(
|
||||
&self,
|
||||
label: &str,
|
||||
ty: &RuntimeType,
|
||||
exec_state: &mut ExecState,
|
||||
) -> Result<T, KclError>
|
||||
where
|
||||
T: for<'a> FromKclValue<'a>,
|
||||
{
|
||||
let arg = self
|
||||
.unlabeled_kw_arg_unconverted()
|
||||
.ok_or(KclError::Semantic(KclErrorDetails {
|
||||
source_ranges: vec![self.source_range],
|
||||
message: format!("This function requires a value for the special unlabeled first parameter, '{label}'"),
|
||||
}))?;
|
||||
|
||||
let arg = arg.value.coerce(ty, exec_state).ok_or_else(|| {
|
||||
let actual_type_name = arg.value.human_friendly_type();
|
||||
let msg_base = format!(
|
||||
"This function expected the input argument to be {} but it's actually of type {actual_type_name}",
|
||||
ty.human_friendly_type(),
|
||||
);
|
||||
let suggestion = match (ty, actual_type_name) {
|
||||
(RuntimeType::Primitive(PrimitiveType::Solid), "Sketch")
|
||||
| (RuntimeType::Array(PrimitiveType::Solid, _), "Sketch") => Some(
|
||||
"You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`",
|
||||
),
|
||||
_ => None,
|
||||
@ -248,7 +280,10 @@ impl Args {
|
||||
source_ranges: arg.source_ranges(),
|
||||
message,
|
||||
})
|
||||
})
|
||||
})?;
|
||||
|
||||
// TODO unnecessary cloning
|
||||
Ok(T::from_kcl_val(&arg).unwrap())
|
||||
}
|
||||
|
||||
// Add a modeling command to the batch but don't fire it right away.
|
||||
@ -338,10 +373,10 @@ impl Args {
|
||||
|
||||
/// Flush just the fillets and chamfers for this specific SolidSet.
|
||||
#[allow(clippy::vec_box)]
|
||||
pub(crate) async fn flush_batch_for_solid_set(
|
||||
pub(crate) async fn flush_batch_for_solids(
|
||||
&self,
|
||||
exec_state: &mut ExecState,
|
||||
solids: Vec<Box<Solid>>,
|
||||
solids: Vec<Solid>,
|
||||
) -> Result<(), KclError> {
|
||||
// Make sure we don't traverse sketches more than once.
|
||||
let mut traversed_sketches = Vec::new();
|
||||
@ -510,12 +545,48 @@ impl Args {
|
||||
Ok((a.n, b.n, ty))
|
||||
}
|
||||
|
||||
pub(crate) fn get_sketches(&self) -> Result<(SketchSet, Sketch), KclError> {
|
||||
FromArgs::from_args(self, 0)
|
||||
pub(crate) fn get_sketches(&self, exec_state: &mut ExecState) -> Result<(Vec<Sketch>, Sketch), KclError> {
|
||||
let sarg = self.args[0]
|
||||
.value
|
||||
.coerce(&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::None), exec_state)
|
||||
.ok_or(KclError::Type(KclErrorDetails {
|
||||
message: format!(
|
||||
"Expected an array of sketches, found {}",
|
||||
self.args[0].value.human_friendly_type()
|
||||
),
|
||||
source_ranges: vec![self.source_range],
|
||||
}))?;
|
||||
let sketches = match sarg {
|
||||
KclValue::HomArray { value, .. } => value.iter().map(|v| v.as_sketch().unwrap().clone()).collect(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let sarg = self.args[1]
|
||||
.value
|
||||
.coerce(&RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)
|
||||
.ok_or(KclError::Type(KclErrorDetails {
|
||||
message: format!("Expected a sketch, found {}", self.args[1].value.human_friendly_type()),
|
||||
source_ranges: vec![self.source_range],
|
||||
}))?;
|
||||
let sketch = match sarg {
|
||||
KclValue::Sketch { value } => *value,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Ok((sketches, sketch))
|
||||
}
|
||||
|
||||
pub(crate) fn get_sketch(&self) -> Result<Sketch, KclError> {
|
||||
FromArgs::from_args(self, 0)
|
||||
pub(crate) fn get_sketch(&self, exec_state: &mut ExecState) -> Result<Sketch, KclError> {
|
||||
let sarg = self.args[0]
|
||||
.value
|
||||
.coerce(&RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)
|
||||
.ok_or(KclError::Type(KclErrorDetails {
|
||||
message: format!("Expected a sketch, found {}", self.args[0].value.human_friendly_type()),
|
||||
source_ranges: vec![self.source_range],
|
||||
}))?;
|
||||
match sarg {
|
||||
KclValue::Sketch { value } => Ok(*value),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_data<'a, T>(&'a self) -> Result<T, KclError>
|
||||
@ -536,18 +607,55 @@ impl Args {
|
||||
FromArgs::from_args(self, 0)
|
||||
}
|
||||
|
||||
pub(crate) fn get_data_and_sketch_set<'a, T>(&'a self) -> Result<(T, SketchSet), KclError>
|
||||
pub(crate) fn get_data_and_sketches<'a, T>(
|
||||
&'a self,
|
||||
exec_state: &mut ExecState,
|
||||
) -> Result<(T, Vec<Sketch>), KclError>
|
||||
where
|
||||
T: serde::de::DeserializeOwned + FromArgs<'a>,
|
||||
{
|
||||
FromArgs::from_args(self, 0)
|
||||
let data: T = FromArgs::from_args(self, 0)?;
|
||||
let sarg = self.args[1]
|
||||
.value
|
||||
.coerce(&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::None), exec_state)
|
||||
.ok_or(KclError::Type(KclErrorDetails {
|
||||
message: format!(
|
||||
"Expected an array of sketches for second argument, found {}",
|
||||
self.args[1].value.human_friendly_type()
|
||||
),
|
||||
source_ranges: vec![self.source_range],
|
||||
}))?;
|
||||
let sketches = match sarg {
|
||||
KclValue::HomArray { value, .. } => value.iter().map(|v| v.as_sketch().unwrap().clone()).collect(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Ok((data, sketches))
|
||||
}
|
||||
|
||||
pub(crate) fn get_data_and_sketch_and_tag<'a, T>(&'a self) -> Result<(T, Sketch, Option<TagNode>), KclError>
|
||||
pub(crate) fn get_data_and_sketch_and_tag<'a, T>(
|
||||
&'a self,
|
||||
exec_state: &mut ExecState,
|
||||
) -> Result<(T, Sketch, Option<TagNode>), KclError>
|
||||
where
|
||||
T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
|
||||
{
|
||||
FromArgs::from_args(self, 0)
|
||||
let data: T = FromArgs::from_args(self, 0)?;
|
||||
let sarg = self.args[1]
|
||||
.value
|
||||
.coerce(&RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)
|
||||
.ok_or(KclError::Type(KclErrorDetails {
|
||||
message: format!(
|
||||
"Expected a sketch for second argument, found {}",
|
||||
self.args[1].value.human_friendly_type()
|
||||
),
|
||||
source_ranges: vec![self.source_range],
|
||||
}))?;
|
||||
let sketch = match sarg {
|
||||
KclValue::Sketch { value } => *value,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let tag: Option<TagNode> = FromArgs::from_args(self, 2)?;
|
||||
Ok((data, sketch, tag))
|
||||
}
|
||||
|
||||
pub(crate) fn get_data_and_sketch_surface<'a, T>(&'a self) -> Result<(T, SketchSurface, Option<TagNode>), KclError>
|
||||
@ -557,11 +665,26 @@ impl Args {
|
||||
FromArgs::from_args(self, 0)
|
||||
}
|
||||
|
||||
pub(crate) fn get_data_and_solid<'a, T>(&'a self) -> Result<(T, Box<Solid>), KclError>
|
||||
pub(crate) fn get_data_and_solid<'a, T>(&'a self, exec_state: &mut ExecState) -> Result<(T, Box<Solid>), KclError>
|
||||
where
|
||||
T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
|
||||
{
|
||||
FromArgs::from_args(self, 0)
|
||||
let data: T = FromArgs::from_args(self, 0)?;
|
||||
let sarg = self.args[1]
|
||||
.value
|
||||
.coerce(&RuntimeType::Primitive(PrimitiveType::Solid), exec_state)
|
||||
.ok_or(KclError::Type(KclErrorDetails {
|
||||
message: format!(
|
||||
"Expected a solid for second argument, found {}",
|
||||
self.args[1].value.human_friendly_type()
|
||||
),
|
||||
source_ranges: vec![self.source_range],
|
||||
}))?;
|
||||
let solid = match sarg {
|
||||
KclValue::Solid { value } => value,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Ok((data, solid))
|
||||
}
|
||||
|
||||
pub(crate) fn get_tag_to_number_sketch(&self) -> Result<(TagIdentifier, f64, Sketch), KclError> {
|
||||
@ -1304,7 +1427,6 @@ impl_from_kcl_for_vec!(crate::execution::EdgeCut);
|
||||
impl_from_kcl_for_vec!(crate::execution::Metadata);
|
||||
impl_from_kcl_for_vec!(super::fillet::EdgeReference);
|
||||
impl_from_kcl_for_vec!(ExtrudeSurface);
|
||||
impl_from_kcl_for_vec!(Sketch);
|
||||
|
||||
impl<'a> FromKclValue<'a> for SourceRange {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
@ -1340,8 +1462,10 @@ impl<'a> FromKclValue<'a> for crate::execution::Solid {
|
||||
impl<'a> FromKclValue<'a> for crate::execution::SolidOrImportedGeometry {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
match arg {
|
||||
KclValue::Solid { value } => Some(Self::Solid(value.clone())),
|
||||
KclValue::Solids { value } => Some(Self::SolidSet(value.clone())),
|
||||
KclValue::Solid { value } => Some(Self::SolidSet(vec![(**value).clone()])),
|
||||
KclValue::HomArray { value, .. } => Some(Self::SolidSet(
|
||||
value.iter().map(|v| v.as_solid().unwrap().clone()).collect(),
|
||||
)),
|
||||
KclValue::ImportedGeometry(value) => Some(Self::ImportedGeometry(Box::new(value.clone()))),
|
||||
_ => None,
|
||||
}
|
||||
@ -1354,11 +1478,13 @@ impl<'a> FromKclValue<'a> for super::sketch::SketchData {
|
||||
let case1 = crate::execution::Plane::from_kcl_val;
|
||||
let case2 = super::sketch::PlaneData::from_kcl_val;
|
||||
let case3 = crate::execution::Solid::from_kcl_val;
|
||||
let case4 = <Vec<Solid>>::from_kcl_val;
|
||||
case1(arg)
|
||||
.map(Box::new)
|
||||
.map(Self::Plane)
|
||||
.or_else(|| case2(arg).map(Self::PlaneOrientation))
|
||||
.or_else(|| case3(arg).map(Box::new).map(Self::Solid))
|
||||
.or_else(|| case4(arg).map(|v| Box::new(v[0].clone())).map(Self::Solid))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1531,6 +1657,7 @@ impl<'a> FromKclValue<'a> for TyF64 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for Sketch {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
let KclValue::Sketch { value } = arg else {
|
||||
@ -1548,13 +1675,16 @@ impl<'a> FromKclValue<'a> for Helix {
|
||||
Some(value.as_ref().to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for SweepPath {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
let case1 = Sketch::from_kcl_val;
|
||||
let case2 = Helix::from_kcl_val;
|
||||
let case2 = <Vec<Sketch>>::from_kcl_val;
|
||||
let case3 = Helix::from_kcl_val;
|
||||
case1(arg)
|
||||
.map(Self::Sketch)
|
||||
.or_else(|| case2(arg).map(|arg0: Helix| Self::Helix(Box::new(arg0))))
|
||||
.or_else(|| case2(arg).map(|arg0: Vec<Sketch>| Self::Sketch(arg0[0].clone())))
|
||||
.or_else(|| case3(arg).map(|arg0: Helix| Self::Helix(Box::new(arg0))))
|
||||
}
|
||||
}
|
||||
impl<'a> FromKclValue<'a> for String {
|
||||
@ -1582,20 +1712,6 @@ impl<'a> FromKclValue<'a> for bool {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for SketchSet {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
match arg {
|
||||
KclValue::Sketch { value: sketch } => Some(SketchSet::from(sketch.to_owned())),
|
||||
KclValue::Sketches { value } => Some(SketchSet::from(value.to_owned())),
|
||||
KclValue::MixedArray { .. } => {
|
||||
let v: Option<Vec<Sketch>> = FromKclValue::from_kcl_val(arg);
|
||||
Some(SketchSet::Sketches(v?.iter().cloned().map(Box::new).collect()))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for Box<Solid> {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
let KclValue::Solid { value } = arg else {
|
||||
@ -1605,15 +1721,27 @@ impl<'a> FromKclValue<'a> for Box<Solid> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for &'a FunctionSource {
|
||||
impl<'a> FromKclValue<'a> for Vec<Solid> {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
arg.get_function()
|
||||
let KclValue::HomArray { value, .. } = arg else {
|
||||
return None;
|
||||
};
|
||||
value.iter().map(Solid::from_kcl_val).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for SolidSet {
|
||||
impl<'a> FromKclValue<'a> for Vec<Sketch> {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
arg.get_solid_set().ok()
|
||||
let KclValue::HomArray { value, .. } = arg else {
|
||||
return None;
|
||||
};
|
||||
value.iter().map(Sketch::from_kcl_val).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for &'a FunctionSource {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
arg.get_function()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,10 @@ use kittycad_modeling_cmds as kcmc;
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{ChamferSurface, EdgeCut, ExecState, ExtrudeSurface, GeoMeta, KclValue, Solid},
|
||||
execution::{
|
||||
kcl_value::RuntimeType, ChamferSurface, EdgeCut, ExecState, ExtrudeSurface, GeoMeta, KclValue, PrimitiveType,
|
||||
Solid,
|
||||
},
|
||||
parsing::ast::types::TagNode,
|
||||
std::{fillet::EdgeReference, Args},
|
||||
};
|
||||
@ -16,7 +19,7 @@ pub(crate) const DEFAULT_TOLERANCE: f64 = 0.0000001;
|
||||
|
||||
/// Create chamfers on tagged paths.
|
||||
pub async fn chamfer(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let solid = args.get_unlabeled_kw_arg("solid")?;
|
||||
let solid = args.get_unlabeled_kw_arg_typed("solid", &RuntimeType::Primitive(PrimitiveType::Solid), exec_state)?;
|
||||
let length = args.get_kw_arg("length")?;
|
||||
let tags = args.kw_arg_array_and_source::<EdgeReference>("tags")?;
|
||||
let tag = args.get_kw_arg_opt("tag")?;
|
||||
|
@ -19,18 +19,22 @@ use uuid::Uuid;
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
ArtifactId, ExecState, ExtrudeSurface, GeoMeta, KclValue, Path, Sketch, SketchSet, SketchSurface, Solid,
|
||||
SolidSet,
|
||||
kcl_value::{ArrayLen, RuntimeType},
|
||||
ArtifactId, ExecState, ExtrudeSurface, GeoMeta, KclValue, Path, PrimitiveType, Sketch, SketchSurface, Solid,
|
||||
},
|
||||
std::Args,
|
||||
};
|
||||
|
||||
/// Extrudes by a given amount.
|
||||
pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch_set = args.get_unlabeled_kw_arg("sketch_set")?;
|
||||
let sketches = args.get_unlabeled_kw_arg_typed(
|
||||
"sketches",
|
||||
&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
|
||||
exec_state,
|
||||
)?;
|
||||
let length = args.get_kw_arg("length")?;
|
||||
|
||||
let result = inner_extrude(sketch_set, length, exec_state, args).await?;
|
||||
let result = inner_extrude(sketches, length, exec_state, args).await?;
|
||||
|
||||
Ok(result.into())
|
||||
}
|
||||
@ -90,18 +94,17 @@ pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
sketch_set = { docs = "Which sketch or set of sketches should be extruded"},
|
||||
sketches = { docs = "Which sketch or sketches should be extruded"},
|
||||
length = { docs = "How far to extrude the given sketches"},
|
||||
}
|
||||
}]
|
||||
async fn inner_extrude(
|
||||
sketch_set: SketchSet,
|
||||
sketches: Vec<Sketch>,
|
||||
length: f64,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<SolidSet, KclError> {
|
||||
) -> Result<Vec<Solid>, KclError> {
|
||||
// Extrude the element(s).
|
||||
let sketches: Vec<Sketch> = sketch_set.into();
|
||||
let mut solids = Vec::new();
|
||||
for sketch in &sketches {
|
||||
let id = exec_state.next_uuid();
|
||||
@ -121,7 +124,7 @@ async fn inner_extrude(
|
||||
solids.push(do_post_extrude(sketch.clone(), id.into(), length, exec_state, args.clone()).await?);
|
||||
}
|
||||
|
||||
Ok(solids.into())
|
||||
Ok(solids)
|
||||
}
|
||||
|
||||
pub(crate) async fn do_post_extrude(
|
||||
@ -130,7 +133,7 @@ pub(crate) async fn do_post_extrude(
|
||||
length: f64,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Box<Solid>, KclError> {
|
||||
) -> Result<Solid, KclError> {
|
||||
// Bring the object to the front of the scene.
|
||||
// See: https://github.com/KittyCAD/modeling-app/issues/806
|
||||
args.batch_modeling_cmd(
|
||||
@ -269,7 +272,7 @@ pub(crate) async fn do_post_extrude(
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(Box::new(Solid {
|
||||
Ok(Solid {
|
||||
// Ok so you would think that the id would be the id of the solid,
|
||||
// that we passed in to the function, but it's actually the id of the
|
||||
// sketch.
|
||||
@ -283,7 +286,7 @@ pub(crate) async fn do_post_extrude(
|
||||
start_cap_id,
|
||||
end_cap_id,
|
||||
edge_cuts: vec![],
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -14,7 +14,10 @@ use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{EdgeCut, ExecState, ExtrudeSurface, FilletSurface, GeoMeta, KclValue, Solid, TagIdentifier},
|
||||
execution::{
|
||||
kcl_value::RuntimeType, EdgeCut, ExecState, ExtrudeSurface, FilletSurface, GeoMeta, KclValue, PrimitiveType,
|
||||
Solid, TagIdentifier,
|
||||
},
|
||||
parsing::ast::types::TagNode,
|
||||
settings::types::UnitLength,
|
||||
std::Args,
|
||||
@ -64,8 +67,7 @@ pub(super) fn validate_unique<T: Eq + std::hash::Hash>(tags: &[(T, SourceRange)]
|
||||
|
||||
/// Create fillets on tagged paths.
|
||||
pub async fn fillet(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
// Get all args:
|
||||
let solid = args.get_unlabeled_kw_arg("solid")?;
|
||||
let solid = args.get_unlabeled_kw_arg_typed("solid", &RuntimeType::Primitive(PrimitiveType::Solid), exec_state)?;
|
||||
let radius = args.get_kw_arg("radius")?;
|
||||
let tolerance = args.get_kw_arg_opt("tolerance")?;
|
||||
let tags = args.kw_arg_array_and_source::<EdgeReference>("tags")?;
|
||||
|
@ -196,7 +196,7 @@ pub struct HelixRevolutionsData {
|
||||
|
||||
/// Create a helix on a cylinder.
|
||||
pub async fn helix_revolutions(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, solid): (HelixRevolutionsData, Box<Solid>) = args.get_data_and_solid()?;
|
||||
let (data, solid): (HelixRevolutionsData, Box<Solid>) = args.get_data_and_solid(exec_state)?;
|
||||
|
||||
let value = inner_helix_revolutions(data, solid, exec_state, args).await?;
|
||||
Ok(KclValue::Solid { value })
|
||||
|
@ -9,7 +9,10 @@ use kittycad_modeling_cmds as kcmc;
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{ExecState, KclValue, Sketch, Solid},
|
||||
execution::{
|
||||
kcl_value::{ArrayLen, RuntimeType},
|
||||
ExecState, KclValue, PrimitiveType, Sketch, Solid,
|
||||
},
|
||||
std::{extrude::do_post_extrude, fillet::default_tolerance, Args},
|
||||
};
|
||||
|
||||
@ -17,7 +20,11 @@ const DEFAULT_V_DEGREE: u32 = 2;
|
||||
|
||||
/// Create a 3D surface or solid by interpolating between two or more sketches.
|
||||
pub async fn loft(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketches = args.get_unlabeled_kw_arg("sketches")?;
|
||||
let sketches = args.get_unlabeled_kw_arg_typed(
|
||||
"sketches",
|
||||
&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
|
||||
exec_state,
|
||||
)?;
|
||||
let v_degree: NonZeroU32 = args
|
||||
.get_kw_arg_opt("vDegree")?
|
||||
.unwrap_or(NonZeroU32::new(DEFAULT_V_DEGREE).unwrap());
|
||||
@ -159,5 +166,7 @@ async fn inner_loft(
|
||||
let mut sketch = sketches[0].clone();
|
||||
// Override its id with the loft id so we can get its faces later
|
||||
sketch.id = id;
|
||||
do_post_extrude(sketch, id.into(), 0.0, exec_state, args).await
|
||||
Ok(Box::new(
|
||||
do_post_extrude(sketch, id.into(), 0.0, exec_state, args).await?,
|
||||
))
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
errors::KclError,
|
||||
execution::{ExecState, KclValue, Sketch, SketchSet},
|
||||
execution::{ExecState, KclValue, Sketch},
|
||||
std::{axis_or_reference::Axis2dOrEdgeReference, Args},
|
||||
};
|
||||
|
||||
@ -26,7 +26,7 @@ pub struct Mirror2dData {
|
||||
///
|
||||
/// Only works on unclosed sketches for now.
|
||||
pub async fn mirror_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, sketch_set): (Mirror2dData, SketchSet) = args.get_data_and_sketch_set()?;
|
||||
let (data, sketch_set): (Mirror2dData, Vec<Sketch>) = args.get_data_and_sketches(exec_state)?;
|
||||
|
||||
let sketches = inner_mirror_2d(data, sketch_set, exec_state, args).await?;
|
||||
Ok(sketches.into())
|
||||
@ -103,14 +103,11 @@ pub async fn mirror_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
}]
|
||||
async fn inner_mirror_2d(
|
||||
data: Mirror2dData,
|
||||
sketch_set: SketchSet,
|
||||
sketches: Vec<Sketch>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Vec<Box<Sketch>>, KclError> {
|
||||
let starting_sketches = match sketch_set {
|
||||
SketchSet::Sketch(sketch) => vec![sketch],
|
||||
SketchSet::Sketches(sketches) => sketches,
|
||||
};
|
||||
) -> Result<Vec<Sketch>, KclError> {
|
||||
let starting_sketches = sketches;
|
||||
|
||||
if args.ctx.no_engine_commands().await {
|
||||
return Ok(starting_sketches);
|
||||
|
@ -20,9 +20,8 @@ use super::args::Arg;
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
kcl_value::{FunctionSource, NumericType},
|
||||
ExecState, Geometries, Geometry, KclObjectFields, KclValue, Point2d, Point3d, Sketch, SketchSet, Solid,
|
||||
SolidSet,
|
||||
kcl_value::{ArrayLen, FunctionSource, NumericType, RuntimeType},
|
||||
ExecState, Geometries, Geometry, KclObjectFields, KclValue, Point2d, Point3d, PrimitiveType, Sketch, Solid,
|
||||
},
|
||||
std::Args,
|
||||
ExecutorContext, SourceRange,
|
||||
@ -48,25 +47,32 @@ pub struct LinearPattern3dData {
|
||||
|
||||
/// Repeat some 3D solid, changing each repetition slightly.
|
||||
pub async fn pattern_transform(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let solid_set = args.get_unlabeled_kw_arg("solidSet")?;
|
||||
let solids = args.get_unlabeled_kw_arg_typed(
|
||||
"solids",
|
||||
&RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
|
||||
exec_state,
|
||||
)?;
|
||||
let instances: u32 = args.get_kw_arg("instances")?;
|
||||
let transform: &FunctionSource = args.get_kw_arg("transform")?;
|
||||
let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?;
|
||||
|
||||
let solids = inner_pattern_transform(solid_set, instances, transform, use_original, exec_state, &args).await?;
|
||||
Ok(KclValue::Solids { value: solids })
|
||||
let solids = inner_pattern_transform(solids, instances, transform, use_original, exec_state, &args).await?;
|
||||
Ok(solids.into())
|
||||
}
|
||||
|
||||
/// Repeat some 2D sketch, changing each repetition slightly.
|
||||
pub async fn pattern_transform_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch_set = args.get_unlabeled_kw_arg("sketchSet")?;
|
||||
let sketches = args.get_unlabeled_kw_arg_typed(
|
||||
"sketches",
|
||||
&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
|
||||
exec_state,
|
||||
)?;
|
||||
let instances: u32 = args.get_kw_arg("instances")?;
|
||||
let transform: &FunctionSource = args.get_kw_arg("transform")?;
|
||||
let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?;
|
||||
|
||||
let sketches =
|
||||
inner_pattern_transform_2d(sketch_set, instances, transform, use_original, exec_state, &args).await?;
|
||||
Ok(KclValue::Sketches { value: sketches })
|
||||
let sketches = inner_pattern_transform_2d(sketches, instances, transform, use_original, exec_state, &args).await?;
|
||||
Ok(sketches.into())
|
||||
}
|
||||
|
||||
/// Repeat a 3-dimensional solid, changing it each time.
|
||||
@ -258,20 +264,20 @@ pub async fn pattern_transform_2d(exec_state: &mut ExecState, args: Args) -> Res
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
solid_set = { docs = "The solid(s) to duplicate" },
|
||||
solids = { docs = "The solid(s) to duplicate" },
|
||||
instances = { docs = "The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect." },
|
||||
transform = { docs = "How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples." },
|
||||
use_original = { docs = "If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false." },
|
||||
}
|
||||
}]
|
||||
async fn inner_pattern_transform<'a>(
|
||||
solid_set: SolidSet,
|
||||
solids: Vec<Solid>,
|
||||
instances: u32,
|
||||
transform: &'a FunctionSource,
|
||||
use_original: Option<bool>,
|
||||
exec_state: &mut ExecState,
|
||||
args: &'a Args,
|
||||
) -> Result<Vec<Box<Solid>>, KclError> {
|
||||
) -> Result<Vec<Solid>, KclError> {
|
||||
// Build the vec of transforms, one for each repetition.
|
||||
let mut transform_vec = Vec::with_capacity(usize::try_from(instances).unwrap());
|
||||
if instances < 1 {
|
||||
@ -281,12 +287,12 @@ async fn inner_pattern_transform<'a>(
|
||||
}));
|
||||
}
|
||||
for i in 1..instances {
|
||||
let t = make_transform::<Box<Solid>>(i, transform, args.source_range, exec_state, &args.ctx).await?;
|
||||
let t = make_transform::<Solid>(i, transform, args.source_range, exec_state, &args.ctx).await?;
|
||||
transform_vec.push(t);
|
||||
}
|
||||
execute_pattern_transform(
|
||||
transform_vec,
|
||||
solid_set,
|
||||
solids,
|
||||
use_original.unwrap_or_default(),
|
||||
exec_state,
|
||||
args,
|
||||
@ -311,20 +317,20 @@ async fn inner_pattern_transform<'a>(
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
sketch_set = { docs = "The sketch(es) to duplicate" },
|
||||
sketches = { docs = "The sketch(es) to duplicate" },
|
||||
instances = { docs = "The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect." },
|
||||
transform = { docs = "How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples." },
|
||||
use_original = { docs = "If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false." },
|
||||
}
|
||||
}]
|
||||
async fn inner_pattern_transform_2d<'a>(
|
||||
sketch_set: SketchSet,
|
||||
sketches: Vec<Sketch>,
|
||||
instances: u32,
|
||||
transform: &'a FunctionSource,
|
||||
use_original: Option<bool>,
|
||||
exec_state: &mut ExecState,
|
||||
args: &'a Args,
|
||||
) -> Result<Vec<Box<Sketch>>, KclError> {
|
||||
) -> Result<Vec<Sketch>, KclError> {
|
||||
// Build the vec of transforms, one for each repetition.
|
||||
let mut transform_vec = Vec::with_capacity(usize::try_from(instances).unwrap());
|
||||
if instances < 1 {
|
||||
@ -334,12 +340,12 @@ async fn inner_pattern_transform_2d<'a>(
|
||||
}));
|
||||
}
|
||||
for i in 1..instances {
|
||||
let t = make_transform::<Box<Sketch>>(i, transform, args.source_range, exec_state, &args.ctx).await?;
|
||||
let t = make_transform::<Sketch>(i, transform, args.source_range, exec_state, &args.ctx).await?;
|
||||
transform_vec.push(t);
|
||||
}
|
||||
execute_pattern_transform(
|
||||
transform_vec,
|
||||
sketch_set,
|
||||
sketches,
|
||||
use_original.unwrap_or_default(),
|
||||
exec_state,
|
||||
args,
|
||||
@ -611,8 +617,8 @@ trait GeometryTrait: Clone {
|
||||
async fn flush_batch(args: &Args, exec_state: &mut ExecState, set: Self::Set) -> Result<(), KclError>;
|
||||
}
|
||||
|
||||
impl GeometryTrait for Box<Sketch> {
|
||||
type Set = SketchSet;
|
||||
impl GeometryTrait for Sketch {
|
||||
type Set = Vec<Sketch>;
|
||||
fn set_id(&mut self, id: Uuid) {
|
||||
self.id = id;
|
||||
}
|
||||
@ -632,8 +638,8 @@ impl GeometryTrait for Box<Sketch> {
|
||||
}
|
||||
}
|
||||
|
||||
impl GeometryTrait for Box<Solid> {
|
||||
type Set = SolidSet;
|
||||
impl GeometryTrait for Solid {
|
||||
type Set = Vec<Solid>;
|
||||
fn set_id(&mut self, id: Uuid) {
|
||||
self.id = id;
|
||||
}
|
||||
@ -651,7 +657,7 @@ impl GeometryTrait for Box<Solid> {
|
||||
}
|
||||
|
||||
async fn flush_batch(args: &Args, exec_state: &mut ExecState, solid_set: Self::Set) -> Result<(), KclError> {
|
||||
args.flush_batch_for_solid_set(exec_state, solid_set.into()).await
|
||||
args.flush_batch_for_solids(exec_state, solid_set).await
|
||||
}
|
||||
}
|
||||
|
||||
@ -690,7 +696,11 @@ mod tests {
|
||||
|
||||
/// A linear pattern on a 2D sketch.
|
||||
pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch_set: SketchSet = args.get_unlabeled_kw_arg("sketchSet")?;
|
||||
let sketches = args.get_unlabeled_kw_arg_typed(
|
||||
"sketches",
|
||||
&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
|
||||
exec_state,
|
||||
)?;
|
||||
let instances: u32 = args.get_kw_arg("instances")?;
|
||||
let distance: f64 = args.get_kw_arg("distance")?;
|
||||
let axis: [f64; 2] = args.get_kw_arg("axis")?;
|
||||
@ -705,8 +715,7 @@ pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result
|
||||
}));
|
||||
}
|
||||
|
||||
let sketches =
|
||||
inner_pattern_linear_2d(sketch_set, instances, distance, axis, use_original, exec_state, args).await?;
|
||||
let sketches = inner_pattern_linear_2d(sketches, instances, distance, axis, use_original, exec_state, args).await?;
|
||||
Ok(sketches.into())
|
||||
}
|
||||
|
||||
@ -729,7 +738,7 @@ pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
sketch_set = { docs = "The sketch(es) to duplicate" },
|
||||
sketches = { docs = "The sketch(es) to duplicate" },
|
||||
instances = { docs = "The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect." },
|
||||
distance = { docs = "Distance between each repetition. Also known as 'spacing'."},
|
||||
axis = { docs = "The axis of the pattern. A 2D vector." },
|
||||
@ -737,14 +746,14 @@ pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result
|
||||
}
|
||||
}]
|
||||
async fn inner_pattern_linear_2d(
|
||||
sketch_set: SketchSet,
|
||||
sketches: Vec<Sketch>,
|
||||
instances: u32,
|
||||
distance: f64,
|
||||
axis: [f64; 2],
|
||||
use_original: Option<bool>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Vec<Box<Sketch>>, KclError> {
|
||||
) -> Result<Vec<Sketch>, KclError> {
|
||||
let [x, y] = axis;
|
||||
let axis_len = f64::sqrt(x * x + y * y);
|
||||
let normalized_axis = kcmc::shared::Point2d::from([x / axis_len, y / axis_len]);
|
||||
@ -760,7 +769,7 @@ async fn inner_pattern_linear_2d(
|
||||
.collect();
|
||||
execute_pattern_transform(
|
||||
transforms,
|
||||
sketch_set,
|
||||
sketches,
|
||||
use_original.unwrap_or_default(),
|
||||
exec_state,
|
||||
&args,
|
||||
@ -770,7 +779,11 @@ async fn inner_pattern_linear_2d(
|
||||
|
||||
/// A linear pattern on a 3D model.
|
||||
pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let solid_set: SolidSet = args.get_unlabeled_kw_arg("solidSet")?;
|
||||
let solids = args.get_unlabeled_kw_arg_typed(
|
||||
"solids",
|
||||
&RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
|
||||
exec_state,
|
||||
)?;
|
||||
let instances: u32 = args.get_kw_arg("instances")?;
|
||||
let distance: f64 = args.get_kw_arg("distance")?;
|
||||
let axis: [f64; 3] = args.get_kw_arg("axis")?;
|
||||
@ -785,7 +798,7 @@ pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result
|
||||
}));
|
||||
}
|
||||
|
||||
let solids = inner_pattern_linear_3d(solid_set, instances, distance, axis, use_original, exec_state, args).await?;
|
||||
let solids = inner_pattern_linear_3d(solids, instances, distance, axis, use_original, exec_state, args).await?;
|
||||
Ok(solids.into())
|
||||
}
|
||||
|
||||
@ -866,7 +879,7 @@ pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
solid_set = { docs = "The solid(s) to duplicate" },
|
||||
solids = { docs = "The solid(s) to duplicate" },
|
||||
instances = { docs = "The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect." },
|
||||
distance = { docs = "Distance between each repetition. Also known as 'spacing'."},
|
||||
axis = { docs = "The axis of the pattern. A 2D vector." },
|
||||
@ -874,14 +887,14 @@ pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result
|
||||
}
|
||||
}]
|
||||
async fn inner_pattern_linear_3d(
|
||||
solid_set: SolidSet,
|
||||
solids: Vec<Solid>,
|
||||
instances: u32,
|
||||
distance: f64,
|
||||
axis: [f64; 3],
|
||||
use_original: Option<bool>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Vec<Box<Solid>>, KclError> {
|
||||
) -> Result<Vec<Solid>, KclError> {
|
||||
let [x, y, z] = axis;
|
||||
let axis_len = f64::sqrt(x * x + y * y + z * z);
|
||||
let normalized_axis = kcmc::shared::Point3d::from([x / axis_len, y / axis_len, z / axis_len]);
|
||||
@ -895,14 +908,7 @@ async fn inner_pattern_linear_3d(
|
||||
}]
|
||||
})
|
||||
.collect();
|
||||
execute_pattern_transform(
|
||||
transforms,
|
||||
solid_set,
|
||||
use_original.unwrap_or_default(),
|
||||
exec_state,
|
||||
&args,
|
||||
)
|
||||
.await
|
||||
execute_pattern_transform(transforms, solids, use_original.unwrap_or_default(), exec_state, &args).await
|
||||
}
|
||||
|
||||
/// Data for a circular pattern on a 2D sketch.
|
||||
@ -1022,7 +1028,11 @@ impl CircularPattern {
|
||||
|
||||
/// A circular pattern on a 2D sketch.
|
||||
pub async fn pattern_circular_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch_set: SketchSet = args.get_unlabeled_kw_arg("sketchSet")?;
|
||||
let sketches = args.get_unlabeled_kw_arg_typed(
|
||||
"sketches",
|
||||
&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
|
||||
exec_state,
|
||||
)?;
|
||||
let instances: u32 = args.get_kw_arg("instances")?;
|
||||
let center: [f64; 2] = args.get_kw_arg("center")?;
|
||||
let arc_degrees: f64 = args.get_kw_arg("arcDegrees")?;
|
||||
@ -1030,7 +1040,7 @@ pub async fn pattern_circular_2d(exec_state: &mut ExecState, args: Args) -> Resu
|
||||
let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?;
|
||||
|
||||
let sketches = inner_pattern_circular_2d(
|
||||
sketch_set,
|
||||
sketches,
|
||||
instances,
|
||||
center,
|
||||
arc_degrees,
|
||||
@ -1079,7 +1089,7 @@ pub async fn pattern_circular_2d(exec_state: &mut ExecState, args: Args) -> Resu
|
||||
}]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn inner_pattern_circular_2d(
|
||||
sketch_set: SketchSet,
|
||||
sketch_set: Vec<Sketch>,
|
||||
instances: u32,
|
||||
center: [f64; 2],
|
||||
arc_degrees: f64,
|
||||
@ -1087,8 +1097,8 @@ async fn inner_pattern_circular_2d(
|
||||
use_original: Option<bool>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Vec<Box<Sketch>>, KclError> {
|
||||
let starting_sketches: Vec<Box<Sketch>> = sketch_set.into();
|
||||
) -> Result<Vec<Sketch>, KclError> {
|
||||
let starting_sketches = sketch_set;
|
||||
|
||||
if args.ctx.context_type == crate::execution::ContextType::Mock {
|
||||
return Ok(starting_sketches);
|
||||
@ -1126,7 +1136,11 @@ async fn inner_pattern_circular_2d(
|
||||
|
||||
/// A circular pattern on a 3D model.
|
||||
pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let solid_set: SolidSet = args.get_unlabeled_kw_arg("solidSet")?;
|
||||
let solids = args.get_unlabeled_kw_arg_typed(
|
||||
"solids",
|
||||
&RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
|
||||
exec_state,
|
||||
)?;
|
||||
// The number of total instances. Must be greater than or equal to 1.
|
||||
// This includes the original entity. For example, if instances is 2,
|
||||
// there will be two copies -- the original, and one new copy.
|
||||
@ -1145,7 +1159,7 @@ pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Resu
|
||||
let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?;
|
||||
|
||||
let solids = inner_pattern_circular_3d(
|
||||
solid_set,
|
||||
solids,
|
||||
instances,
|
||||
axis,
|
||||
center,
|
||||
@ -1183,7 +1197,7 @@ pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Resu
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
solid_set = { docs = "Which solid(s) to pattern" },
|
||||
solids = { docs = "Which solid(s) to pattern" },
|
||||
instances = { docs = "The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect."},
|
||||
axis = { docs = "The axis around which to make the pattern. This is a 3D vector"},
|
||||
center = { docs = "The center about which to make the pattern. This is a 3D vector."},
|
||||
@ -1194,7 +1208,7 @@ pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Resu
|
||||
}]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn inner_pattern_circular_3d(
|
||||
solid_set: SolidSet,
|
||||
solids: Vec<Solid>,
|
||||
instances: u32,
|
||||
axis: [f64; 3],
|
||||
center: [f64; 3],
|
||||
@ -1203,14 +1217,13 @@ async fn inner_pattern_circular_3d(
|
||||
use_original: Option<bool>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Vec<Box<Solid>>, KclError> {
|
||||
) -> Result<Vec<Solid>, KclError> {
|
||||
// Flush the batch for our fillets/chamfers if there are any.
|
||||
// If we do not flush these, then you won't be able to pattern something with fillets.
|
||||
// Flush just the fillets/chamfers that apply to these solids.
|
||||
args.flush_batch_for_solid_set(exec_state, solid_set.clone().into())
|
||||
.await?;
|
||||
args.flush_batch_for_solids(exec_state, solids.clone()).await?;
|
||||
|
||||
let starting_solids: Vec<Box<Solid>> = solid_set.into();
|
||||
let starting_solids = solids;
|
||||
|
||||
if args.ctx.context_type == crate::execution::ContextType::Mock {
|
||||
return Ok(starting_solids);
|
||||
|
@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{ExecState, KclValue, Sketch, SketchSet, SolidSet},
|
||||
execution::{ExecState, KclValue, Sketch, Solid},
|
||||
std::{axis_or_reference::Axis2dOrEdgeReference, extrude::do_post_extrude, fillet::default_tolerance, Args},
|
||||
};
|
||||
|
||||
@ -30,9 +30,9 @@ pub struct RevolveData {
|
||||
|
||||
/// Revolve a sketch or set of sketches around an axis.
|
||||
pub async fn revolve(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, sketch_set): (RevolveData, SketchSet) = args.get_data_and_sketch_set()?;
|
||||
let (data, sketches): (RevolveData, _) = args.get_data_and_sketches(exec_state)?;
|
||||
|
||||
let value = inner_revolve(data, sketch_set, exec_state, args).await?;
|
||||
let value = inner_revolve(data, sketches, exec_state, args).await?;
|
||||
Ok(value.into())
|
||||
}
|
||||
|
||||
@ -206,10 +206,10 @@ pub async fn revolve(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
}]
|
||||
async fn inner_revolve(
|
||||
data: RevolveData,
|
||||
sketch_set: SketchSet,
|
||||
sketches: Vec<Sketch>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<SolidSet, KclError> {
|
||||
) -> Result<Vec<Solid>, KclError> {
|
||||
if let Some(angle) = data.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
|
||||
@ -224,7 +224,6 @@ async fn inner_revolve(
|
||||
|
||||
let angle = Angle::from_degrees(data.angle.unwrap_or(360.0));
|
||||
|
||||
let sketches: Vec<Sketch> = sketch_set.into();
|
||||
let mut solids = Vec::new();
|
||||
for sketch in &sketches {
|
||||
let id = exec_state.next_uuid();
|
||||
@ -263,5 +262,5 @@ async fn inner_revolve(
|
||||
solids.push(do_post_extrude(sketch.clone(), id.into(), 0.0, exec_state, args.clone()).await?);
|
||||
}
|
||||
|
||||
Ok(solids.into())
|
||||
Ok(solids)
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use kittycad_modeling_cmds::shared::Angle;
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{ExecState, KclValue, Point2d, Sketch, TagIdentifier},
|
||||
execution::{kcl_value::RuntimeType, ExecState, KclValue, Point2d, PrimitiveType, Sketch, TagIdentifier},
|
||||
std::{utils::between, Args},
|
||||
};
|
||||
|
||||
@ -282,8 +282,9 @@ fn inner_segment_start_y(tag: &TagIdentifier, exec_state: &mut ExecState, args:
|
||||
Ok(path.get_from()[1])
|
||||
}
|
||||
/// Returns the last segment of x.
|
||||
pub async fn last_segment_x(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch = args.get_unlabeled_kw_arg("sketch")?;
|
||||
pub async fn last_segment_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch =
|
||||
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||
let result = inner_last_segment_x(sketch, args.clone())?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
@ -327,8 +328,9 @@ fn inner_last_segment_x(sketch: Sketch, args: Args) -> Result<f64, KclError> {
|
||||
}
|
||||
|
||||
/// Returns the last segment of y.
|
||||
pub async fn last_segment_y(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch = args.get_unlabeled_kw_arg("sketch")?;
|
||||
pub async fn last_segment_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch =
|
||||
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||
let result = inner_last_segment_y(sketch, args.clone())?;
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
|
@ -7,17 +7,24 @@ use kittycad_modeling_cmds as kcmc;
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{ExecState, KclValue, Solid, SolidSet},
|
||||
execution::{
|
||||
kcl_value::{ArrayLen, RuntimeType},
|
||||
ExecState, KclValue, PrimitiveType, Solid,
|
||||
},
|
||||
std::{sketch::FaceTag, Args},
|
||||
};
|
||||
|
||||
/// Create a shell.
|
||||
pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let solid_set = args.get_unlabeled_kw_arg("solidSet")?;
|
||||
let solids = args.get_unlabeled_kw_arg_typed(
|
||||
"solids",
|
||||
&RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
|
||||
exec_state,
|
||||
)?;
|
||||
let thickness = args.get_kw_arg("thickness")?;
|
||||
let faces = args.get_kw_arg("faces")?;
|
||||
|
||||
let result = inner_shell(solid_set, thickness, faces, exec_state, args).await?;
|
||||
let result = inner_shell(solids, thickness, faces, exec_state, args).await?;
|
||||
Ok(result.into())
|
||||
}
|
||||
|
||||
@ -173,18 +180,18 @@ pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
solid_set = { docs = "Which solid (or solids) to shell out"},
|
||||
solids = { docs = "Which solid (or solids) to shell out"},
|
||||
thickness = {docs = "The thickness of the shell"},
|
||||
faces = {docs = "The faces you want removed"},
|
||||
}
|
||||
}]
|
||||
async fn inner_shell(
|
||||
solid_set: SolidSet,
|
||||
solids: Vec<Solid>,
|
||||
thickness: f64,
|
||||
faces: Vec<FaceTag>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<SolidSet, KclError> {
|
||||
) -> Result<Vec<Solid>, KclError> {
|
||||
if faces.is_empty() {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
message: "You must shell at least one face".to_string(),
|
||||
@ -192,7 +199,6 @@ async fn inner_shell(
|
||||
}));
|
||||
}
|
||||
|
||||
let solids: Vec<Box<Solid>> = solid_set.clone().into();
|
||||
if solids.is_empty() {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
message: "You must shell at least one solid".to_string(),
|
||||
@ -204,7 +210,7 @@ async fn inner_shell(
|
||||
for solid in &solids {
|
||||
// Flush the batch for our fillets/chamfers if there are any.
|
||||
// If we do not do these for sketch on face, things will fail with face does not exist.
|
||||
args.flush_batch_for_solid_set(exec_state, solid.clone().into()).await?;
|
||||
args.flush_batch_for_solids(exec_state, vec![solid.clone()]).await?;
|
||||
|
||||
for tag in &faces {
|
||||
let extrude_plane_id = tag.get_face_id(solid, exec_state, &args, false).await?;
|
||||
@ -241,12 +247,12 @@ async fn inner_shell(
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(solid_set)
|
||||
Ok(solids)
|
||||
}
|
||||
|
||||
/// Make the inside of a 3D object hollow.
|
||||
pub async fn hollow(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (thickness, solid): (f64, Box<Solid>) = args.get_data_and_solid()?;
|
||||
let (thickness, solid): (f64, Box<Solid>) = args.get_data_and_solid(exec_state)?;
|
||||
|
||||
let value = inner_hollow(thickness, solid, exec_state, args).await?;
|
||||
Ok(KclValue::Solid { value })
|
||||
@ -314,7 +320,7 @@ async fn inner_hollow(
|
||||
) -> Result<Box<Solid>, KclError> {
|
||||
// Flush the batch for our fillets/chamfers if there are any.
|
||||
// If we do not do these for sketch on face, things will fail with face does not exist.
|
||||
args.flush_batch_for_solid_set(exec_state, solid.clone().into()).await?;
|
||||
args.flush_batch_for_solids(exec_state, vec![(*solid).clone()]).await?;
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.next_uuid(),
|
||||
|
@ -11,11 +11,13 @@ use parse_display::{Display, FromStr};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::execution::kcl_value::RuntimeType;
|
||||
use crate::execution::PrimitiveType;
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
Artifact, ArtifactId, BasePath, CodeRef, ExecState, Face, GeoMeta, KclValue, Path, Plane, Point2d, Point3d,
|
||||
Sketch, SketchSet, SketchSurface, Solid, StartSketchOnFace, StartSketchOnPlane, TagEngineInfo, TagIdentifier,
|
||||
Sketch, SketchSurface, Solid, StartSketchOnFace, StartSketchOnPlane, TagEngineInfo, TagIdentifier,
|
||||
},
|
||||
parsing::ast::types::TagNode,
|
||||
std::{
|
||||
@ -96,7 +98,8 @@ pub const NEW_TAG_KW: &str = "tag";
|
||||
/// Draw a line to a point.
|
||||
pub async fn line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
// let (to, sketch, tag): ([f64; 2], Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
let sketch = args.get_unlabeled_kw_arg("sketch")?;
|
||||
let sketch =
|
||||
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||
let end = args.get_kw_arg_opt("end")?;
|
||||
let end_absolute = args.get_kw_arg_opt("endAbsolute")?;
|
||||
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
|
||||
@ -263,7 +266,8 @@ async fn straight_line(
|
||||
|
||||
/// Draw a line on the x-axis.
|
||||
pub async fn x_line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch = args.get_unlabeled_kw_arg("sketch")?;
|
||||
let sketch =
|
||||
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||
let length = args.get_kw_arg_opt("length")?;
|
||||
let end_absolute = args.get_kw_arg_opt("endAbsolute")?;
|
||||
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
|
||||
@ -331,7 +335,8 @@ async fn inner_x_line(
|
||||
|
||||
/// Draw a line on the y-axis.
|
||||
pub async fn y_line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch = args.get_unlabeled_kw_arg("sketch")?;
|
||||
let sketch =
|
||||
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||
let length = args.get_kw_arg_opt("length")?;
|
||||
let end_absolute = args.get_kw_arg_opt("endAbsolute")?;
|
||||
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
|
||||
@ -410,7 +415,8 @@ pub enum AngledLineData {
|
||||
|
||||
/// Draw an angled line.
|
||||
pub async fn angled_line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, sketch, tag): (AngledLineData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
let (data, sketch, tag): (AngledLineData, Sketch, Option<TagNode>) =
|
||||
args.get_data_and_sketch_and_tag(exec_state)?;
|
||||
|
||||
let new_sketch = inner_angled_line(data, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::Sketch {
|
||||
@ -498,7 +504,8 @@ async fn inner_angled_line(
|
||||
|
||||
/// Draw an angled line of a given x length.
|
||||
pub async fn angled_line_of_x_length(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, sketch, tag): (AngledLineData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
let (data, sketch, tag): (AngledLineData, Sketch, Option<TagNode>) =
|
||||
args.get_data_and_sketch_and_tag(exec_state)?;
|
||||
|
||||
let new_sketch = inner_angled_line_of_x_length(data, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::Sketch {
|
||||
@ -568,7 +575,8 @@ pub struct AngledLineToData {
|
||||
|
||||
/// Draw an angled line to a given x coordinate.
|
||||
pub async fn angled_line_to_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, sketch, tag): (AngledLineToData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
let (data, sketch, tag): (AngledLineToData, Sketch, Option<TagNode>) =
|
||||
args.get_data_and_sketch_and_tag(exec_state)?;
|
||||
|
||||
let new_sketch = inner_angled_line_to_x(data, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::Sketch {
|
||||
@ -632,7 +640,8 @@ async fn inner_angled_line_to_x(
|
||||
|
||||
/// Draw an angled line of a given y length.
|
||||
pub async fn angled_line_of_y_length(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, sketch, tag): (AngledLineData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
let (data, sketch, tag): (AngledLineData, Sketch, Option<TagNode>) =
|
||||
args.get_data_and_sketch_and_tag(exec_state)?;
|
||||
|
||||
let new_sketch = inner_angled_line_of_y_length(data, sketch, tag, exec_state, args).await?;
|
||||
|
||||
@ -694,7 +703,8 @@ async fn inner_angled_line_of_y_length(
|
||||
|
||||
/// Draw an angled line to a given y coordinate.
|
||||
pub async fn angled_line_to_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, sketch, tag): (AngledLineToData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
let (data, sketch, tag): (AngledLineToData, Sketch, Option<TagNode>) =
|
||||
args.get_data_and_sketch_and_tag(exec_state)?;
|
||||
|
||||
let new_sketch = inner_angled_line_to_y(data, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::Sketch {
|
||||
@ -773,7 +783,7 @@ pub struct AngledLineThatIntersectsData {
|
||||
/// Draw an angled line that intersects with a given line.
|
||||
pub async fn angled_line_that_intersects(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, sketch, tag): (AngledLineThatIntersectsData, Sketch, Option<TagNode>) =
|
||||
args.get_data_and_sketch_and_tag()?;
|
||||
args.get_data_and_sketch_and_tag(exec_state)?;
|
||||
let new_sketch = inner_angled_line_that_intersects(data, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
@ -1202,7 +1212,7 @@ pub(crate) async fn inner_start_profile_at(
|
||||
SketchSurface::Face(face) => {
|
||||
// Flush the batch for our fillets/chamfers if there are any.
|
||||
// If we do not do these for sketch on face, things will fail with face does not exist.
|
||||
args.flush_batch_for_solid_set(exec_state, face.solid.clone().into())
|
||||
args.flush_batch_for_solids(exec_state, vec![(*face.solid).clone()])
|
||||
.await?;
|
||||
}
|
||||
SketchSurface::Plane(plane) if !plane.is_standard() => {
|
||||
@ -1301,8 +1311,8 @@ pub(crate) async fn inner_start_profile_at(
|
||||
}
|
||||
|
||||
/// Returns the X component of the sketch profile start point.
|
||||
pub async fn profile_start_x(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch: Sketch = args.get_sketch()?;
|
||||
pub async fn profile_start_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch: Sketch = args.get_sketch(exec_state)?;
|
||||
let ty = sketch.units.into();
|
||||
let x = inner_profile_start_x(sketch)?;
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(x, ty)))
|
||||
@ -1326,8 +1336,8 @@ pub(crate) fn inner_profile_start_x(sketch: Sketch) -> Result<f64, KclError> {
|
||||
}
|
||||
|
||||
/// Returns the Y component of the sketch profile start point.
|
||||
pub async fn profile_start_y(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch: Sketch = args.get_sketch()?;
|
||||
pub async fn profile_start_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch: Sketch = args.get_sketch(exec_state)?;
|
||||
let ty = sketch.units.into();
|
||||
let x = inner_profile_start_y(sketch)?;
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(x, ty)))
|
||||
@ -1350,8 +1360,8 @@ pub(crate) fn inner_profile_start_y(sketch: Sketch) -> Result<f64, KclError> {
|
||||
}
|
||||
|
||||
/// Returns the sketch profile start point.
|
||||
pub async fn profile_start(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch: Sketch = args.get_sketch()?;
|
||||
pub async fn profile_start(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch: Sketch = args.get_sketch(exec_state)?;
|
||||
let ty = sketch.units.into();
|
||||
let point = inner_profile_start(sketch)?;
|
||||
Ok(KclValue::from_point2d(point, ty, args.into()))
|
||||
@ -1378,7 +1388,8 @@ pub(crate) fn inner_profile_start(sketch: Sketch) -> Result<[f64; 2], KclError>
|
||||
|
||||
/// Close the current sketch.
|
||||
pub async fn close(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch = args.get_unlabeled_kw_arg("sketch")?;
|
||||
let sketch =
|
||||
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
|
||||
let new_sketch = inner_close(sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::Sketch {
|
||||
@ -1495,7 +1506,7 @@ pub struct ArcToData {
|
||||
|
||||
/// Draw an arc.
|
||||
pub async fn arc(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, sketch, tag): (ArcData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
let (data, sketch, tag): (ArcData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag(exec_state)?;
|
||||
|
||||
let new_sketch = inner_arc(data, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::Sketch {
|
||||
@ -1608,7 +1619,7 @@ pub(crate) async fn inner_arc(
|
||||
|
||||
/// Draw a three point arc.
|
||||
pub async fn arc_to(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, sketch, tag): (ArcToData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
let (data, sketch, tag): (ArcToData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag(exec_state)?;
|
||||
|
||||
let new_sketch = inner_arc_to(data, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::Sketch {
|
||||
@ -1744,7 +1755,8 @@ pub enum TangentialArcData {
|
||||
|
||||
/// Draw a tangential arc.
|
||||
pub async fn tangential_arc(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, sketch, tag): (TangentialArcData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
let (data, sketch, tag): (TangentialArcData, Sketch, Option<TagNode>) =
|
||||
args.get_data_and_sketch_and_tag(exec_state)?;
|
||||
|
||||
let new_sketch = inner_tangential_arc(data, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::Sketch {
|
||||
@ -1871,7 +1883,7 @@ fn tan_arc_to(sketch: &Sketch, to: &[f64; 2]) -> ModelingCmd {
|
||||
|
||||
/// Draw a tangential arc to a specific point.
|
||||
pub async fn tangential_arc_to(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (to, sketch, tag): ([f64; 2], Sketch, Option<TagNode>) = super::args::FromArgs::from_args(&args, 0)?;
|
||||
let (to, sketch, tag): ([f64; 2], Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag(exec_state)?;
|
||||
|
||||
let new_sketch = inner_tangential_arc_to(to, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::Sketch {
|
||||
@ -1881,7 +1893,7 @@ pub async fn tangential_arc_to(exec_state: &mut ExecState, args: Args) -> Result
|
||||
|
||||
/// Draw a tangential arc to point some distance away..
|
||||
pub async fn tangential_arc_to_relative(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (delta, sketch, tag): ([f64; 2], Sketch, Option<TagNode>) = super::args::FromArgs::from_args(&args, 0)?;
|
||||
let (delta, sketch, tag): ([f64; 2], Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag(exec_state)?;
|
||||
|
||||
let new_sketch = inner_tangential_arc_to_relative(delta, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::Sketch {
|
||||
@ -2055,7 +2067,7 @@ pub struct BezierData {
|
||||
|
||||
/// Draw a bezier curve.
|
||||
pub async fn bezier_curve(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, sketch, tag): (BezierData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
let (data, sketch, tag): (BezierData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag(exec_state)?;
|
||||
|
||||
let new_sketch = inner_bezier_curve(data, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::Sketch {
|
||||
@ -2138,7 +2150,7 @@ async fn inner_bezier_curve(
|
||||
|
||||
/// Use a sketch to cut a hole in another sketch.
|
||||
pub async fn hole(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (hole_sketch, sketch): (SketchSet, Sketch) = args.get_sketches()?;
|
||||
let (hole_sketch, sketch): (Vec<Sketch>, Sketch) = args.get_sketches(exec_state)?;
|
||||
|
||||
let new_sketch = inner_hole(hole_sketch, sketch, exec_state, args).await?;
|
||||
Ok(KclValue::Sketch {
|
||||
@ -2182,13 +2194,12 @@ pub async fn hole(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
feature_tree_operation = true,
|
||||
}]
|
||||
async fn inner_hole(
|
||||
hole_sketch: SketchSet,
|
||||
hole_sketch: Vec<Sketch>,
|
||||
sketch: Sketch,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Sketch, KclError> {
|
||||
let hole_sketches: Vec<Sketch> = hole_sketch.into();
|
||||
for hole_sketch in hole_sketches {
|
||||
for hole_sketch in hole_sketch {
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::Solid2dAddHole {
|
||||
|
@ -9,7 +9,10 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
errors::KclError,
|
||||
execution::{ExecState, Helix, KclValue, Sketch, SketchSet, SolidSet},
|
||||
execution::{
|
||||
kcl_value::{ArrayLen, RuntimeType},
|
||||
ExecState, Helix, KclValue, PrimitiveType, Sketch, Solid,
|
||||
},
|
||||
std::{extrude::do_post_extrude, fillet::default_tolerance, Args},
|
||||
};
|
||||
|
||||
@ -24,12 +27,16 @@ pub enum SweepPath {
|
||||
|
||||
/// Extrude a sketch along a path.
|
||||
pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch_set = args.get_unlabeled_kw_arg("sketch_set")?;
|
||||
let sketches = args.get_unlabeled_kw_arg_typed(
|
||||
"sketches",
|
||||
&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
|
||||
exec_state,
|
||||
)?;
|
||||
let path: SweepPath = args.get_kw_arg("path")?;
|
||||
let sectional = args.get_kw_arg_opt("sectional")?;
|
||||
let tolerance = args.get_kw_arg_opt("tolerance")?;
|
||||
|
||||
let value = inner_sweep(sketch_set, path, sectional, tolerance, exec_state, args).await?;
|
||||
let value = inner_sweep(sketches, path, sectional, tolerance, exec_state, args).await?;
|
||||
Ok(value.into())
|
||||
}
|
||||
|
||||
@ -134,26 +141,25 @@ pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
sketch_set = { docs = "The sketch or set of sketches that should be swept in space" },
|
||||
sketches = { docs = "The sketch or set of sketches that should be swept in space" },
|
||||
path = { docs = "The path to sweep the sketch along" },
|
||||
sectional = { docs = "If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components." },
|
||||
tolerance = { docs = "Tolerance for this operation" },
|
||||
}
|
||||
}]
|
||||
async fn inner_sweep(
|
||||
sketch_set: SketchSet,
|
||||
sketches: Vec<Sketch>,
|
||||
path: SweepPath,
|
||||
sectional: Option<bool>,
|
||||
tolerance: Option<f64>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<SolidSet, KclError> {
|
||||
) -> Result<Vec<Solid>, KclError> {
|
||||
let trajectory = match path {
|
||||
SweepPath::Sketch(sketch) => sketch.id.into(),
|
||||
SweepPath::Helix(helix) => helix.value.into(),
|
||||
};
|
||||
|
||||
let sketches: Vec<Sketch> = sketch_set.into();
|
||||
let mut solids = Vec::new();
|
||||
for sketch in &sketches {
|
||||
let id = exec_state.next_uuid();
|
||||
@ -171,5 +177,5 @@ async fn inner_sweep(
|
||||
solids.push(do_post_extrude(sketch.clone(), id.into(), 0.0, exec_state, args.clone()).await?);
|
||||
}
|
||||
|
||||
Ok(solids.into())
|
||||
Ok(solids)
|
||||
}
|
||||
|
@ -13,18 +13,28 @@ use kittycad_modeling_cmds as kcmc;
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{ExecState, KclValue, SolidOrImportedGeometry},
|
||||
execution::{
|
||||
kcl_value::{ArrayLen, RuntimeType},
|
||||
ExecState, KclValue, PrimitiveType, SolidOrImportedGeometry,
|
||||
},
|
||||
std::Args,
|
||||
};
|
||||
|
||||
/// Scale a solid.
|
||||
pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let solid_set = args.get_unlabeled_kw_arg("solid_set")?;
|
||||
let solids = args.get_unlabeled_kw_arg_typed(
|
||||
"solids",
|
||||
&RuntimeType::Union(vec![
|
||||
RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
|
||||
RuntimeType::Primitive(PrimitiveType::ImportedGeometry),
|
||||
]),
|
||||
exec_state,
|
||||
)?;
|
||||
let scale = args.get_kw_arg("scale")?;
|
||||
let global = args.get_kw_arg_opt("global")?;
|
||||
|
||||
let solid = inner_scale(solid_set, scale, global, exec_state, args).await?;
|
||||
Ok(solid.into())
|
||||
let solids = inner_scale(solids, scale, global, exec_state, args).await?;
|
||||
Ok(solids.into())
|
||||
}
|
||||
|
||||
/// Scale a solid.
|
||||
@ -124,19 +134,19 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
solid_set = {docs = "The solid or set of solids to scale."},
|
||||
solids = {docs = "The solid or set of solids to scale."},
|
||||
scale = {docs = "The scale factor for the x, y, and z axes."},
|
||||
global = {docs = "If true, the transform is applied in global space. The origin of the model will move. By default, the transform is applied in local sketch axis, therefore the origin will not move."}
|
||||
}
|
||||
}]
|
||||
async fn inner_scale(
|
||||
solid_set: SolidOrImportedGeometry,
|
||||
solids: SolidOrImportedGeometry,
|
||||
scale: [f64; 3],
|
||||
global: Option<bool>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<SolidOrImportedGeometry, KclError> {
|
||||
for solid_id in solid_set.ids() {
|
||||
for solid_id in solids.ids() {
|
||||
let id = exec_state.next_uuid();
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
@ -162,17 +172,24 @@ async fn inner_scale(
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(solid_set)
|
||||
Ok(solids)
|
||||
}
|
||||
|
||||
/// Move a solid.
|
||||
pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let solid_set = args.get_unlabeled_kw_arg("solid_set")?;
|
||||
let solids = args.get_unlabeled_kw_arg_typed(
|
||||
"solids",
|
||||
&RuntimeType::Union(vec![
|
||||
RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
|
||||
RuntimeType::Primitive(PrimitiveType::ImportedGeometry),
|
||||
]),
|
||||
exec_state,
|
||||
)?;
|
||||
let translate = args.get_kw_arg("translate")?;
|
||||
let global = args.get_kw_arg_opt("global")?;
|
||||
|
||||
let solid = inner_translate(solid_set, translate, global, exec_state, args).await?;
|
||||
Ok(solid.into())
|
||||
let solids = inner_translate(solids, translate, global, exec_state, args).await?;
|
||||
Ok(solids.into())
|
||||
}
|
||||
|
||||
/// Move a solid.
|
||||
@ -264,19 +281,19 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
solid_set = {docs = "The solid or set of solids to move."},
|
||||
solids = {docs = "The solid or set of solids to move."},
|
||||
translate = {docs = "The amount to move the solid in all three axes."},
|
||||
global = {docs = "If true, the transform is applied in global space. The origin of the model will move. By default, the transform is applied in local sketch axis, therefore the origin will not move."}
|
||||
}
|
||||
}]
|
||||
async fn inner_translate(
|
||||
solid_set: SolidOrImportedGeometry,
|
||||
solids: SolidOrImportedGeometry,
|
||||
translate: [f64; 3],
|
||||
global: Option<bool>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<SolidOrImportedGeometry, KclError> {
|
||||
for solid_id in solid_set.ids() {
|
||||
for solid_id in solids.ids() {
|
||||
let id = exec_state.next_uuid();
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
@ -302,12 +319,19 @@ async fn inner_translate(
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(solid_set)
|
||||
Ok(solids)
|
||||
}
|
||||
|
||||
/// Rotate a solid.
|
||||
pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let solid_set = args.get_unlabeled_kw_arg("solid_set")?;
|
||||
let solids = args.get_unlabeled_kw_arg_typed(
|
||||
"solid",
|
||||
&RuntimeType::Union(vec![
|
||||
RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
|
||||
RuntimeType::Primitive(PrimitiveType::ImportedGeometry),
|
||||
]),
|
||||
exec_state,
|
||||
)?;
|
||||
let roll = args.get_kw_arg_opt("roll")?;
|
||||
let pitch = args.get_kw_arg_opt("pitch")?;
|
||||
let yaw = args.get_kw_arg_opt("yaw")?;
|
||||
@ -415,8 +439,8 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
}
|
||||
}
|
||||
|
||||
let solid = inner_rotate(solid_set, roll, pitch, yaw, axis, angle, global, exec_state, args).await?;
|
||||
Ok(solid.into())
|
||||
let solids = inner_rotate(solids, roll, pitch, yaw, axis, angle, global, exec_state, args).await?;
|
||||
Ok(solids.into())
|
||||
}
|
||||
|
||||
/// Rotate a solid.
|
||||
@ -571,7 +595,7 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
solid_set = {docs = "The solid or set of solids to rotate."},
|
||||
solids = {docs = "The solid or set of solids to rotate."},
|
||||
roll = {docs = "The roll angle in degrees. Must be used with `pitch` and `yaw`. Must be between -360 and 360.", include_in_snippet = true},
|
||||
pitch = {docs = "The pitch angle in degrees. Must be used with `roll` and `yaw`. Must be between -360 and 360.", include_in_snippet = true},
|
||||
yaw = {docs = "The yaw angle in degrees. Must be used with `roll` and `pitch`. Must be between -360 and 360.", include_in_snippet = true},
|
||||
@ -582,7 +606,7 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
}]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn inner_rotate(
|
||||
solid_set: SolidOrImportedGeometry,
|
||||
solids: SolidOrImportedGeometry,
|
||||
roll: Option<f64>,
|
||||
pitch: Option<f64>,
|
||||
yaw: Option<f64>,
|
||||
@ -592,7 +616,7 @@ async fn inner_rotate(
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<SolidOrImportedGeometry, KclError> {
|
||||
for solid_id in solid_set.ids() {
|
||||
for solid_id in solids.ids() {
|
||||
let id = exec_state.next_uuid();
|
||||
|
||||
if let (Some(roll), Some(pitch), Some(yaw)) = (roll, pitch, yaw) {
|
||||
@ -645,7 +669,7 @@ async fn inner_rotate(
|
||||
}
|
||||
}
|
||||
|
||||
Ok(solid_set)
|
||||
Ok(solids)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
Reference in New Issue
Block a user