2020
2121#include " boolean3.h"
2222#include " csg_tree.h"
23+ #include " execution_impl.h"
2324#include " impl.h"
2425#include " mesh_fixes.h"
2526#include " parallel.h"
@@ -96,7 +97,8 @@ std::shared_ptr<const Manifold::Impl> CsgLeafNode::GetImpl() const {
9697 return pImpl_;
9798}
9899
99- std::shared_ptr<CsgLeafNode> CsgLeafNode::ToLeafNode () const {
100+ std::shared_ptr<CsgLeafNode> CsgLeafNode::ToLeafNode (
101+ ExecutionContext::Impl*) const {
100102 return std::make_shared<CsgLeafNode>(*this );
101103}
102104
@@ -139,8 +141,19 @@ std::shared_ptr<CsgLeafNode> ImplToLeaf(Manifold::Impl&& impl) {
139141 std::make_shared<Manifold::Impl>(std::move (impl)));
140142}
141143
144+ // Build a leaf with the given error status — used to short-circuit boolean
145+ // evaluation on cancellation.
146+ std::shared_ptr<CsgLeafNode> ErrorLeaf (Manifold::Error err) {
147+ Manifold::Impl impl;
148+ impl.status_ = err;
149+ return ImplToLeaf (std::move (impl));
150+ }
151+
142152std::shared_ptr<CsgLeafNode> SimpleBoolean (const Manifold::Impl& a,
143- const Manifold::Impl& b, OpType op) {
153+ const Manifold::Impl& b, OpType op,
154+ ExecutionContext::Impl* ctx) {
155+ if (ctx && ctx->cancel .load (std::memory_order_relaxed))
156+ return ErrorLeaf (Manifold::Error::Cancelled);
144157#ifdef MANIFOLD_DEBUG
145158 auto dump = [&]() {
146159 dump_lock.lock ();
@@ -170,6 +183,7 @@ std::shared_ptr<CsgLeafNode> SimpleBoolean(const Manifold::Impl& a,
170183 dump_lock.unlock ();
171184 throw logicErr (" self intersection detected" );
172185 }
186+ if (ctx) ctx->doneBooleans .fetch_add (1 , std::memory_order_relaxed);
173187 return ImplToLeaf (std::move (impl));
174188 } catch (logicErr& err) {
175189 dump ();
@@ -179,7 +193,9 @@ std::shared_ptr<CsgLeafNode> SimpleBoolean(const Manifold::Impl& a,
179193 throw err;
180194 }
181195#else
182- return ImplToLeaf (Boolean3 (a, b, op).Result (op));
196+ auto leaf = ImplToLeaf (Boolean3 (a, b, op).Result (op));
197+ if (ctx) ctx->doneBooleans .fetch_add (1 , std::memory_order_relaxed);
198+ return leaf;
183199#endif
184200}
185201
@@ -374,7 +390,8 @@ std::shared_ptr<CsgLeafNode> CsgLeafNode::Compose(
374390 * operation. Only supports union and intersection.
375391 */
376392std::shared_ptr<CsgLeafNode> BatchBoolean (
377- OpType operation, std::vector<std::shared_ptr<CsgLeafNode>>& results) {
393+ OpType operation, std::vector<std::shared_ptr<CsgLeafNode>>& results,
394+ ExecutionContext::Impl* ctx) {
378395 ZoneScoped;
379396 DEBUG_ASSERT (operation != OpType::Subtract, logicErr,
380397 " BatchBoolean doesn't support Difference." );
@@ -383,7 +400,7 @@ std::shared_ptr<CsgLeafNode> BatchBoolean(
383400 if (results.size () == 1 ) return results.front ();
384401 if (results.size () == 2 )
385402 return SimpleBoolean (*results[0 ]->GetImpl (), *results[1 ]->GetImpl (),
386- operation);
403+ operation, ctx );
387404 // apply boolean operations starting from smaller meshes
388405 // the assumption is that boolean operations on smaller meshes is faster,
389406 // due to less data being copied and processed
@@ -397,6 +414,8 @@ std::shared_ptr<CsgLeafNode> BatchBoolean(
397414 for (int i = 0 ; i < 4 ; i++) parallelTmp.push_back (nullptr );
398415#endif
399416 while (results.size () > 1 ) {
417+ if (ctx && ctx->cancel .load (std::memory_order_relaxed))
418+ return ErrorLeaf (Manifold::Error::Cancelled);
400419 for (size_t i = 0 ; i < 4 && results.size () > 1 ; i++) {
401420 std::pop_heap (results.begin (), results.end (), cmpFn);
402421 auto a = std::move (results.back ());
@@ -406,10 +425,11 @@ std::shared_ptr<CsgLeafNode> BatchBoolean(
406425 results.pop_back ();
407426#if MANIFOLD_PAR == 1
408427 group.run ([&, i, a, b]() {
409- parallelTmp[i] = SimpleBoolean (*a->GetImpl (), *b->GetImpl (), operation);
428+ parallelTmp[i] =
429+ SimpleBoolean (*a->GetImpl (), *b->GetImpl (), operation, ctx);
410430 });
411431#else
412- auto result = SimpleBoolean (*a->GetImpl (), *b->GetImpl (), operation);
432+ auto result = SimpleBoolean (*a->GetImpl (), *b->GetImpl (), operation, ctx );
413433 tmp.push_back (result);
414434#endif
415435 }
@@ -432,7 +452,8 @@ std::shared_ptr<CsgLeafNode> BatchBoolean(
432452 * possible.
433453 */
434454std::shared_ptr<CsgLeafNode> BatchUnion (
435- std::vector<std::shared_ptr<CsgLeafNode>>& children) {
455+ std::vector<std::shared_ptr<CsgLeafNode>>& children,
456+ ExecutionContext::Impl* ctx) {
436457 ZoneScoped;
437458 // INVARIANT: children_ is a vector of leaf nodes
438459 // this kMaxUnionSize is a heuristic to avoid the pairwise disjoint check
@@ -443,6 +464,8 @@ std::shared_ptr<CsgLeafNode> BatchUnion(
443464 DEBUG_ASSERT (!children.empty (), logicErr,
444465 " BatchUnion should not have empty children" );
445466 while (children.size () > 1 ) {
467+ if (ctx && ctx->cancel .load (std::memory_order_relaxed))
468+ return ErrorLeaf (Manifold::Error::Cancelled);
446469 const size_t start = (children.size () > kMaxUnionSize )
447470 ? (children.size () - kMaxUnionSize )
448471 : 0 ;
@@ -478,11 +501,16 @@ std::shared_ptr<CsgLeafNode> BatchUnion(
478501 tmp.push_back (children[start + j]);
479502 }
480503 impls.push_back (CsgLeafNode::Compose (tmp));
504+ // Compose absorbs set.size() leaves into 1 leaf, which is
505+ // set.size() - 1 leaf-reductions toward the final result.
506+ if (ctx)
507+ ctx->doneBooleans .fetch_add (static_cast <int >(set.size () - 1 ),
508+ std::memory_order_relaxed);
481509 }
482510 }
483511
484512 children.erase (children.begin () + start, children.end ());
485- children.push_back (BatchBoolean (OpType::Add, impls));
513+ children.push_back (BatchBoolean (OpType::Add, impls, ctx ));
486514 // move it to the front as we process from the back, and the newly added
487515 // child should be quite complicated
488516 std::swap (children.front (), children.back ());
@@ -563,7 +591,8 @@ struct CsgStackFrame {
563591 op_node(op_node) {}
564592};
565593
566- std::shared_ptr<CsgLeafNode> CsgOpNode::ToLeafNode () const {
594+ std::shared_ptr<CsgLeafNode> CsgOpNode::ToLeafNode (
595+ ExecutionContext::Impl* ctx) const {
567596 ZoneScoped;
568597 if (cache_ != nullptr ) return cache_;
569598
@@ -666,31 +695,37 @@ std::shared_ptr<CsgLeafNode> CsgOpNode::ToLeafNode() const {
666695 // destination->push_back(node->cache_->Transform(transform));
667696 // }
668697 while (!stack.empty ()) {
698+ if (ctx && ctx->cancel .load (std::memory_order_relaxed)) {
699+ cache_ = ErrorLeaf (Manifold::Error::Cancelled);
700+ return cache_;
701+ }
669702 std::shared_ptr<CsgStackFrame> frame = stack.back ();
670703 auto impl = frame->op_node ->impl_ .GetGuard ();
671704 if (frame->finalize ) {
672705 if (!frame->op_node ->cache_ ) {
673706 switch (frame->op_node ->op_ ) {
674707 case OpType::Add:
675- *impl = {BatchUnion (frame->positive_children )};
708+ *impl = {BatchUnion (frame->positive_children , ctx )};
676709 break ;
677710 case OpType::Intersect: {
678- *impl = {BatchBoolean (OpType::Intersect, frame->positive_children )};
711+ *impl = {
712+ BatchBoolean (OpType::Intersect, frame->positive_children , ctx)};
679713 break ;
680714 };
681715 case OpType::Subtract:
682716 if (frame->positive_children .empty ()) {
683717 // nothing to subtract from, so the result is empty.
684718 *impl = {std::make_shared<CsgLeafNode>()};
685719 } else {
686- auto positive = BatchUnion (frame->positive_children );
720+ auto positive = BatchUnion (frame->positive_children , ctx );
687721 if (frame->negative_children .empty ()) {
688722 // nothing to subtract, result equal to the LHS.
689723 *impl = {frame->positive_children [0 ]};
690724 } else {
691- auto negative = BatchUnion (frame->negative_children );
725+ auto negative = BatchUnion (frame->negative_children , ctx );
692726 *impl = {SimpleBoolean (*positive->GetImpl (),
693- *negative->GetImpl (), OpType::Subtract)};
727+ *negative->GetImpl (), OpType::Subtract,
728+ ctx)};
694729 }
695730 }
696731 break ;
@@ -761,4 +796,14 @@ CsgNodeType CsgOpNode::GetNodeType() const {
761796 return CsgNodeType::Leaf;
762797}
763798
799+ size_t CsgOpNode::NumLeaves () const {
800+ // An already-evaluated CsgOpNode counts as a single leaf for the purposes
801+ // of estimating remaining boolean work.
802+ if (cache_ != nullptr ) return 1 ;
803+ auto impl = impl_.GetGuard ();
804+ size_t total = 0 ;
805+ for (const auto & child : *impl) total += child->NumLeaves ();
806+ return total;
807+ }
808+
764809} // namespace manifold
0 commit comments