2024-06-17 13:10:40 -07:00
|
|
|
//! Standard library shells.
|
|
|
|
|
|
|
|
use anyhow::Result;
|
2024-09-19 14:06:29 -07:00
|
|
|
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, ModelingCmd};
|
2024-09-18 17:04:04 -05:00
|
|
|
use kittycad_modeling_cmds as kcmc;
|
2024-06-17 13:10:40 -07:00
|
|
|
|
2025-04-26 21:21:26 -07:00
|
|
|
use super::args::TyF64;
|
2024-06-17 13:10:40 -07:00
|
|
|
use crate::{
|
|
|
|
errors::{KclError, KclErrorDetails},
|
2025-04-28 14:20:38 +12:00
|
|
|
execution::{
|
|
|
|
types::{ArrayLen, RuntimeType},
|
|
|
|
ExecState, KclValue, Solid,
|
|
|
|
},
|
2024-06-23 23:04:32 -07:00
|
|
|
std::{sketch::FaceTag, Args},
|
2024-06-17 13:10:40 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/// Create a shell.
|
2024-09-16 15:10:33 -04:00
|
|
|
pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
2025-03-21 10:56:55 +13:00
|
|
|
let solids = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?;
|
2025-04-23 10:58:35 +12:00
|
|
|
let thickness: TyF64 = args.get_kw_arg_typed("thickness", &RuntimeType::length(), exec_state)?;
|
2025-04-28 14:20:38 +12:00
|
|
|
let faces = args.get_kw_arg_typed(
|
|
|
|
"faces",
|
|
|
|
&RuntimeType::Array(Box::new(RuntimeType::tag()), ArrayLen::NonEmpty),
|
|
|
|
exec_state,
|
|
|
|
)?;
|
2024-06-17 13:10:40 -07:00
|
|
|
|
2025-04-23 10:58:35 +12:00
|
|
|
let result = inner_shell(solids, thickness, faces, exec_state, args).await?;
|
2024-09-17 18:49:08 -07:00
|
|
|
Ok(result.into())
|
2024-06-17 13:10:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async fn inner_shell(
|
2025-03-17 17:57:26 +13:00
|
|
|
solids: Vec<Solid>,
|
2025-04-23 10:58:35 +12:00
|
|
|
thickness: TyF64,
|
2025-02-06 20:03:12 -06:00
|
|
|
faces: Vec<FaceTag>,
|
2024-09-16 15:10:33 -04:00
|
|
|
exec_state: &mut ExecState,
|
2024-06-17 13:10:40 -07:00
|
|
|
args: Args,
|
2025-03-17 17:57:26 +13:00
|
|
|
) -> Result<Vec<Solid>, KclError> {
|
2025-02-06 20:03:12 -06:00
|
|
|
if faces.is_empty() {
|
2025-05-19 14:13:10 -04:00
|
|
|
return Err(KclError::Type(KclErrorDetails::new(
|
|
|
|
"You must shell at least one face".to_owned(),
|
|
|
|
vec![args.source_range],
|
|
|
|
)));
|
2024-06-17 13:10:40 -07:00
|
|
|
}
|
|
|
|
|
2024-09-27 15:44:44 -07:00
|
|
|
if solids.is_empty() {
|
2025-05-19 14:13:10 -04:00
|
|
|
return Err(KclError::Type(KclErrorDetails::new(
|
|
|
|
"You must shell at least one solid".to_owned(),
|
|
|
|
vec![args.source_range],
|
|
|
|
)));
|
2024-09-17 18:49:08 -07:00
|
|
|
}
|
|
|
|
|
2024-06-17 13:10:40 -07:00
|
|
|
let mut face_ids = Vec::new();
|
2024-09-27 15:44:44 -07:00
|
|
|
for solid in &solids {
|
2024-09-17 18:49:08 -07:00
|
|
|
// 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.
|
2025-03-19 12:28:56 -07:00
|
|
|
args.flush_batch_for_solids(exec_state, &[solid.clone()]).await?;
|
2024-09-17 18:49:08 -07:00
|
|
|
|
2025-02-06 20:03:12 -06:00
|
|
|
for tag in &faces {
|
2024-09-27 15:44:44 -07:00
|
|
|
let extrude_plane_id = tag.get_face_id(solid, exec_state, &args, false).await?;
|
2024-06-17 13:10:40 -07:00
|
|
|
|
2024-09-17 18:49:08 -07:00
|
|
|
face_ids.push(extrude_plane_id);
|
|
|
|
}
|
2024-06-17 13:10:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if face_ids.is_empty() {
|
2025-05-19 14:13:10 -04:00
|
|
|
return Err(KclError::Type(KclErrorDetails::new(
|
|
|
|
"Expected at least one valid face".to_owned(),
|
|
|
|
vec![args.source_range],
|
|
|
|
)));
|
2024-06-17 13:10:40 -07:00
|
|
|
}
|
|
|
|
|
2024-09-27 15:44:44 -07:00
|
|
|
// Make sure all the solids have the same id, as we are going to shell them all at
|
2024-09-17 18:49:08 -07:00
|
|
|
// once.
|
2024-09-27 15:44:44 -07:00
|
|
|
if !solids.iter().all(|eg| eg.id == solids[0].id) {
|
2025-05-19 14:13:10 -04:00
|
|
|
return Err(KclError::Type(KclErrorDetails::new(
|
|
|
|
"All solids stem from the same root object, like multiple sketch on face extrusions, etc.".to_owned(),
|
|
|
|
vec![args.source_range],
|
|
|
|
)));
|
2024-09-17 18:49:08 -07:00
|
|
|
}
|
2024-08-04 15:37:40 -07:00
|
|
|
|
2024-06-19 13:57:50 -07:00
|
|
|
args.batch_modeling_cmd(
|
2024-12-17 09:38:32 +13:00
|
|
|
exec_state.next_uuid(),
|
2024-09-18 17:04:04 -05:00
|
|
|
ModelingCmd::from(mcmd::Solid3dShellFace {
|
2024-08-23 07:50:30 -05:00
|
|
|
hollow: false,
|
2024-06-17 13:10:40 -07:00
|
|
|
face_ids,
|
2024-09-27 15:44:44 -07:00
|
|
|
object_id: solids[0].id,
|
2025-04-23 10:58:35 +12:00
|
|
|
shell_thickness: LengthUnit(thickness.to_mm()),
|
2024-09-18 17:04:04 -05:00
|
|
|
}),
|
2024-06-17 13:10:40 -07:00
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
2025-03-17 17:57:26 +13:00
|
|
|
Ok(solids)
|
2024-06-17 13:10:40 -07:00
|
|
|
}
|
2024-08-23 09:57:02 -07:00
|
|
|
|
|
|
|
/// Make the inside of a 3D object hollow.
|
2024-09-16 15:10:33 -04:00
|
|
|
pub async fn hollow(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
2025-04-23 13:35:33 -05:00
|
|
|
let solid = args.get_unlabeled_kw_arg_typed("solid", &RuntimeType::solid(), exec_state)?;
|
|
|
|
let thickness: TyF64 = args.get_kw_arg_typed("thickness", &RuntimeType::length(), exec_state)?;
|
2024-08-23 09:57:02 -07:00
|
|
|
|
2025-04-23 13:35:33 -05:00
|
|
|
let value = inner_hollow(solid, thickness, exec_state, args).await?;
|
2025-01-22 09:42:09 +13:00
|
|
|
Ok(KclValue::Solid { value })
|
2024-08-23 09:57:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async fn inner_hollow(
|
2024-09-27 15:44:44 -07:00
|
|
|
solid: Box<Solid>,
|
2025-04-23 13:35:33 -05:00
|
|
|
thickness: TyF64,
|
2024-09-16 15:10:33 -04:00
|
|
|
exec_state: &mut ExecState,
|
2024-08-23 09:57:02 -07:00
|
|
|
args: Args,
|
2024-09-27 15:44:44 -07:00
|
|
|
) -> Result<Box<Solid>, KclError> {
|
2024-08-23 09:57:02 -07:00
|
|
|
// 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.
|
2025-03-19 12:28:56 -07:00
|
|
|
args.flush_batch_for_solids(exec_state, &[(*solid).clone()]).await?;
|
2024-08-23 09:57:02 -07:00
|
|
|
|
|
|
|
args.batch_modeling_cmd(
|
2024-12-17 09:38:32 +13:00
|
|
|
exec_state.next_uuid(),
|
2024-09-18 17:04:04 -05:00
|
|
|
ModelingCmd::from(mcmd::Solid3dShellFace {
|
2024-08-23 09:57:02 -07:00
|
|
|
hollow: true,
|
|
|
|
face_ids: Vec::new(), // This is empty because we want to hollow the entire object.
|
2024-09-27 15:44:44 -07:00
|
|
|
object_id: solid.id,
|
2025-04-23 10:58:35 +12:00
|
|
|
shell_thickness: LengthUnit(thickness.to_mm()),
|
2024-09-18 17:04:04 -05:00
|
|
|
}),
|
2024-08-23 09:57:02 -07:00
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
2024-09-27 15:44:44 -07:00
|
|
|
Ok(solid)
|
2024-08-23 09:57:02 -07:00
|
|
|
}
|