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:
@ -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)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
||||||
#[ts(export_to = "Artifact.ts")]
|
#[ts(export_to = "Artifact.ts")]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
@ -318,6 +342,7 @@ pub struct Helix {
|
|||||||
#[ts(export_to = "Artifact.ts")]
|
#[ts(export_to = "Artifact.ts")]
|
||||||
#[serde(tag = "type", rename_all = "camelCase")]
|
#[serde(tag = "type", rename_all = "camelCase")]
|
||||||
pub enum Artifact {
|
pub enum Artifact {
|
||||||
|
CompositeSolid(CompositeSolid),
|
||||||
Plane(Plane),
|
Plane(Plane),
|
||||||
Path(Path),
|
Path(Path),
|
||||||
Segment(Segment),
|
Segment(Segment),
|
||||||
@ -336,6 +361,7 @@ pub enum Artifact {
|
|||||||
impl Artifact {
|
impl Artifact {
|
||||||
pub(crate) fn id(&self) -> ArtifactId {
|
pub(crate) fn id(&self) -> ArtifactId {
|
||||||
match self {
|
match self {
|
||||||
|
Artifact::CompositeSolid(a) => a.id,
|
||||||
Artifact::Plane(a) => a.id,
|
Artifact::Plane(a) => a.id,
|
||||||
Artifact::Path(a) => a.id,
|
Artifact::Path(a) => a.id,
|
||||||
Artifact::Segment(a) => a.id,
|
Artifact::Segment(a) => a.id,
|
||||||
@ -355,6 +381,7 @@ impl Artifact {
|
|||||||
#[expect(dead_code)]
|
#[expect(dead_code)]
|
||||||
pub(crate) fn code_ref(&self) -> Option<&CodeRef> {
|
pub(crate) fn code_ref(&self) -> Option<&CodeRef> {
|
||||||
match self {
|
match self {
|
||||||
|
Artifact::CompositeSolid(a) => Some(&a.code_ref),
|
||||||
Artifact::Plane(a) => Some(&a.code_ref),
|
Artifact::Plane(a) => Some(&a.code_ref),
|
||||||
Artifact::Path(a) => Some(&a.code_ref),
|
Artifact::Path(a) => Some(&a.code_ref),
|
||||||
Artifact::Segment(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.
|
/// type, return the new artifact which should be used as a replacement.
|
||||||
fn merge(&mut self, new: Artifact) -> Option<Artifact> {
|
fn merge(&mut self, new: Artifact) -> Option<Artifact> {
|
||||||
match self {
|
match self {
|
||||||
|
Artifact::CompositeSolid(a) => a.merge(new),
|
||||||
Artifact::Plane(a) => a.merge(new),
|
Artifact::Plane(a) => a.merge(new),
|
||||||
Artifact::Path(a) => a.merge(new),
|
Artifact::Path(a) => a.merge(new),
|
||||||
Artifact::Segment(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 {
|
impl Plane {
|
||||||
fn merge(&mut self, new: Artifact) -> Option<Artifact> {
|
fn merge(&mut self, new: Artifact) -> Option<Artifact> {
|
||||||
let Artifact::Plane(new) = new else {
|
let Artifact::Plane(new) = new else {
|
||||||
@ -1047,6 +1087,85 @@ fn artifacts_to_update(
|
|||||||
// the helix here, but it's not useful right now.
|
// the helix here, but it's not useful right now.
|
||||||
return Ok(return_arr);
|
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);
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,6 +67,11 @@ impl Artifact {
|
|||||||
/// the graph. This should be disjoint with `child_ids`.
|
/// the graph. This should be disjoint with `child_ids`.
|
||||||
pub(crate) fn back_edges(&self) -> Vec<ArtifactId> {
|
pub(crate) fn back_edges(&self) -> Vec<ArtifactId> {
|
||||||
match self {
|
match self {
|
||||||
|
Artifact::CompositeSolid(a) => {
|
||||||
|
let mut ids = a.solid_ids.clone();
|
||||||
|
ids.extend(a.tool_ids.iter());
|
||||||
|
ids
|
||||||
|
}
|
||||||
Artifact::Plane(_) => Vec::new(),
|
Artifact::Plane(_) => Vec::new(),
|
||||||
Artifact::Path(a) => vec![a.plane_id],
|
Artifact::Path(a) => vec![a.plane_id],
|
||||||
Artifact::Segment(a) => vec![a.path_id],
|
Artifact::Segment(a) => vec![a.path_id],
|
||||||
@ -87,6 +92,11 @@ impl Artifact {
|
|||||||
/// the graph.
|
/// the graph.
|
||||||
pub(crate) fn child_ids(&self) -> Vec<ArtifactId> {
|
pub(crate) fn child_ids(&self) -> Vec<ArtifactId> {
|
||||||
match self {
|
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::Plane(a) => a.path_ids.clone(),
|
||||||
Artifact::Path(a) => {
|
Artifact::Path(a) => {
|
||||||
// Note: Don't include these since they're parents: plane_id.
|
// Note: Don't include these since they're parents: plane_id.
|
||||||
@ -213,6 +223,7 @@ impl ArtifactGraph {
|
|||||||
let id = artifact.id();
|
let id = artifact.id();
|
||||||
|
|
||||||
let grouped = match artifact {
|
let grouped = match artifact {
|
||||||
|
Artifact::CompositeSolid(_) => false,
|
||||||
Artifact::Plane(_) => false,
|
Artifact::Plane(_) => false,
|
||||||
Artifact::Path(_) => {
|
Artifact::Path(_) => {
|
||||||
groups.entry(id).or_insert_with(Vec::new).push(id);
|
groups.entry(id).or_insert_with(Vec::new).push(id);
|
||||||
@ -278,6 +289,15 @@ impl ArtifactGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match artifact {
|
match artifact {
|
||||||
|
Artifact::CompositeSolid(composite_solid) => {
|
||||||
|
writeln!(
|
||||||
|
output,
|
||||||
|
"{prefix}{}[\"CompositeSolid {:?}<br>{:?}\"]",
|
||||||
|
id,
|
||||||
|
composite_solid.sub_type,
|
||||||
|
code_ref_display(&composite_solid.code_ref)
|
||||||
|
)?;
|
||||||
|
}
|
||||||
Artifact::Plane(plane) => {
|
Artifact::Plane(plane) => {
|
||||||
writeln!(
|
writeln!(
|
||||||
output,
|
output,
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 57 KiB |
@ -48,6 +48,7 @@ flowchart LR
|
|||||||
42["SweepEdge Adjacent"]
|
42["SweepEdge Adjacent"]
|
||||||
43["SweepEdge Opposite"]
|
43["SweepEdge Opposite"]
|
||||||
44["SweepEdge Adjacent"]
|
44["SweepEdge Adjacent"]
|
||||||
|
45["CompositeSolid Intersect<br>[448, 477, 0]"]
|
||||||
1 --- 2
|
1 --- 2
|
||||||
2 --- 3
|
2 --- 3
|
||||||
2 --- 4
|
2 --- 4
|
||||||
@ -114,4 +115,6 @@ flowchart LR
|
|||||||
30 --- 42
|
30 --- 42
|
||||||
30 --- 43
|
30 --- 43
|
||||||
30 --- 44
|
30 --- 44
|
||||||
|
2 <--x 45
|
||||||
|
24 <--x 45
|
||||||
```
|
```
|
||||||
|
@ -36,6 +36,7 @@ flowchart LR
|
|||||||
30["Cap End"]
|
30["Cap End"]
|
||||||
31["SweepEdge Opposite"]
|
31["SweepEdge Opposite"]
|
||||||
32["SweepEdge Adjacent"]
|
32["SweepEdge Adjacent"]
|
||||||
|
33["CompositeSolid Subtract<br>[461, 497, 0]"]
|
||||||
1 --- 2
|
1 --- 2
|
||||||
2 --- 3
|
2 --- 3
|
||||||
2 --- 4
|
2 --- 4
|
||||||
@ -81,4 +82,6 @@ flowchart LR
|
|||||||
27 --- 30
|
27 --- 30
|
||||||
27 --- 31
|
27 --- 31
|
||||||
27 --- 32
|
27 --- 32
|
||||||
|
2 <--x 33
|
||||||
|
24 <--x 33
|
||||||
```
|
```
|
||||||
|
@ -48,6 +48,7 @@ flowchart LR
|
|||||||
42["SweepEdge Adjacent"]
|
42["SweepEdge Adjacent"]
|
||||||
43["SweepEdge Opposite"]
|
43["SweepEdge Opposite"]
|
||||||
44["SweepEdge Adjacent"]
|
44["SweepEdge Adjacent"]
|
||||||
|
45["CompositeSolid Union<br>[448, 473, 0]"]
|
||||||
1 --- 2
|
1 --- 2
|
||||||
2 --- 3
|
2 --- 3
|
||||||
2 --- 4
|
2 --- 4
|
||||||
@ -114,4 +115,6 @@ flowchart LR
|
|||||||
30 --- 42
|
30 --- 42
|
||||||
30 --- 43
|
30 --- 43
|
||||||
30 --- 44
|
30 --- 44
|
||||||
|
2 <--x 45
|
||||||
|
24 <--x 45
|
||||||
```
|
```
|
||||||
|
@ -514,7 +514,9 @@ export function getCodeRefsByArtifactId(
|
|||||||
artifactGraph: ArtifactGraph
|
artifactGraph: ArtifactGraph
|
||||||
): Array<CodeRef> | null {
|
): Array<CodeRef> | null {
|
||||||
const artifact = artifactGraph.get(id)
|
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)
|
const codeRef = getSolid2dCodeRef(artifact, artifactGraph)
|
||||||
if (err(codeRef)) return null
|
if (err(codeRef)) return null
|
||||||
return [codeRef]
|
return [codeRef]
|
||||||
|
@ -71,6 +71,7 @@ export type {
|
|||||||
ArtifactId,
|
ArtifactId,
|
||||||
Cap as CapArtifact,
|
Cap as CapArtifact,
|
||||||
CodeRef,
|
CodeRef,
|
||||||
|
CompositeSolid as CompositeSolidArtifact,
|
||||||
EdgeCut,
|
EdgeCut,
|
||||||
Path as PathArtifact,
|
Path as PathArtifact,
|
||||||
Plane as PlaneArtifact,
|
Plane as PlaneArtifact,
|
||||||
|
Reference in New Issue
Block a user