diff --git a/rust/kcl-lib/src/execution/artifact.rs b/rust/kcl-lib/src/execution/artifact.rs index 4767c6c28e1..717081595a6 100644 --- a/rust/kcl-lib/src/execution/artifact.rs +++ b/rust/kcl-lib/src/execution/artifact.rs @@ -115,6 +115,30 @@ impl CodeRef { } } +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)] +#[ts(export_to = "Artifact.ts")] +#[serde(rename_all = "camelCase")] +pub struct CompositeSolid { + pub id: ArtifactId, + pub sub_type: CompositeSolidSubType, + /// Constituent solids of the composite solid. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub solid_ids: Vec, + /// Tool solids used for asymmetric operations like subtract. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub tool_ids: Vec, + pub code_ref: CodeRef, +} + +#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS)] +#[ts(export_to = "Artifact.ts")] +#[serde(rename_all = "camelCase")] +pub enum CompositeSolidSubType { + Intersect, + Subtract, + Union, +} + #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)] #[ts(export_to = "Artifact.ts")] #[serde(rename_all = "camelCase")] @@ -318,6 +342,7 @@ pub struct Helix { #[ts(export_to = "Artifact.ts")] #[serde(tag = "type", rename_all = "camelCase")] pub enum Artifact { + CompositeSolid(CompositeSolid), Plane(Plane), Path(Path), Segment(Segment), @@ -336,6 +361,7 @@ pub enum Artifact { impl Artifact { pub(crate) fn id(&self) -> ArtifactId { match self { + Artifact::CompositeSolid(a) => a.id, Artifact::Plane(a) => a.id, Artifact::Path(a) => a.id, Artifact::Segment(a) => a.id, @@ -355,6 +381,7 @@ impl Artifact { #[expect(dead_code)] pub(crate) fn code_ref(&self) -> Option<&CodeRef> { match self { + Artifact::CompositeSolid(a) => Some(&a.code_ref), Artifact::Plane(a) => Some(&a.code_ref), Artifact::Path(a) => Some(&a.code_ref), Artifact::Segment(a) => Some(&a.code_ref), @@ -375,6 +402,7 @@ impl Artifact { /// type, return the new artifact which should be used as a replacement. fn merge(&mut self, new: Artifact) -> Option { match self { + Artifact::CompositeSolid(a) => a.merge(new), Artifact::Plane(a) => a.merge(new), Artifact::Path(a) => a.merge(new), Artifact::Segment(a) => a.merge(new), @@ -392,6 +420,18 @@ impl Artifact { } } +impl CompositeSolid { + fn merge(&mut self, new: Artifact) -> Option { + let Artifact::CompositeSolid(new) = new else { + return Some(new); + }; + merge_ids(&mut self.solid_ids, new.solid_ids); + merge_ids(&mut self.tool_ids, new.tool_ids); + + None + } +} + impl Plane { fn merge(&mut self, new: Artifact) -> Option { let Artifact::Plane(new) = new else { @@ -1047,6 +1087,85 @@ fn artifacts_to_update( // the helix here, but it's not useful right now. return Ok(return_arr); } + ModelingCmd::BooleanIntersection(_) | ModelingCmd::BooleanSubtract(_) | ModelingCmd::BooleanUnion(_) => { + let (sub_type, solid_ids, tool_ids) = match cmd { + ModelingCmd::BooleanIntersection(intersection) => { + let solid_ids = intersection + .solid_ids + .iter() + .copied() + .map(ArtifactId::new) + .collect::>(); + (CompositeSolidSubType::Intersect, solid_ids, Vec::new()) + } + ModelingCmd::BooleanSubtract(subtract) => { + let solid_ids = subtract + .target_ids + .iter() + .copied() + .map(ArtifactId::new) + .collect::>(); + let tool_ids = subtract + .tool_ids + .iter() + .copied() + .map(ArtifactId::new) + .collect::>(); + (CompositeSolidSubType::Subtract, solid_ids, tool_ids) + } + ModelingCmd::BooleanUnion(union) => { + let solid_ids = union.solid_ids.iter().copied().map(ArtifactId::new).collect::>(); + (CompositeSolidSubType::Union, solid_ids, Vec::new()) + } + _ => unreachable!(), + }; + + let mut new_solid_ids = vec![id]; + + match response { + OkModelingCmdResponse::BooleanIntersection(intersection) => intersection + .extra_solid_ids + .iter() + .copied() + .map(ArtifactId::new) + .for_each(|id| new_solid_ids.push(id)), + OkModelingCmdResponse::BooleanSubtract(subtract) => subtract + .extra_solid_ids + .iter() + .copied() + .map(ArtifactId::new) + .for_each(|id| new_solid_ids.push(id)), + OkModelingCmdResponse::BooleanUnion(union) => union + .extra_solid_ids + .iter() + .copied() + .map(ArtifactId::new) + .for_each(|id| new_solid_ids.push(id)), + _ => {} + } + let return_arr = new_solid_ids + .into_iter() + // Extra solid IDs may include the command's ID. Make sure we + // don't create a duplicate. + .filter(|solid_id| *solid_id != id) + .map(|solid_id| { + Artifact::CompositeSolid(CompositeSolid { + id: solid_id, + sub_type, + solid_ids: solid_ids.clone(), + tool_ids: tool_ids.clone(), + code_ref: CodeRef { + range, + path_to_node: path_to_node.clone(), + }, + }) + }) + .collect::>(); + + // TODO: Should we add the reverse graph edges? + + return Ok(return_arr); + } _ => {} } diff --git a/rust/kcl-lib/src/execution/artifact/mermaid_tests.rs b/rust/kcl-lib/src/execution/artifact/mermaid_tests.rs index 2b1839eee98..5d1de5988ec 100644 --- a/rust/kcl-lib/src/execution/artifact/mermaid_tests.rs +++ b/rust/kcl-lib/src/execution/artifact/mermaid_tests.rs @@ -67,6 +67,11 @@ impl Artifact { /// the graph. This should be disjoint with `child_ids`. pub(crate) fn back_edges(&self) -> Vec { match self { + Artifact::CompositeSolid(a) => { + let mut ids = a.solid_ids.clone(); + ids.extend(a.tool_ids.iter()); + ids + } Artifact::Plane(_) => Vec::new(), Artifact::Path(a) => vec![a.plane_id], Artifact::Segment(a) => vec![a.path_id], @@ -87,6 +92,11 @@ impl Artifact { /// the graph. pub(crate) fn child_ids(&self) -> Vec { match self { + Artifact::CompositeSolid(_) => { + // Note: Don't include these since they're parents: solid_ids, + // tool_ids. + Vec::new() + } Artifact::Plane(a) => a.path_ids.clone(), Artifact::Path(a) => { // Note: Don't include these since they're parents: plane_id. @@ -213,6 +223,7 @@ impl ArtifactGraph { let id = artifact.id(); let grouped = match artifact { + Artifact::CompositeSolid(_) => false, Artifact::Plane(_) => false, Artifact::Path(_) => { groups.entry(id).or_insert_with(Vec::new).push(id); @@ -278,6 +289,15 @@ impl ArtifactGraph { } match artifact { + Artifact::CompositeSolid(composite_solid) => { + writeln!( + output, + "{prefix}{}[\"CompositeSolid {:?}
{:?}\"]", + id, + composite_solid.sub_type, + code_ref_display(&composite_solid.code_ref) + )?; + } Artifact::Plane(plane) => { writeln!( output, diff --git a/rust/kcl-lib/tests/big_number_angle_to_match_length_y/rendered_model.png b/rust/kcl-lib/tests/big_number_angle_to_match_length_y/rendered_model.png index fbe4e14842b..ad1d06eeeb8 100644 Binary files a/rust/kcl-lib/tests/big_number_angle_to_match_length_y/rendered_model.png and b/rust/kcl-lib/tests/big_number_angle_to_match_length_y/rendered_model.png differ diff --git a/rust/kcl-lib/tests/intersect_cubes/artifact_graph_flowchart.snap.md b/rust/kcl-lib/tests/intersect_cubes/artifact_graph_flowchart.snap.md index d94dcb7c60a..7529ff76928 100644 --- a/rust/kcl-lib/tests/intersect_cubes/artifact_graph_flowchart.snap.md +++ b/rust/kcl-lib/tests/intersect_cubes/artifact_graph_flowchart.snap.md @@ -48,6 +48,7 @@ flowchart LR 42["SweepEdge Adjacent"] 43["SweepEdge Opposite"] 44["SweepEdge Adjacent"] + 45["CompositeSolid Intersect
[448, 477, 0]"] 1 --- 2 2 --- 3 2 --- 4 @@ -114,4 +115,6 @@ flowchart LR 30 --- 42 30 --- 43 30 --- 44 + 2 <--x 45 + 24 <--x 45 ``` diff --git a/rust/kcl-lib/tests/subtract_cylinder_from_cube/artifact_graph_flowchart.snap.md b/rust/kcl-lib/tests/subtract_cylinder_from_cube/artifact_graph_flowchart.snap.md index 2ca151fdeb4..477743d8c30 100644 --- a/rust/kcl-lib/tests/subtract_cylinder_from_cube/artifact_graph_flowchart.snap.md +++ b/rust/kcl-lib/tests/subtract_cylinder_from_cube/artifact_graph_flowchart.snap.md @@ -36,6 +36,7 @@ flowchart LR 30["Cap End"] 31["SweepEdge Opposite"] 32["SweepEdge Adjacent"] + 33["CompositeSolid Subtract
[461, 497, 0]"] 1 --- 2 2 --- 3 2 --- 4 @@ -81,4 +82,6 @@ flowchart LR 27 --- 30 27 --- 31 27 --- 32 + 2 <--x 33 + 24 <--x 33 ``` diff --git a/rust/kcl-lib/tests/union_cubes/artifact_graph_flowchart.snap.md b/rust/kcl-lib/tests/union_cubes/artifact_graph_flowchart.snap.md index d94dcb7c60a..a991f572efc 100644 --- a/rust/kcl-lib/tests/union_cubes/artifact_graph_flowchart.snap.md +++ b/rust/kcl-lib/tests/union_cubes/artifact_graph_flowchart.snap.md @@ -48,6 +48,7 @@ flowchart LR 42["SweepEdge Adjacent"] 43["SweepEdge Opposite"] 44["SweepEdge Adjacent"] + 45["CompositeSolid Union
[448, 473, 0]"] 1 --- 2 2 --- 3 2 --- 4 @@ -114,4 +115,6 @@ flowchart LR 30 --- 42 30 --- 43 30 --- 44 + 2 <--x 45 + 24 <--x 45 ``` diff --git a/src/lang/std/artifactGraph.ts b/src/lang/std/artifactGraph.ts index 05adb064703..1e0aa624ffe 100644 --- a/src/lang/std/artifactGraph.ts +++ b/src/lang/std/artifactGraph.ts @@ -514,7 +514,9 @@ export function getCodeRefsByArtifactId( artifactGraph: ArtifactGraph ): Array | null { const artifact = artifactGraph.get(id) - if (artifact?.type === 'solid2d') { + if (artifact?.type === 'compositeSolid') { + return [artifact.codeRef] + } else if (artifact?.type === 'solid2d') { const codeRef = getSolid2dCodeRef(artifact, artifactGraph) if (err(codeRef)) return null return [codeRef] diff --git a/src/lang/wasm.ts b/src/lang/wasm.ts index 99f1a6407eb..b3c24e9e154 100644 --- a/src/lang/wasm.ts +++ b/src/lang/wasm.ts @@ -71,6 +71,7 @@ export type { ArtifactId, Cap as CapArtifact, CodeRef, + CompositeSolid as CompositeSolidArtifact, EdgeCut, Path as PathArtifact, Plane as PlaneArtifact,