mirror of https://github.com/vitalif/openscad
Implemented CSG tree pruning - this should significantly improve performance of difficult models in OpenCSG preview mode
parent
d7ca192077
commit
8d5be2c524
|
@ -48,11 +48,11 @@ void CSGTermEvaluator::applyToChildren(const AbstractNode &node, CSGTermEvaluato
|
|||
t1 = t2;
|
||||
} else if (t2 && t1) {
|
||||
if (op == CSGT_UNION) {
|
||||
t1.reset(new CSGTerm(CSGTerm::TYPE_UNION, t1, t2));
|
||||
t1 = CSGTerm::createCSGTerm(CSGTerm::TYPE_UNION, t1, t2);
|
||||
} else if (op == CSGT_DIFFERENCE) {
|
||||
t1.reset(new CSGTerm(CSGTerm::TYPE_DIFFERENCE, t1, t2));
|
||||
t1 = CSGTerm::createCSGTerm(CSGTerm::TYPE_DIFFERENCE, t1, t2);
|
||||
} else if (op == CSGT_INTERSECTION) {
|
||||
t1.reset(new CSGTerm(CSGTerm::TYPE_INTERSECTION, t1, t2));
|
||||
t1 = CSGTerm::createCSGTerm(CSGTerm::TYPE_INTERSECTION, t1, t2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
179
src/csgterm.cc
179
src/csgterm.cc
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include "csgterm.h"
|
||||
#include "polyset.h"
|
||||
#include "linalg.h"
|
||||
#include <sstream>
|
||||
|
||||
/*!
|
||||
|
@ -47,29 +48,102 @@
|
|||
*/
|
||||
|
||||
|
||||
CSGTerm::CSGTerm(const shared_ptr<PolySet> &polyset, const Transform3d &matrix, const double color[4], const std::string &label)
|
||||
: type(TYPE_PRIMITIVE), polyset(polyset), label(label)
|
||||
shared_ptr<CSGTerm> CSGTerm::createCSGTerm(type_e type, shared_ptr<CSGTerm> left, shared_ptr<CSGTerm> right)
|
||||
{
|
||||
if (type != TYPE_PRIMITIVE) {
|
||||
// In case we're creating a CSG terms from a pruned tree, left/right can be NULL
|
||||
if (!right) {
|
||||
if (type == TYPE_UNION || type == TYPE_DIFFERENCE) return left;
|
||||
else return right;
|
||||
}
|
||||
if (!left) {
|
||||
if (type == TYPE_UNION) return right;
|
||||
else return left;
|
||||
}
|
||||
}
|
||||
|
||||
// Pruning the tree. For details, see:
|
||||
// http://www.cc.gatech.edu/~turk/my_papers/pxpl_csg.pdf
|
||||
const BoundingBox &leftbox = left->getBoundingBox();
|
||||
const BoundingBox &rightbox = right->getBoundingBox();
|
||||
if (type == TYPE_INTERSECTION) {
|
||||
BoundingBox newbox(leftbox.min().cwise().max(rightbox.min()),
|
||||
leftbox.max().cwise().min(rightbox.max()));
|
||||
if (newbox.isNull()) {
|
||||
return shared_ptr<CSGTerm>(); // Prune entire product
|
||||
}
|
||||
}
|
||||
else if (type == TYPE_DIFFERENCE) {
|
||||
BoundingBox newbox(leftbox.min().cwise().max(rightbox.min()),
|
||||
leftbox.max().cwise().min(rightbox.max()));
|
||||
if (newbox.isNull()) {
|
||||
return left; // Prune the negative component
|
||||
}
|
||||
}
|
||||
|
||||
return shared_ptr<CSGTerm>(new CSGTerm(type, left, right));
|
||||
}
|
||||
|
||||
shared_ptr<CSGTerm> CSGTerm::createCSGTerm(type_e type, CSGTerm *left, CSGTerm *right)
|
||||
{
|
||||
return createCSGTerm(type, shared_ptr<CSGTerm>(left), shared_ptr<CSGTerm>(right));
|
||||
}
|
||||
|
||||
CSGTerm::CSGTerm(const shared_ptr<PolySet> &polyset, const Transform3d &matrix, const double color[4], const std::string &label)
|
||||
: type(TYPE_PRIMITIVE), polyset(polyset), label(label), m(matrix)
|
||||
{
|
||||
this->m = matrix;
|
||||
for (int i = 0; i < 4; i++) this->color[i] = color[i];
|
||||
initBoundingBox();
|
||||
}
|
||||
|
||||
CSGTerm::CSGTerm(type_e type, shared_ptr<CSGTerm> left, shared_ptr<CSGTerm> right)
|
||||
: type(type), left(left), right(right)
|
||||
: type(type), left(left), right(right), m(Transform3d::Identity())
|
||||
{
|
||||
initBoundingBox();
|
||||
}
|
||||
|
||||
CSGTerm::CSGTerm(type_e type, CSGTerm *left, CSGTerm *right)
|
||||
: type(type), left(left), right(right)
|
||||
: type(type), left(left), right(right), m(Transform3d::Identity())
|
||||
{
|
||||
initBoundingBox();
|
||||
}
|
||||
|
||||
CSGTerm::~CSGTerm()
|
||||
{
|
||||
}
|
||||
|
||||
void CSGTerm::initBoundingBox()
|
||||
{
|
||||
if (this->type == TYPE_PRIMITIVE) {
|
||||
BoundingBox polybox = this->polyset->getBoundingBox();
|
||||
this->bbox.extend(this->m * polybox.min());
|
||||
this->bbox.extend(this->m * polybox.max());
|
||||
}
|
||||
else {
|
||||
const BoundingBox &leftbox = this->left->getBoundingBox();
|
||||
const BoundingBox &rightbox = this->right->getBoundingBox();
|
||||
switch (this->type) {
|
||||
case TYPE_UNION:
|
||||
this->bbox.extend(this->m * leftbox.min().cwise().min(rightbox.min()));
|
||||
this->bbox.extend(this->m * leftbox.max().cwise().max(rightbox.max()));
|
||||
break;
|
||||
case TYPE_INTERSECTION:
|
||||
this->bbox.extend(this->m * leftbox.min().cwise().max(rightbox.min()));
|
||||
this->bbox.extend(this->m * leftbox.max().cwise().min(rightbox.max()));
|
||||
break;
|
||||
case TYPE_DIFFERENCE:
|
||||
this->bbox.extend(this->m * leftbox.min());
|
||||
this->bbox.extend(this->m * leftbox.max());
|
||||
break;
|
||||
case TYPE_PRIMITIVE:
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<CSGTerm> CSGTerm::normalize(shared_ptr<CSGTerm> &term)
|
||||
shared_ptr<CSGTerm> CSGTerm::normalize(shared_ptr<CSGTerm> term)
|
||||
{
|
||||
// This function implements the CSG normalization
|
||||
// Reference:
|
||||
|
@ -79,27 +153,34 @@ shared_ptr<CSGTerm> CSGTerm::normalize(shared_ptr<CSGTerm> &term)
|
|||
// 1989.
|
||||
// http://www.cc.gatech.edu/~turk/my_papers/pxpl_csg.pdf
|
||||
|
||||
if (term->type == TYPE_PRIMITIVE) return term;
|
||||
if (term->type == TYPE_PRIMITIVE) {
|
||||
return term;
|
||||
}
|
||||
|
||||
// FIXME: We can optimize the normalized tree by pruning based on bounding boxes
|
||||
// as described in the above mentioned paper:
|
||||
// 1) If the bounding boxes of two intersected nodes don't intersect, prune the
|
||||
// intersection node
|
||||
// 2) If the bounding boxes of two subtracted nodes don't intersect, replace the
|
||||
// difference node with the positive operator
|
||||
do {
|
||||
while (normalize_tail(term)) {}
|
||||
normalize(term->left);
|
||||
while (term && normalize_tail(term)) { }
|
||||
if (!term || term->type == TYPE_PRIMITIVE) return term;
|
||||
term->left = normalize(term->left);
|
||||
} while (term->type != TYPE_UNION &&
|
||||
(term->right->type != TYPE_PRIMITIVE || term->left->type == TYPE_UNION));
|
||||
normalize(term->right);
|
||||
term->right = normalize(term->right);
|
||||
|
||||
// FIXME: Do we need to take into account any transformation of item here?
|
||||
if (!term->right) {
|
||||
if (term->type == TYPE_UNION || term->type == TYPE_DIFFERENCE) return term->left;
|
||||
else return term->right;
|
||||
}
|
||||
if (!term->left) {
|
||||
if (term->type == TYPE_UNION) return term->right;
|
||||
else return term->left;
|
||||
}
|
||||
|
||||
return term;
|
||||
}
|
||||
|
||||
bool CSGTerm::normalize_tail(shared_ptr<CSGTerm> &term)
|
||||
{
|
||||
if (term->type == TYPE_UNION) return false; // Don't normalize outer union
|
||||
if (term->type == TYPE_UNION || term->type == TYPE_PRIMITIVE) return false;
|
||||
|
||||
// Part A: The 'x . (y . z)' expressions
|
||||
|
||||
|
@ -107,46 +188,48 @@ bool CSGTerm::normalize_tail(shared_ptr<CSGTerm> &term)
|
|||
shared_ptr<CSGTerm> y = term->right->left;
|
||||
shared_ptr<CSGTerm> z = term->right->right;
|
||||
|
||||
CSGTerm *result = NULL;
|
||||
shared_ptr<CSGTerm> result = term;
|
||||
|
||||
// 1. x - (y + z) -> (x - y) - z
|
||||
if (term->type == TYPE_DIFFERENCE && term->right->type == TYPE_UNION) {
|
||||
result = new CSGTerm(TYPE_DIFFERENCE,
|
||||
shared_ptr<CSGTerm>(new CSGTerm(TYPE_DIFFERENCE, x, y)),
|
||||
term = createCSGTerm(TYPE_DIFFERENCE,
|
||||
createCSGTerm(TYPE_DIFFERENCE, x, y),
|
||||
z);
|
||||
return true;
|
||||
}
|
||||
// 2. x * (y + z) -> (x * y) + (x * z)
|
||||
else if (term->type == TYPE_INTERSECTION && term->right->type == TYPE_UNION) {
|
||||
result = new CSGTerm(TYPE_UNION,
|
||||
new CSGTerm(TYPE_INTERSECTION, x, y),
|
||||
new CSGTerm(TYPE_INTERSECTION, x, z));
|
||||
term = createCSGTerm(TYPE_UNION,
|
||||
createCSGTerm(TYPE_INTERSECTION, x, y),
|
||||
createCSGTerm(TYPE_INTERSECTION, x, z));
|
||||
return true;
|
||||
}
|
||||
// 3. x - (y * z) -> (x - y) + (x - z)
|
||||
else if (term->type == TYPE_DIFFERENCE && term->right->type == TYPE_INTERSECTION) {
|
||||
result = new CSGTerm(TYPE_UNION,
|
||||
new CSGTerm(TYPE_DIFFERENCE, x, y),
|
||||
new CSGTerm(TYPE_DIFFERENCE, x, z));
|
||||
term = createCSGTerm(TYPE_UNION,
|
||||
createCSGTerm(TYPE_DIFFERENCE, x, y),
|
||||
createCSGTerm(TYPE_DIFFERENCE, x, z));
|
||||
return true;
|
||||
}
|
||||
// 4. x * (y * z) -> (x * y) * z
|
||||
else if (term->type == TYPE_INTERSECTION && term->right->type == TYPE_INTERSECTION) {
|
||||
result = new CSGTerm(TYPE_INTERSECTION,
|
||||
shared_ptr<CSGTerm>(new CSGTerm(TYPE_INTERSECTION, x, y)),
|
||||
term = createCSGTerm(TYPE_INTERSECTION,
|
||||
createCSGTerm(TYPE_INTERSECTION, x, y),
|
||||
z);
|
||||
return true;
|
||||
}
|
||||
// 5. x - (y - z) -> (x - y) + (x * z)
|
||||
else if (term->type == TYPE_DIFFERENCE && term->right->type == TYPE_DIFFERENCE) {
|
||||
result = new CSGTerm(TYPE_UNION,
|
||||
new CSGTerm(TYPE_DIFFERENCE, x, y),
|
||||
new CSGTerm(TYPE_INTERSECTION, x, z));
|
||||
term = createCSGTerm(TYPE_UNION,
|
||||
createCSGTerm(TYPE_DIFFERENCE, x, y),
|
||||
createCSGTerm(TYPE_INTERSECTION, x, z));
|
||||
return true;
|
||||
}
|
||||
// 6. x * (y - z) -> (x * y) - z
|
||||
else if (term->type == TYPE_INTERSECTION && term->right->type == TYPE_DIFFERENCE) {
|
||||
result = new CSGTerm(TYPE_DIFFERENCE,
|
||||
shared_ptr<CSGTerm>(new CSGTerm(TYPE_INTERSECTION, x, y)),
|
||||
term = createCSGTerm(TYPE_DIFFERENCE,
|
||||
createCSGTerm(TYPE_INTERSECTION, x, y),
|
||||
z);
|
||||
}
|
||||
if (result) {
|
||||
term.reset(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -158,28 +241,26 @@ bool CSGTerm::normalize_tail(shared_ptr<CSGTerm> &term)
|
|||
|
||||
// 7. (x - y) * z -> (x * z) - y
|
||||
if (term->left->type == TYPE_DIFFERENCE && term->type == TYPE_INTERSECTION) {
|
||||
result = new CSGTerm(TYPE_DIFFERENCE,
|
||||
shared_ptr<CSGTerm>(new CSGTerm(TYPE_INTERSECTION, x, z)),
|
||||
term = createCSGTerm(TYPE_DIFFERENCE,
|
||||
createCSGTerm(TYPE_INTERSECTION, x, z),
|
||||
y);
|
||||
return true;
|
||||
}
|
||||
// 8. (x + y) - z -> (x - z) + (y - z)
|
||||
else if (term->left->type == TYPE_UNION && term->type == TYPE_DIFFERENCE) {
|
||||
result = new CSGTerm(TYPE_UNION,
|
||||
new CSGTerm(TYPE_DIFFERENCE, x, z),
|
||||
new CSGTerm(TYPE_DIFFERENCE, y, z));
|
||||
term = createCSGTerm(TYPE_UNION,
|
||||
createCSGTerm(TYPE_DIFFERENCE, x, z),
|
||||
createCSGTerm(TYPE_DIFFERENCE, y, z));
|
||||
return true;
|
||||
}
|
||||
// 9. (x + y) * z -> (x * z) + (y * z)
|
||||
else if (term->left->type == TYPE_UNION && term->type == TYPE_INTERSECTION) {
|
||||
result = new CSGTerm(TYPE_UNION,
|
||||
new CSGTerm(TYPE_INTERSECTION, x, z),
|
||||
new CSGTerm(TYPE_INTERSECTION, y, z));
|
||||
}
|
||||
|
||||
if (result) {
|
||||
term.reset(result);
|
||||
term = createCSGTerm(TYPE_UNION,
|
||||
createCSGTerm(TYPE_INTERSECTION, x, z),
|
||||
createCSGTerm(TYPE_INTERSECTION, y, z));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,23 +18,35 @@ public:
|
|||
TYPE_DIFFERENCE
|
||||
};
|
||||
|
||||
static shared_ptr<CSGTerm> createCSGTerm(type_e type, shared_ptr<CSGTerm> left, shared_ptr<CSGTerm> right);
|
||||
static shared_ptr<CSGTerm> createCSGTerm(type_e type, CSGTerm *left, CSGTerm *right);
|
||||
|
||||
type_e type;
|
||||
shared_ptr<PolySet> polyset;
|
||||
std::string label;
|
||||
shared_ptr<CSGTerm> left;
|
||||
shared_ptr<CSGTerm> right;
|
||||
Transform3d m;
|
||||
double color[4];
|
||||
BoundingBox bbox;
|
||||
|
||||
CSGTerm(const shared_ptr<PolySet> &polyset, const Transform3d &matrix, const double color[4], const std::string &label);
|
||||
CSGTerm(type_e type, shared_ptr<CSGTerm> left, shared_ptr<CSGTerm> right);
|
||||
CSGTerm(type_e type, CSGTerm *left, CSGTerm *right);
|
||||
~CSGTerm();
|
||||
|
||||
static shared_ptr<CSGTerm> normalize(shared_ptr<CSGTerm> &term);
|
||||
const BoundingBox &getBoundingBox() const { return this->bbox; }
|
||||
|
||||
static shared_ptr<CSGTerm> normalize(shared_ptr<CSGTerm> term);
|
||||
static bool normalize_tail(shared_ptr<CSGTerm> &term);
|
||||
|
||||
std::string dump();
|
||||
private:
|
||||
CSGTerm(type_e type, shared_ptr<CSGTerm> left, shared_ptr<CSGTerm> right);
|
||||
CSGTerm(type_e type, CSGTerm *left, CSGTerm *right);
|
||||
|
||||
void initBoundingBox();
|
||||
|
||||
Transform3d m;
|
||||
double color[4];
|
||||
|
||||
friend class CSGChain;
|
||||
};
|
||||
|
||||
class CSGChain
|
||||
|
|
Loading…
Reference in New Issue