pub trait MeshBoolean: Backend {
// Required method
fn boolean(
&self,
subject: &TriMesh,
tool: &TriMesh,
operation: BooleanOperator,
options: &ExecutionOptions,
) -> Result<BooleanOutcome, GeomError>;
// Provided methods
fn scratch_requirement(&self) -> ScratchRequirement { ... }
fn determinism(&self) -> Determinism { ... }
fn cancellation_granularity(&self) -> CancellationGranularity { ... }
fn solid_requirements(&self) -> SolidRequirements { ... }
fn subtract_many(
&self,
subject: &TriMesh,
tools: &[TriMesh],
options: &ExecutionOptions,
) -> Result<BooleanOutcome, GeomError> { ... }
fn union_many(
&self,
solids: &[TriMesh],
options: &ExecutionOptions,
) -> Result<BooleanOutcome, GeomError> { ... }
}Expand description
Mesh boolean provider.
Implementing this trait is the capability declaration. Providers that do not implement mesh booleans must not implement this trait.
Required Methods§
Sourcefn boolean(
&self,
subject: &TriMesh,
tool: &TriMesh,
operation: BooleanOperator,
options: &ExecutionOptions,
) -> Result<BooleanOutcome, GeomError>
fn boolean( &self, subject: &TriMesh, tool: &TriMesh, operation: BooleanOperator, options: &ExecutionOptions, ) -> Result<BooleanOutcome, GeomError>
Apply one regularized set operation.
Operands are pre-validated by the registry. Returns a
BooleanOutcome: the mesh plus what was done to produce it. An empty
result mesh is a legitimate value, not an error.
Provided Methods§
Sourcefn scratch_requirement(&self) -> ScratchRequirement
fn scratch_requirement(&self) -> ScratchRequirement
Scratch this provider needs beyond its inputs and result.
Callers budget against this before dispatch. Defaults to
ScratchRequirement::Unbounded so an unaudited provider is treated as
unbudgetable rather than silently assumed cheap.
Sourcefn determinism(&self) -> Determinism
fn determinism(&self) -> Determinism
Reproducibility this provider guarantees for its results.
Defaults to Determinism::BestEffort: the weakest level, so a
provider that has not audited its own reproducibility cannot silently
satisfy a stronger request. Overstating this is the dangerous
direction — Plan::admit refuses a step whose guarantee is weaker
than the caller asked for, and that refusal is only sound if the
declared level is honest.
A provider whose output depends on thread scheduling, hash seeding, or
any other run-to-run variation must not claim Determinism::Bitwise.
Sourcefn cancellation_granularity(&self) -> CancellationGranularity
fn cancellation_granularity(&self) -> CancellationGranularity
How finely this provider polls a cancellation token.
Defaults to CancellationGranularity::None: a provider that has not
declared otherwise is assumed not to poll. Claiming responsiveness a
provider does not have is worse than admitting none.
Sourcefn solid_requirements(&self) -> SolidRequirements
fn solid_requirements(&self) -> SolidRequirements
Admissibility this provider requires of its operands.
Advisory only: the registry validates at the contract level before dispatch. A provider declaring a lower level does not thereby get to accept looser input, and one declaring a higher level is rejected by the conformance suite for narrowing the contract.
Sourcefn subtract_many(
&self,
subject: &TriMesh,
tools: &[TriMesh],
options: &ExecutionOptions,
) -> Result<BooleanOutcome, GeomError>
fn subtract_many( &self, subject: &TriMesh, tools: &[TriMesh], options: &ExecutionOptions, ) -> Result<BooleanOutcome, GeomError>
Subtract many tools in one batch so implementations can union or schedule cutters efficiently. The default is correct but deliberately simple.
The default polls cancellation between tools, which is why the default granularity for an overriding provider must be declared honestly.
Sourcefn union_many(
&self,
solids: &[TriMesh],
options: &ExecutionOptions,
) -> Result<BooleanOutcome, GeomError>
fn union_many( &self, solids: &[TriMesh], options: &ExecutionOptions, ) -> Result<BooleanOutcome, GeomError>
Union many solids in one batch so implementations can choose a reduction order.
The default folds left, which is correct but makes step i pay for
an accumulator holding i operands – quadratic total work. A
provider that can do better should override this; see
BoolmeshBoolean::union_tree for a balanced reduction that measures
28.9x faster on a 512-sphere grid.
An empty slice yields an empty solid: the union of nothing is nothing, which is a legitimate answer rather than an error.
The default polls cancellation between operands, which is why the declared granularity for an overriding provider must stay honest.
Implementations on Foreign Types§
Source§impl MeshBoolean for BoolmeshBoolean
impl MeshBoolean for BoolmeshBoolean
Source§fn determinism(&self) -> Determinism
fn determinism(&self) -> Determinism
Honest about the general path, which is not byte-reproducible.
Upstream boolmesh dedups vertices through a randomly-seeded
HashMap, so its output ordering varies between processes even
single-threaded. That is the same root cause documented in
Self::subtract_boxes_analytic, which avoids it with a BTreeMap. The general
path therefore cannot claim Determinism::Bitwise at any thread
count, and enabling parallel does not make it weaker than it
already is.
Determinism::Topological is the honest ceiling: the result’s
connectivity is stable, its vertex ordering is not. Callers needing
byte reproducibility use Self::subtract_boxes_analytic,
whose ordered vertex identity makes it reproducible across processes.
Source§fn scratch_requirement(&self) -> ScratchRequirement
fn scratch_requirement(&self) -> ScratchRequirement
Measured, not guessed.
boolmesh builds a Morton collider and intersection tables sized by the
combined input. It exposes no bound, so this was measured directly with
a counting global allocator (src/bin/scratch_probe/) across all four
operations at 24 to 1,536 input triangles, after one discarded warmup
call so the first operation measured is not charged for process
startup (#110):
triangles peak bytes bytes/triangle worst operation
24 52424 2184 SymmetricDifference
96 98920 1030 SymmetricDifference
384 343768 895 SymmetricDifference
1536 1349668 878 SymmetricDifferenceConsumption is linear in input size, converging to under 1 KiB per
triangle; the higher ratio at small inputs is fixed setup cost being
divided by few triangles. SymmetricDifference is worst because it is
composed from three passes and holds intermediates alive.
The declared bound is 4 KiB per triangle: about 1.9x the worst
observed ratio, as headroom for allocator variance and future operand
shapes. tests/scratch_bound.rs measures the same workloads and fails
if any peak exceeds it. A declared bound that is occasionally too low
is worse than Unbounded, because it makes a budget look enforced
when it is not.
Source§fn cancellation_granularity(&self) -> CancellationGranularity
fn cancellation_granularity(&self) -> CancellationGranularity
boolmesh takes no cancellation handle, so nothing can interrupt a
single boolean once it starts. Declared honestly: the batch override
polls between groups, which is the only real poll point available.
Source§fn subtract_many(
&self,
subject: &TriMesh,
tools: &[TriMesh],
options: &ExecutionOptions,
) -> Result<BooleanOutcome, GeomError>
fn subtract_many( &self, subject: &TriMesh, tools: &[TriMesh], options: &ExecutionOptions, ) -> Result<BooleanOutcome, GeomError>
Group mutually disjoint cutters and remove each group with one boolean, instead of one boolean per cutter.
Rests on (S \ A) \ B == S \ (A union B) and on a concatenation of
disjoint solids being their union. Bounding-box grouping over-separates
but never wrongly fuses, so the result is identical to the sequential
default – gated by volume equality in tests/batch.rs.
fn union_many( &self, solids: &[TriMesh], options: &ExecutionOptions, ) -> Result<BooleanOutcome, GeomError>
fn boolean( &self, subject: &TriMesh, tool: &TriMesh, operation: BooleanOperator, options: &ExecutionOptions, ) -> Result<BooleanOutcome, GeomError>
Source§impl MeshBoolean for ScalarBoolean
impl MeshBoolean for ScalarBoolean
Source§fn scratch_requirement(&self) -> ScratchRequirement
fn scratch_requirement(&self) -> ScratchRequirement
Exact, so no filter escalation and no scratch beyond the output.
Source§fn cancellation_granularity(&self) -> CancellationGranularity
fn cancellation_granularity(&self) -> CancellationGranularity
Checked per triangle pair, which is the inner loop of the O(n·m) scan.