Import geometry work w transforms (#5757)

* make work with imported geometry

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* iupdates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* update known issues

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Jess Frazelle
2025-03-11 18:23:21 -07:00
committed by GitHub
parent e5c05e1980
commit f31c2c6f81
24 changed files with 5732521 additions and 453 deletions

View File

@ -133,6 +133,7 @@ impl StdLibFnArg {
|| self.type_ == "SolidSet"
|| self.type_ == "SketchSurface"
|| self.type_ == "SketchOrSurface"
|| self.type_ == "SolidOrImportedGeometry"
{
return Ok(Some((index, format!("{label}${{{}:{}}}", index, "%"))));
} else if (self.type_ == "TagDeclarator" || self.type_ == "TagNode") && self.required {

View File

@ -223,6 +223,33 @@ pub struct ImportedGeometry {
pub meta: Vec<Metadata>,
}
/// Data for a solid or an imported geometry.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum SolidOrImportedGeometry {
Solid(Box<Solid>),
ImportedGeometry(Box<ImportedGeometry>),
}
impl SolidOrImportedGeometry {
pub fn id(&self) -> uuid::Uuid {
match self {
SolidOrImportedGeometry::Solid(s) => s.id,
SolidOrImportedGeometry::ImportedGeometry(s) => s.id,
}
}
}
impl From<SolidOrImportedGeometry> for crate::execution::KclValue {
fn from(value: SolidOrImportedGeometry) -> Self {
match value {
SolidOrImportedGeometry::Solid(s) => crate::execution::KclValue::Solid { value: s },
SolidOrImportedGeometry::ImportedGeometry(s) => crate::execution::KclValue::ImportedGeometry(*s),
}
}
}
/// A helix.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]

View File

@ -77,7 +77,7 @@ fn read<P>(filename: &str, dir: P) -> String
where
P: AsRef<Path>,
{
std::fs::read_to_string(dir.as_ref().join(filename)).unwrap()
std::fs::read_to_string(dir.as_ref().join(filename)).expect("Failed to read file: {filename}")
}
fn parse(test_name: &str) {
@ -2136,3 +2136,25 @@ mod flush_batch_on_end {
super::execute(TEST_NAME, true).await
}
}
mod import_transform {
const TEST_NAME: &str = "import_transform";
/// Test parsing KCL.
#[test]
fn parse() {
super::parse(TEST_NAME);
}
/// Test that parsing and unparsing KCL produces the original KCL input.
#[test]
fn unparse() {
super::unparse(TEST_NAME)
}
/// Test that KCL is executed correctly.
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_execute() {
super::execute(TEST_NAME, true).await
}
}

View File

@ -1287,6 +1287,16 @@ 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::ImportedGeometry(value) => Some(Self::ImportedGeometry(Box::new(value.clone()))),
_ => None,
}
}
}
impl<'a> FromKclValue<'a> for super::sketch::SketchData {
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
// Order is critical since PlaneData is a subset of Plane.

View File

@ -13,7 +13,7 @@ use kittycad_modeling_cmds as kcmc;
use crate::{
errors::{KclError, KclErrorDetails},
execution::{ExecState, KclValue, Solid},
execution::{ExecState, KclValue, SolidOrImportedGeometry},
std::Args,
};
@ -24,7 +24,7 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
let global = args.get_kw_arg_opt("global")?;
let solid = inner_scale(solid, scale, global, exec_state, args).await?;
Ok(KclValue::Solid { value: solid })
Ok(solid.into())
}
/// Scale a solid.
@ -73,6 +73,17 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
/// scale = [1.0, 1.0, 2.5],
/// )
/// ```
///
/// ```no_run
/// // Scale an imported model.
///
/// import "tests/inputs/cube.sldprt" as cube
///
/// cube
/// |> scale(
/// scale = [1.0, 1.0, 2.5],
/// )
/// ```
#[stdlib {
name = "scale",
feature_tree_operation = false,
@ -85,18 +96,18 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
}
}]
async fn inner_scale(
solid: Box<Solid>,
solid: SolidOrImportedGeometry,
scale: [f64; 3],
global: Option<bool>,
exec_state: &mut ExecState,
args: Args,
) -> Result<Box<Solid>, KclError> {
) -> Result<SolidOrImportedGeometry, KclError> {
let id = exec_state.next_uuid();
args.batch_modeling_cmd(
id,
ModelingCmd::from(mcmd::SetObjectTransform {
object_id: solid.id,
object_id: solid.id(),
transforms: vec![shared::ComponentTransform {
scale: Some(shared::TransformBy::<Point3d<f64>> {
property: Point3d {
@ -125,7 +136,7 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
let global = args.get_kw_arg_opt("global")?;
let solid = inner_translate(solid, translate, global, exec_state, args).await?;
Ok(KclValue::Solid { value: solid })
Ok(solid.into())
}
/// Move a solid.
@ -166,6 +177,17 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
/// translate = [1.0, 1.0, 2.5],
/// )
/// ```
///
/// ```no_run
/// // Move an imported model.
///
/// import "tests/inputs/cube.sldprt" as cube
///
/// cube
/// |> translate(
/// translate = [1.0, 1.0, 2.5],
/// )
/// ```
#[stdlib {
name = "translate",
feature_tree_operation = false,
@ -178,18 +200,18 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
}
}]
async fn inner_translate(
solid: Box<Solid>,
solid: SolidOrImportedGeometry,
translate: [f64; 3],
global: Option<bool>,
exec_state: &mut ExecState,
args: Args,
) -> Result<Box<Solid>, KclError> {
) -> Result<SolidOrImportedGeometry, KclError> {
let id = exec_state.next_uuid();
args.batch_modeling_cmd(
id,
ModelingCmd::from(mcmd::SetObjectTransform {
object_id: solid.id,
object_id: solid.id(),
transforms: vec![shared::ComponentTransform {
translate: Some(shared::TransformBy::<Point3d<LengthUnit>> {
property: shared::Point3d {
@ -322,7 +344,7 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
}
let solid = inner_rotate(solid, roll, pitch, yaw, axis, angle, global, exec_state, args).await?;
Ok(KclValue::Solid { value: solid })
Ok(solid.into())
}
/// Rotate a solid.
@ -425,6 +447,18 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
/// angle = 90,
/// )
/// ```
///
/// ```no_run
/// // Rotate an imported model.
///
/// import "tests/inputs/cube.sldprt" as cube
///
/// cube
/// |> rotate(
/// axis = [0, 0, 1.0],
/// angle = 90,
/// )
/// ```
#[stdlib {
name = "rotate",
feature_tree_operation = false,
@ -442,7 +476,7 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
}]
#[allow(clippy::too_many_arguments)]
async fn inner_rotate(
solid: Box<Solid>,
solid: SolidOrImportedGeometry,
roll: Option<f64>,
pitch: Option<f64>,
yaw: Option<f64>,
@ -451,14 +485,14 @@ async fn inner_rotate(
global: Option<bool>,
exec_state: &mut ExecState,
args: Args,
) -> Result<Box<Solid>, KclError> {
) -> Result<SolidOrImportedGeometry, KclError> {
let id = exec_state.next_uuid();
if let (Some(roll), Some(pitch), Some(yaw)) = (roll, pitch, yaw) {
args.batch_modeling_cmd(
id,
ModelingCmd::from(mcmd::SetObjectTransform {
object_id: solid.id,
object_id: solid.id(),
transforms: vec![shared::ComponentTransform {
rotate_rpy: Some(shared::TransformBy::<Point3d<f64>> {
property: shared::Point3d {
@ -482,7 +516,7 @@ async fn inner_rotate(
args.batch_modeling_cmd(
id,
ModelingCmd::from(mcmd::SetObjectTransform {
object_id: solid.id,
object_id: solid.id(),
transforms: vec![shared::ComponentTransform {
rotate_angle_axis: Some(shared::TransformBy::<Point4d<f64>> {
property: shared::Point4d {