@@ -73,6 +73,19 @@ struct CheckOverlap {
73
73
} // namespace
74
74
namespace manifold {
75
75
76
+ std::shared_ptr<CsgNode> CsgNode::Boolean (
77
+ const std::shared_ptr<CsgNode> &second, OpType op) {
78
+ if (auto opNode = std::dynamic_pointer_cast<CsgOpNode>(second)) {
79
+ // "this" is not a CsgOpNode (which overrides Boolean), but if "second" is
80
+ // and the operation is commutative, we let it built the tree.
81
+ if ((op == OpType::Add || op == OpType::Intersect)) {
82
+ return opNode->Boolean (shared_from_this (), op);
83
+ }
84
+ }
85
+ std::vector<std::shared_ptr<CsgNode>> children ({shared_from_this (), second});
86
+ return std::make_shared<CsgOpNode>(children, op);
87
+ }
88
+
76
89
std::shared_ptr<CsgNode> CsgNode::Translate (const glm::vec3 &t) const {
77
90
glm::mat4x3 transform (1 .0f );
78
91
transform[3 ] += t;
@@ -227,8 +240,6 @@ CsgOpNode::CsgOpNode(const std::vector<std::shared_ptr<CsgNode>> &children,
227
240
auto impl = impl_.GetGuard ();
228
241
impl->children_ = children;
229
242
SetOp (op);
230
- // opportunistically flatten the tree without costly evaluation
231
- GetChildren (false );
232
243
}
233
244
234
245
CsgOpNode::CsgOpNode (std::vector<std::shared_ptr<CsgNode>> &&children,
@@ -237,8 +248,50 @@ CsgOpNode::CsgOpNode(std::vector<std::shared_ptr<CsgNode>> &&children,
237
248
auto impl = impl_.GetGuard ();
238
249
impl->children_ = children;
239
250
SetOp (op);
240
- // opportunistically flatten the tree without costly evaluation
241
- GetChildren (false );
251
+ }
252
+
253
+ std::shared_ptr<CsgNode> CsgOpNode::Boolean (
254
+ const std::shared_ptr<CsgNode> &second, OpType op) {
255
+ std::vector<std::shared_ptr<CsgNode>> children;
256
+
257
+ auto isReused = [](const auto &node) { return node->impl_ .UseCount () > 1 ; };
258
+
259
+ auto copyChildren = [&](const auto &list, const glm::mat4x3 &transform) {
260
+ for (const auto &child : list) {
261
+ children.push_back (child->Transform (transform));
262
+ }
263
+ };
264
+
265
+ auto self = std::dynamic_pointer_cast<CsgOpNode>(shared_from_this ());
266
+ assert (self);
267
+ if (IsOp (op) && !isReused (self)) {
268
+ auto impl = impl_.GetGuard ();
269
+ copyChildren (impl->children_ , transform_);
270
+ } else {
271
+ children.push_back (self);
272
+ }
273
+
274
+ auto secondOp = std::dynamic_pointer_cast<CsgOpNode>(second);
275
+ auto canInlineSecondOp = [&]() {
276
+ switch (op) {
277
+ case OpType::Add:
278
+ case OpType::Intersect:
279
+ return secondOp->IsOp (op);
280
+ case OpType::Subtract:
281
+ return secondOp->IsOp (OpType::Add);
282
+ default :
283
+ return false ;
284
+ }
285
+ };
286
+
287
+ if (secondOp && canInlineSecondOp () && !isReused (secondOp)) {
288
+ auto secondImpl = secondOp->impl_ .GetGuard ();
289
+ copyChildren (secondImpl->children_ , secondOp->transform_ );
290
+ } else {
291
+ children.push_back (second);
292
+ }
293
+
294
+ return std::make_shared<CsgOpNode>(children, op);
242
295
}
243
296
244
297
std::shared_ptr<CsgNode> CsgOpNode::Transform (const glm::mat4x3 &m) const {
@@ -410,44 +463,24 @@ void CsgOpNode::BatchUnion() const {
410
463
411
464
/* *
412
465
* Flatten the children to a list of leaf nodes and return them.
413
- * If finalize is true, the list will be guaranteed to be a list of leaf nodes
414
- * (i.e. no ops). Otherwise, the list may contain ops.
415
- * Note that this function will not apply the transform to children, as they may
416
- * be shared with other nodes.
466
+ * If forceToLeafNodes is true, the list will be guaranteed to be a list of leaf
467
+ * nodes (i.e. no ops). Otherwise, the list may contain ops. Note that this
468
+ * function will not apply the transform to children, as they may be shared with
469
+ * other nodes.
417
470
*/
418
471
std::vector<std::shared_ptr<CsgNode>> &CsgOpNode::GetChildren (
419
- bool finalize ) const {
472
+ bool forceToLeafNodes ) const {
420
473
auto impl = impl_.GetGuard ();
421
- auto &children_ = impl->children_ ;
422
- if (children_.empty () || (impl->simplified_ && !finalize) || impl->flattened_ )
423
- return children_;
424
- impl->simplified_ = true ;
425
- impl->flattened_ = finalize;
426
- std::vector<std::shared_ptr<CsgNode>> newChildren;
427
-
428
- CsgNodeType op = op_;
429
- for (auto &child : children_) {
430
- if (child->GetNodeType () == op && child.use_count () == 1 &&
431
- std::dynamic_pointer_cast<CsgOpNode>(child)->impl_ .UseCount () == 1 ) {
432
- auto grandchildren =
433
- std::dynamic_pointer_cast<CsgOpNode>(child)->GetChildren (finalize);
434
- int start = children_.size ();
435
- for (auto &grandchild : grandchildren) {
436
- newChildren.push_back (grandchild->Transform (child->GetTransform ()));
437
- }
438
- } else {
439
- if (!finalize || child->GetNodeType () == CsgNodeType::Leaf) {
440
- newChildren.push_back (child);
441
- } else {
442
- newChildren.push_back (child->ToLeafNode ());
474
+
475
+ if (forceToLeafNodes && !impl->forcedToLeafNodes_ ) {
476
+ impl->forcedToLeafNodes_ = true ;
477
+ for (auto &child : impl->children_ ) {
478
+ if (child->GetNodeType () != CsgNodeType::Leaf) {
479
+ child = child->ToLeafNode ();
443
480
}
444
481
}
445
- // special handling for difference: we treat it as first - (second + third +
446
- // ...) so op = Union after the first node
447
- if (op == CsgNodeType::Difference) op = CsgNodeType::Union;
448
482
}
449
- children_ = newChildren;
450
- return children_;
483
+ return impl->children_ ;
451
484
}
452
485
453
486
void CsgOpNode::SetOp (OpType op) {
@@ -464,6 +497,19 @@ void CsgOpNode::SetOp(OpType op) {
464
497
}
465
498
}
466
499
500
+ bool CsgOpNode::IsOp (OpType op) {
501
+ switch (op) {
502
+ case OpType::Add:
503
+ return op_ == CsgNodeType::Union;
504
+ case OpType::Subtract:
505
+ return op_ == CsgNodeType::Difference;
506
+ case OpType::Intersect:
507
+ return op_ == CsgNodeType::Intersection;
508
+ default :
509
+ return false ;
510
+ }
511
+ }
512
+
467
513
glm::mat4x3 CsgOpNode::GetTransform () const { return transform_; }
468
514
469
515
} // namespace manifold
0 commit comments