geop_ops_parts/operation/
boolean.rs1use geop_core_math::{
5 geop_error::{GeopResult, WithContext},
6 scalars::Scalar,
7 with_context,
8};
9use geop_core_part::{Namer, Part};
10use geop_core_topology::SolidId;
11use geop_ops_booleans::{
12 boolean::{BooleanOp, boolean},
13 remesh::remesh::RemeshParams,
14};
15use geop_ops_parts_derive::OperationArgs;
16use serde::{Deserialize, Serialize};
17
18use super::Operation;
19
20#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
32pub struct Boolean;
33
34#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, OperationArgs)]
35pub struct BooleanArgs {
36 #[arg(Solid)]
38 pub a: String,
39 #[arg(Solid)]
41 pub b: String,
42 #[arg(Choice { options: &["union", "intersection", "difference"], default: "difference" })]
44 pub op: BooleanOp,
45}
46
47impl<S: Scalar> Operation<S> for Boolean {
48 type Args = BooleanArgs;
49
50 fn apply(
51 &self,
52 mut part: Part<S>,
53 operation_id: &str,
54 args: &BooleanArgs,
55 ) -> GeopResult<Part<S>> {
56 let ctx = with_context!("boolean({operation_id}, {args:?})");
57 let namer = Namer::new("boolean", operation_id)?;
58 let a = part.solid_id(&args.a).with_context(ctx)?;
59 let b = part.solid_id(&args.b).with_context(ctx)?;
60 boolean(&mut part, &namer, a, b, args.op, RemeshParams::default()).with_context(ctx)?;
61 Ok(part)
62 }
63}
64
65#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
76#[serde(tag = "mode", rename_all = "snake_case")]
77pub enum Combine {
78 #[default]
79 NewBody,
80 Union {
81 target: String,
82 },
83 Intersection {
84 target: String,
85 },
86 Difference {
87 target: String,
88 },
89}
90
91impl Combine {
92 pub(crate) fn built_name(&self, namer: &Namer) -> String {
95 match self {
96 Combine::NewBody => namer.root(),
97 _ => namer.name(&["tool"]),
98 }
99 }
100
101 pub(crate) fn apply<S: Scalar>(
106 &self,
107 part: &mut Part<S>,
108 namer: &Namer,
109 operation_id: &str,
110 built: SolidId,
111 ) -> GeopResult<()> {
112 let (op, target) = match self {
113 Combine::NewBody => return Ok(()),
114 Combine::Union { target } => (BooleanOp::Union, target),
115 Combine::Intersection { target } => (BooleanOp::Intersection, target),
116 Combine::Difference { target } => (BooleanOp::Difference, target),
117 };
118 let ctx = with_context!("combining with {target:?} ({op:?})");
119 let target = part.solid_id(target).with_context(ctx)?;
120 let combine = Namer::new("combine", operation_id)?;
121 let result = boolean(part, &combine, target, built, op, RemeshParams::default())
122 .with_context(ctx)?;
123 if let Some(result) = result {
124 part.rename(result, namer.root())?;
125 }
126 Ok(())
127 }
128}