Add CSG operations to the artifact graph (#6104)

* Add CSG operations to the artifact graph

* Add tool IDs for subtract

* Fix names to match modeling-cmds now that it's done

* Update output since adding CSG ops

* Update formatting

* Add extra solid ids to the graph

* Fix to not add duplicates to the graph
This commit is contained in:
Jonathan Tran
2025-04-11 01:16:29 -04:00
committed by GitHub
parent 6f2e6d14b6
commit 5832890dbb
8 changed files with 152 additions and 1 deletions

View File

@ -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<ArtifactId>,
/// Tool solids used for asymmetric operations like subtract.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tool_ids: Vec<ArtifactId>,
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<Artifact> {
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<Artifact> {
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<Artifact> {
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::<Vec<_>>();
(CompositeSolidSubType::Intersect, solid_ids, Vec::new())
}
ModelingCmd::BooleanSubtract(subtract) => {
let solid_ids = subtract
.target_ids
.iter()
.copied()
.map(ArtifactId::new)
.collect::<Vec<_>>();
let tool_ids = subtract
.tool_ids
.iter()
.copied()
.map(ArtifactId::new)
.collect::<Vec<_>>();
(CompositeSolidSubType::Subtract, solid_ids, tool_ids)
}
ModelingCmd::BooleanUnion(union) => {
let solid_ids = union.solid_ids.iter().copied().map(ArtifactId::new).collect::<Vec<_>>();
(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::<Vec<_>>();
// TODO: Should we add the reverse graph edges?
return Ok(return_arr);
}
_ => {}
}