1use anyhow::Result;
4use kcmc::{ModelingCmd, each_cmd as mcmd, length_unit::LengthUnit, shared::CutType};
5use kittycad_modeling_cmds as kcmc;
6
7use super::args::TyF64;
8use crate::{
9 errors::{KclError, KclErrorDetails},
10 execution::{
11 ChamferSurface, EdgeCut, ExecState, ExtrudeSurface, GeoMeta, KclValue, ModelingCmdMeta, Solid,
12 types::RuntimeType,
13 },
14 parsing::ast::types::TagNode,
15 std::{Args, fillet::EdgeReference},
16};
17
18pub(crate) const DEFAULT_TOLERANCE: f64 = 0.0000001;
19
20pub async fn chamfer(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
22 let solid = args.get_unlabeled_kw_arg("solid", &RuntimeType::solid(), exec_state)?;
23 let length: TyF64 = args.get_kw_arg("length", &RuntimeType::length(), exec_state)?;
24 let tags = args.kw_arg_edge_array_and_source("tags")?;
25 let tag = args.get_kw_arg_opt("tag", &RuntimeType::tag_decl(), exec_state)?;
26
27 super::fillet::validate_unique(&tags)?;
28 let tags: Vec<EdgeReference> = tags.into_iter().map(|item| item.0).collect();
29 let value = inner_chamfer(solid, length, tags, tag, exec_state, args).await?;
30 Ok(KclValue::Solid { value })
31}
32
33async fn inner_chamfer(
34 solid: Box<Solid>,
35 length: TyF64,
36 tags: Vec<EdgeReference>,
37 tag: Option<TagNode>,
38 exec_state: &mut ExecState,
39 args: Args,
40) -> Result<Box<Solid>, KclError> {
41 if tag.is_some() && tags.len() > 1 {
44 return Err(KclError::new_type(KclErrorDetails::new(
45 "You can only tag one edge at a time with a tagged chamfer. Either delete the tag for the chamfer fn if you don't need it OR separate into individual chamfer functions for each tag.".to_string(),
46 vec![args.source_range],
47 )));
48 }
49
50 let mut solid = solid.clone();
51 for edge_tag in tags {
52 let edge_id = match edge_tag {
53 EdgeReference::Uuid(uuid) => uuid,
54 EdgeReference::Tag(edge_tag) => args.get_tag_engine_info(exec_state, &edge_tag)?.id,
55 };
56
57 let id = exec_state.next_uuid();
58 exec_state
59 .batch_end_cmd(
60 ModelingCmdMeta::from_args_id(&args, id),
61 ModelingCmd::from(mcmd::Solid3dFilletEdge {
62 edge_id: None,
63 edge_ids: vec![edge_id],
64 extra_face_ids: vec![],
65 strategy: Default::default(),
66 object_id: solid.id,
67 radius: LengthUnit(length.to_mm()),
68 tolerance: LengthUnit(DEFAULT_TOLERANCE), cut_type: CutType::Chamfer,
70 }),
71 )
72 .await?;
73
74 solid.edge_cuts.push(EdgeCut::Chamfer {
75 id,
76 edge_id,
77 length: length.clone(),
78 tag: Box::new(tag.clone()),
79 });
80
81 if let Some(ref tag) = tag {
82 solid.value.push(ExtrudeSurface::Chamfer(ChamferSurface {
83 face_id: id,
84 tag: Some(tag.clone()),
85 geo_meta: GeoMeta {
86 id,
87 metadata: args.source_range.into(),
88 },
89 }));
90 }
91 }
92
93 Ok(solid)
94}