Another refactoring session:

o mk_cache_id() obsoleted by removing the node index from the dump
o node index output removed from each node and make optional in NodeDumper
o The visitors are no longer global, but associated with a tree
o Added Tree class to manage node trees and the (now implicit) dump cache
o Moved PolySet cache into PolySetRenderer
stl_dim
Marius Kintel 2010-04-12 02:16:36 +02:00
parent 53a9953b7d
commit e8e213b3c9
35 changed files with 296 additions and 274 deletions

View File

@ -22,8 +22,6 @@
#include <assert.h>
#include <QRegExp>
CGALRenderer *CGALRenderer::global_renderer = NULL;
CGAL_Nef_polyhedron CGALRenderer::renderCGALMesh(const AbstractNode &node)
{
if (!isCached(node)) {
@ -31,12 +29,12 @@ CGAL_Nef_polyhedron CGALRenderer::renderCGALMesh(const AbstractNode &node)
render.execute();
assert(isCached(node));
}
return this->cache[mk_cache_id(node)];
return this->cache[this->dumpcache[node]];
}
bool CGALRenderer::isCached(const AbstractNode &node) const
{
return this->cache.contains(mk_cache_id(node));
return this->cache.contains(this->dumpcache[node]);
}
/*!
@ -111,8 +109,7 @@ void CGALRenderer::applyToChildren(const AbstractNode &node, CGALRenderer::CsgOp
chnode->progress_report();
}
}
QString cacheid = mk_cache_id(node);
this->cache.insert(cacheid, N);
this->cache.insert(this->dumpcache[node], N);
}
/*
@ -175,8 +172,7 @@ Response CGALRenderer::visit(const State &state, const TransformNode &node)
applyToChildren(node, UNION);
// Then apply transform
QString cacheid = mk_cache_id(node);
CGAL_Nef_polyhedron N = this->cache[cacheid];
CGAL_Nef_polyhedron N = this->cache[this->dumpcache[node]];
assert(N.dim >= 2 && N.dim <= 3);
if (N.dim == 2) {
// Unfortunately CGAL provides no transform method for CGAL_Nef_polyhedron2
@ -241,8 +237,7 @@ Response CGALRenderer::visit(const State &state, const AbstractPolyNode &node)
node.progress_report();
ps->unlink();
QString cacheid = mk_cache_id(node);
this->cache.insert(cacheid, N);
this->cache.insert(this->dumpcache[node], N);
}
catch (...) { // Don't leak the PolySet on ProgressCancelException
ps->unlink();
@ -261,35 +256,10 @@ Response CGALRenderer::visit(const State &state, const AbstractPolyNode &node)
void CGALRenderer::addToParent(const State &state, const AbstractNode &node)
{
assert(state.isPostfix());
QString cacheid = mk_cache_id(node);
this->visitedchildren.erase(node.index());
if (!state.parent()) {
this->root = &node;
if (state.parent()) {
this->visitedchildren[state.parent()->index()].push_back(std::make_pair(&node, this->dumpcache[node]));
}
else {
this->visitedchildren[state.parent()->index()].push_back(std::make_pair(&node, cacheid));
}
}
/*!
Create a cache id of the entire tree under this node. This cache id
is a non-whitespace plaintext of the evaluated scad tree and is used
for lookup in cgal_nef_cache.
*/
QString CGALRenderer::mk_cache_id(const AbstractNode &node) const
{
// FIXME: should we keep a cache of cache_id's to avoid recalculating this?
// -> check how often we recalculate it.
// FIXME: Get dump from dump cache
// FIXME: assert that cache contains node
QString cache_id = QString::fromStdString(this->dumpcache[node]);
// Remove all node indices and whitespace
cache_id.remove(QRegExp("[a-zA-Z_][a-zA-Z_0-9]*:"));
cache_id.remove(' ');
cache_id.remove('\t');
cache_id.remove('\n');
return cache_id;
}
#if 0

View File

@ -22,8 +22,8 @@ class CGALRenderer : public Visitor
{
public:
enum CsgOp {UNION, INTERSECTION, DIFFERENCE, MINKOWSKI};
// FIXME: If a cache is not give, we need to fix this ourselves
CGALRenderer(const NodeCache<string> &dumpcache) : root(NULL), dumpcache(dumpcache) {}
// FIXME: If a cache is not given, we need to fix this ourselves
CGALRenderer(QHash<QString, CGAL_Nef_polyhedron> &cache, const NodeCache<string> &dumpcache) : cache(cache), dumpcache(dumpcache) {}
virtual ~CGALRenderer() {}
virtual Response visit(const State &state, const AbstractNode &node);
@ -32,14 +32,9 @@ public:
virtual Response visit(const State &state, const TransformNode &node);
virtual Response visit(const State &state, const AbstractPolyNode &node);
QHash<QString, CGAL_Nef_polyhedron> &getCache() { return this->cache; }
CGAL_Nef_polyhedron renderCGALMesh(const AbstractNode &node);
CGAL_Nef_polyhedron renderCGALMesh(const PolySet &polyset);
// FIXME: Questionable design...
static CGALRenderer *renderer() { return global_renderer; }
static void setRenderer(CGALRenderer *r) { global_renderer = r; }
private:
void addToParent(const State &state, const AbstractNode &node);
bool isCached(const AbstractNode &node) const;
@ -48,15 +43,11 @@ private:
void applyToChildren(const AbstractNode &node, CGALRenderer::CsgOp op);
string currindent;
const AbstractNode *root;
typedef list<pair<const AbstractNode *, QString> > ChildList;
map<int, ChildList> visitedchildren;
// FIXME: enforce some maximum cache size (old version had 100K vertices as limit)
QHash<QString, CGAL_Nef_polyhedron> cache;
QHash<QString, CGAL_Nef_polyhedron> &cache;
const NodeCache<string> &dumpcache;
static CGALRenderer *global_renderer;
};
#endif

View File

@ -32,6 +32,7 @@ public:
ModuleInstantiation root_inst; // Top level instance
AbstractNode *absolute_root_node; // Result of tree evaluation
AbstractNode *root_node; // Root if the root modifier (!) is used
class NodeDumper *dumper;
class CSGTerm *root_raw_term; // Result of CSG term rendering
CSGTerm *root_norm_term; // Normalized CSG products
@ -76,6 +77,7 @@ private:
void compileCSG(bool procevents);
bool maybeSave();
bool checkModified();
QString dumpCSGTree(AbstractNode *root);
static void consoleOutput(const QString &msg, void *userdata) {
static_cast<MainWindow*>(userdata)->console->append(msg);
}

View File

@ -14,6 +14,10 @@
PolySet *PolySetCGALRenderer::renderPolySet(const ProjectionNode &node, AbstractPolyNode::render_mode_e)
{
// FIXME: create cacheid from node
QString cacheid;
if (this->cache.contains(cacheid)) return this->cache[cacheid]->ps->link();
CGAL_Nef_polyhedron N = this->cgalrenderer.renderCGALMesh(node);
PolySet *ps = new PolySet();
@ -155,6 +159,7 @@ PolySet *PolySetCGALRenderer::renderPolySet(const ProjectionNode &node, Abstract
cant_project_non_simple_polyhedron:
this->cache.insert(cacheid, new cache_entry(ps->link()));
return ps;
}
@ -232,6 +237,10 @@ static void add_slice(PolySet *ps, DxfData::Path *pt, double rot1, double rot2,
PolySet *PolySetCGALRenderer::renderPolySet(const DxfLinearExtrudeNode &node, AbstractPolyNode::render_mode_e)
{
// FIXME: create cacheid from node
QString cacheid;
if (this->cache.contains(cacheid)) return this->cache[cacheid]->ps->link();
DxfData *dxf;
if (node.filename.isEmpty())
@ -305,11 +314,16 @@ PolySet *PolySetCGALRenderer::renderPolySet(const DxfLinearExtrudeNode &node, Ab
delete dxf;
this->cache.insert(cacheid, new cache_entry(ps->link()));
return ps;
}
PolySet *PolySetCGALRenderer::renderPolySet(const DxfRotateExtrudeNode &node, AbstractPolyNode::render_mode_e)
{
// FIXME: create cacheid from node
QString cacheid;
if (this->cache.contains(cacheid)) return this->cache[cacheid]->ps->link();
DxfData *dxf;
if (node.filename.isEmpty())
@ -380,5 +394,6 @@ PolySet *PolySetCGALRenderer::renderPolySet(const DxfRotateExtrudeNode &node, Ab
delete dxf;
this->cache.insert(cacheid, new cache_entry(ps->link()));
return ps;
}

View File

@ -1,5 +1,15 @@
#include "PolySetRenderer.h"
#include "printutils.h"
#include "polyset.h"
PolySetRenderer *PolySetRenderer::global_renderer = NULL;
PolySetRenderer::cache_entry::cache_entry(PolySet *ps) :
ps(ps), msg(print_messages_stack.last())
{
}
PolySetRenderer::cache_entry::~cache_entry()
{
ps->unlink();
}

View File

@ -2,20 +2,37 @@
#define POLYSETRENDERER_H_
#include "node.h"
#include <QCache>
class PolySetRenderer
{
public:
enum RenderMode { RENDER_CGAL, RENDER_OPENCSG };
PolySetRenderer() {}
PolySetRenderer() : cache(100) {}
virtual ~PolySetRenderer() {}
virtual PolySet *renderPolySet(const class ProjectionNode &node, AbstractPolyNode::render_mode_e) = 0;
virtual PolySet *renderPolySet(const class DxfLinearExtrudeNode &node, AbstractPolyNode::render_mode_e) = 0;
virtual PolySet *renderPolySet(const class DxfRotateExtrudeNode &node, AbstractPolyNode::render_mode_e) = 0;
virtual PolySet *renderPolySet(const class ProjectionNode &, AbstractPolyNode::render_mode_e) = 0;
virtual PolySet *renderPolySet(const class DxfLinearExtrudeNode &, AbstractPolyNode::render_mode_e) = 0;
virtual PolySet *renderPolySet(const class DxfRotateExtrudeNode &, AbstractPolyNode::render_mode_e) = 0;
void clearCache() {
this->cache.clear();
}
static PolySetRenderer *renderer() { return global_renderer; }
static void setRenderer(PolySetRenderer *r) { global_renderer = r; }
protected:
struct cache_entry {
class PolySet *ps;
QString msg;
cache_entry(PolySet *ps);
~cache_entry();
};
QCache<QString, cache_entry> cache;
private:
static PolySetRenderer *global_renderer;
};

17
src/Tree.cc Normal file
View File

@ -0,0 +1,17 @@
#include "Tree.h"
#include "nodedumper.h"
#include <assert.h>
const std::string &Tree::getString(const AbstractNode &node) const
{
assert(this->root_node);
if (!this->nodecache.contains(node)) {
NodeDumper dumper(this->nodecache, false);
Traverser trav(dumper, *this->root_node, Traverser::PRE_AND_POSTFIX);
trav.execute();
assert(this->nodecache.contains(*this->root_node) &&
"NodeDumper failed to create a cache");
}
return this->nodecache[node];
}

29
src/Tree.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef TREE_H_
#define TREE_H_
#include "nodecache.h"
//#include "cgal.h"
using std::string;
class Tree
{
public:
Tree() {
this->root_node = NULL;
}
~Tree() {}
void setRoot(const AbstractNode *root) { this->root_node = root; }
const AbstractNode *root() const { return this->root_node; }
// FIXME: Really return a reference?
const string &getString(const AbstractNode &node) const;
// CGAL_Nef_polyhedron getCGALMesh(const AbstractNode &node) const;
private:
const AbstractNode *root_node;
mutable NodeCache nodecache;
};
#endif

View File

@ -160,7 +160,6 @@ CSGTerm *CgaladvNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlight
std::string CgaladvNode::toString() const
{
std::stringstream stream;
stream << "n" << this->index() << ": ";
switch (type) {
case MINKOWSKI:

View File

@ -80,7 +80,6 @@ CSGTerm *CsgNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights, Q
std::string CsgNode::toString() const
{
std::stringstream stream;
stream << "n" << this->index() << ": ";
switch (this->type) {
case CSG_TYPE_UNION:

View File

@ -136,16 +136,9 @@ PolySet *DxfLinearExtrudeNode::render_polyset(render_mode_e mode) const
return ps;
}
QString key = mk_cache_id();
if (PolySet::ps_cache.contains(key)) {
PRINT(PolySet::ps_cache[key]->msg);
return PolySet::ps_cache[key]->ps->link();
}
print_messages_push();
PolySet *ps = renderer->renderPolySet(*this, mode);
PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link()));
print_messages_pop();
@ -155,7 +148,6 @@ PolySet *DxfLinearExtrudeNode::render_polyset(render_mode_e mode) const
std::string DxfLinearExtrudeNode::toString() const
{
std::stringstream stream;
stream << "n" << this->index() << ": ";
QString text;
struct stat st;

View File

@ -112,18 +112,10 @@ PolySet *DxfRotateExtrudeNode::render_polyset(render_mode_e mode) const
return ps;
}
QString key = mk_cache_id();
if (PolySet::ps_cache.contains(key)) {
PRINT(PolySet::ps_cache[key]->msg);
return PolySet::ps_cache[key]->ps->link();
}
print_messages_push();
PolySet *ps = renderer->renderPolySet(*this, mode);
PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link()));
print_messages_pop();
return ps;
@ -132,7 +124,6 @@ PolySet *DxfRotateExtrudeNode::render_polyset(render_mode_e mode) const
std::string DxfRotateExtrudeNode::toString() const
{
std::stringstream stream;
stream << "n" << this->index() << ": ";
struct stat st;
memset(&st, 0, sizeof(struct stat));

View File

@ -192,7 +192,6 @@ PolySet *ImportNode::render_polyset(render_mode_e) const
std::string ImportNode::toString() const
{
std::stringstream stream;
stream << "n" << this->index() << ": ";
QString text;
struct stat st;

View File

@ -151,6 +151,7 @@ MainWindow::MainWindow(const QString &filename)
root_raw_term = NULL;
root_norm_term = NULL;
root_chain = NULL;
this->dumper = new NodeDumper;
#ifdef ENABLE_CGAL
this->root_N = NULL;
this->recreate_cgal_ogl_p = false;
@ -538,6 +539,13 @@ AbstractNode *MainWindow::find_root_tag(AbstractNode *n)
return NULL;
}
QString MainWindow::dumpCSGTree(AbstractNode *root)
{
Traverser trav(*this->dumper, *root, Traverser::PRE_AND_POSTFIX);
trav.execute();
return QString::fromStdString(this->dumper->getDump() + "\n");
}
/*!
Parse and evaluate the design -> this->root_node
*/
@ -653,11 +661,11 @@ void MainWindow::compile(bool procevents)
if (!(this->root_node = find_root_tag(absolute_root_node))) {
this->root_node = absolute_root_node;
}
// Dump the tree (to initialize caches). I guess we wouldn't really need to do
// this explicitly..
root_node->dump();
// Dump the tree (to initialize caches).
// FIXME: We shouldn't really need to do this explicitly..
dumpCSGTree(this->root_node);
if (1) {
if (1) {
PRINT("Compilation finished.");
if (procevents)
QApplication::processEvents();
@ -1131,7 +1139,11 @@ void MainWindow::actionRenderCGAL()
progress_report_prep(root_node, report_func, pd);
try {
this->root_N = new CGAL_Nef_polyhedron(CGALRenderer::renderer()->renderCGALMesh(*root_node));
// this->root_N = new CGAL_Nef_polyhedron(CGALRenderer::renderer()->renderCGALMesh(*root_node));
Tree *tree = this->tree;
CGALRenderDriver renderer(tree);
this->root_N = new CGAL_Nef_polyhedron(renderer->renderCGALMesh());
}
catch (ProgressCancelException e) {
PRINT("Rendering cancelled.");
@ -1234,7 +1246,7 @@ void MainWindow::actionDisplayCSGTree()
e->setWindowTitle("CSG Tree Dump");
e->setReadOnly(true);
if (root_node) {
e->setPlainText(root_node->dump());
e->setPlainText(QString::fromStdString(this->dumper->getDump()));
} else {
e->setPlainText("No CSG to dump. Please try compiling first...");
}
@ -1359,10 +1371,10 @@ void MainWindow::actionExportDXF()
void MainWindow::actionFlushCaches()
{
// FIXME: Polycache -> PolySetRenderer
PolySet::ps_cache.clear();
PolySetRenderer::renderer()->clearCache();
#ifdef ENABLE_CGAL
CGALRenderer::renderer()->getCache().clear();
NodeDumper::dumper()->clearCache();
this->dumper->clearCache();
#endif
dxf_dim_cache.clear();
dxf_cross_cache.clear();

View File

@ -64,26 +64,17 @@ Response AbstractPolyNode::accept(const class State &state, Visitor &visitor) co
return visitor.visit(state, *this);
}
// FIXME: Temporarily offer a top-level dump function to keep existing code running
QString AbstractNode::dump() const
{
NodeDumper dumper;
Traverser trav(dumper, *this, Traverser::PRE_AND_POSTFIX);
trav.execute();
return QString::fromStdString(dumper.getDump() + "\n");
}
std::string AbstractNode::toString() const
{
std::stringstream stream;
stream << "n" << this->index() << ": group()";
stream << "group()";
return stream.str();
}
std::string AbstractIntersectionNode::toString() const
{
std::stringstream stream;
stream << "n" << this->index() << ": intersection()";
stream << "intersection()";
return stream.str();
}
@ -161,13 +152,3 @@ std::ostream &operator<<(std::ostream &stream, const AbstractNode &node)
stream << node.toString();
return stream;
}
QString AbstractNode::mk_cache_id() const
{
QString cache_id = dump();
cache_id.remove(QRegExp("[a-zA-Z_][a-zA-Z_0-9]*:"));
cache_id.remove(' ');
cache_id.remove('\t');
cache_id.remove('\n');
return cache_id;
}

View File

@ -51,10 +51,6 @@ public:
int idx; // Node index (unique per tree)
// FIXME: Remove these three with dump() method
virtual QString mk_cache_id() const;
QString dump() const;
// FIXME: Rewrite to visitor
virtual class CSGTerm *render_csg_term(double m[20], QVector<CSGTerm*> *highlights, QVector<CSGTerm*> *background) const;

View File

@ -2,27 +2,36 @@
#define NODECACHE_H_
#include <vector>
#include <string>
#include "node.h"
template <class T>
/*!
Caches values per node based on the node.index().
The node index guaranteed to be unique per node tree since the index is reset
every time a new tree is generated.
*/
class NodeCache
{
public:
NodeCache() { }
virtual ~NodeCache() { }
const T & operator[](const AbstractNode &node) const {
bool contains(const AbstractNode &node) const {
return !(*this)[node].empty();
}
const std::string & operator[](const AbstractNode &node) const {
if (this->cache.size() > node.index()) return this->cache[node.index()];
else return nullvalue;
else return this->nullvalue;
}
void insert(const class AbstractNode &node, const T & value) {
void insert(const class AbstractNode &node, const std::string & value) {
if (this->cache.size() <= node.index()) this->cache.resize(node.index() + 1);
this->cache[node.index()] = value;
}
void remove(const class AbstractNode &node) {
if (this->cache.size() > node.index()) this->cache[node.index()] = nullvalue;
if (this->cache.size() > node.index()) this->cache[node.index()] = std::string();
}
void clear() {
@ -30,8 +39,8 @@ public:
}
private:
std::vector<T> cache;
T nullvalue;
std::vector<std::string> cache;
std::string nullvalue;
};
#endif

View File

@ -10,21 +10,31 @@
#include <iostream>
#include <assert.h>
// For compatibility with old dump() output
#define NODEDUMPER_COMPAT_MODE
// For compatibility with old dump() output.
// FIXME: Only needed for testing.
//#define NODEDUMPER_COMPAT_MODE
#ifdef NODEDUMPER_COMPAT_MODE
#include "dxflinextrudenode.h"
#include "dxfrotextrudenode.h"
#include "projectionnode.h"
#endif
NodeDumper *NodeDumper::global_dumper = NULL;
/*!
\class NodeDumper
bool NodeDumper::isCached(const AbstractNode &node)
A visitor responsible for creating a text dump of a node tree. Also
contains a cache for fast retrieval of the text representation of
any node or subtree.
*/
bool NodeDumper::isCached(const AbstractNode &node) const
{
return !this->cache[node].empty();
}
/*!
Indent or deindent. Must be called before we output any children.
*/
void NodeDumper::handleIndent(const State &state)
{
if (state.isPrefix()) {
@ -36,6 +46,11 @@ void NodeDumper::handleIndent(const State &state)
}
}
/*!
Dumps the block of children contained in this->visitedchildren,
including braces and indentation.
All children are assumed to be cached already.
*/
string NodeDumper::dumpChildren(const AbstractNode &node)
{
std::stringstream dump;
@ -45,7 +60,7 @@ string NodeDumper::dumpChildren(const AbstractNode &node)
for (ChildList::const_iterator iter = this->visitedchildren[node.index()].begin();
iter != this->visitedchildren[node.index()].end();
iter++) {
// FIXME: assert that cache contains **iter
assert(isCached(**iter));
dump << this->cache[**iter] << "\n";
}
@ -65,14 +80,20 @@ string NodeDumper::dumpChildren(const AbstractNode &node)
return dump.str();
}
/*!
Called for each node in the tree.
Will abort traversal if we're cached
*/
Response NodeDumper::visit(const State &state, const AbstractNode &node)
{
if (isCached(node)) return PruneTraversal;
else handleIndent(state);
handleIndent(state);
if (state.isPostfix()) {
std::stringstream dump;
dump << this->currindent << node;
dump << this->currindent;
if (this->idprefix) dump << "n" << node.index() << ":";
dump << node;
dump << dumpChildren(node);
this->cache.insert(node, dump.str());
}
@ -81,13 +102,6 @@ Response NodeDumper::visit(const State &state, const AbstractNode &node)
return ContinueTraversal;
}
const string &NodeDumper::getDump() const
{
assert(this->root);
// FIXME: assert that cache contains root
return this->cache[*this->root];
}
/*!
Adds this given node to its parent's child list.
Should be called for all nodes, including leaf nodes.

View File

@ -14,31 +14,27 @@ using std::list;
class NodeDumper : public Visitor
{
public:
NodeDumper() : root(NULL) {}
/*! If idPrefix is true, we will output "n<id>:" in front of each node,
which is useful for debugging. */
NodeDumper(NodeCache &cache, bool idPrefix = false) :
cache(cache), idprefix(idPrefix), root(NULL) { }
virtual ~NodeDumper() {}
virtual Response visit(const State &state, const AbstractNode &node);
const string &getDump() const;
const NodeCache<string> &getCache() const { return this->cache; }
void clearCache() { this->cache.clear(); }
// FIXME: Questionable design...
static NodeDumper *dumper() { return global_dumper; }
static void setDumper(NodeDumper *d) { global_dumper = d; }
private:
void handleVisitedChildren(const State &state, const AbstractNode &node);
bool isCached(const AbstractNode &node);
bool isCached(const AbstractNode &node) const;
void handleIndent(const State &state);
string dumpChildren(const AbstractNode &node);
NodeCache &cache;
bool idprefix;
string currindent;
const AbstractNode *root;
typedef list<const AbstractNode *> ChildList;
map<int, ChildList> visitedchildren;
NodeCache<string> cache;
static NodeDumper *global_dumper;
};
#endif

View File

@ -241,7 +241,6 @@ int main(int argc, char **argv)
NodeDumper dumper;
CGALRenderer cgalrenderer(dumper.getCache());
PolySetCGALRenderer psrenderer(cgalrenderer);
NodeDumper::setDumper(&dumper);
CGALRenderer::setRenderer(&cgalrenderer);
PolySetRenderer::setRenderer(&psrenderer);
@ -297,9 +296,11 @@ int main(int argc, char **argv)
// FIXME: It shouldn't be necessary to dump manually, only when
// the dumper and the renderer wants to share a cache
Traverser trav(*NodeDumper::dumper(), *root_node, Traverser::PRE_AND_POSTFIX);
trav.execute();
CGAL_Nef_polyhedron root_N = CGALRenderer::renderer()->renderCGALMesh(*root_node);
// FIXME: Rewrite to non-global dumper
// Traverser trav(*NodeDumper::dumper(), *root_node, Traverser::PRE_AND_POSTFIX);
// trav.execute();
// CGAL_Nef_polyhedron root_N = CGALRenderer::renderer()->renderCGALMesh(*root_node);
CGAL_Nef_polyhedron root_N;
QDir::setCurrent(original_path.absolutePath());

View File

@ -33,15 +33,6 @@
#include <Eigen/Core>
#include <Eigen/LU>
QCache<QString,PolySet::ps_cache_entry> PolySet::ps_cache(100);
PolySet::ps_cache_entry::ps_cache_entry(PolySet *ps) :
ps(ps), msg(print_messages_stack.last()) { }
PolySet::ps_cache_entry::~ps_cache_entry() {
ps->unlink();
}
PolySet::PolySet() : grid(GRID_FINE)
{
is2d = false;

View File

@ -62,15 +62,6 @@ public:
CSGMODE_HIGHLIGHT_DIFFERENCE = 22
};
struct ps_cache_entry {
PolySet *ps;
QString msg;
ps_cache_entry(PolySet *ps);
~ps_cache_entry();
};
static QCache<QString,ps_cache_entry> ps_cache;
void render_surface(colormode_e colormode, csgmode_e csgmode, double *m, GLint *shaderinfo = NULL) const;
void render_edges(colormode_e colormode, csgmode_e csgmode) const;

View File

@ -529,7 +529,6 @@ sphere_next_r2:
std::string PrimitiveNode::toString() const
{
std::stringstream stream;
stream << "n" << this->index() << ": ";
switch (this->type) {
case CUBE:

View File

@ -85,11 +85,6 @@ AbstractNode *ProjectionModule::evaluate(const Context *ctx, const ModuleInstant
return node;
}
void register_builtin_projection()
{
builtin_modules["projection"] = new ProjectionModule();
}
PolySet *ProjectionNode::render_polyset(render_mode_e mode) const
{
PolySetRenderer *renderer = PolySetRenderer::renderer();
@ -100,16 +95,9 @@ PolySet *ProjectionNode::render_polyset(render_mode_e mode) const
return ps;
}
QString key = mk_cache_id();
if (PolySet::ps_cache.contains(key)) {
PRINT(PolySet::ps_cache[key]->msg);
return PolySet::ps_cache[key]->ps->link();
}
print_messages_push();
PolySet *ps = renderer->renderPolySet(*this, mode);
PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link()));
print_messages_pop();
@ -119,10 +107,14 @@ PolySet *ProjectionNode::render_polyset(render_mode_e mode) const
std::string ProjectionNode::toString() const
{
std::stringstream stream;
stream << "n" << this->index() << ": ";
stream << "projection(cut = " << (this->cut_mode ? "true" : "false")
<< ", convexity = " << this->convexity << ")";
return stream.str();
}
void register_builtin_projection()
{
builtin_modules["projection"] = new ProjectionModule();
}

View File

@ -206,7 +206,6 @@ CSGTerm *RenderNode::render_csg_term(double m[20], QVector<CSGTerm*> *highlights
std::string RenderNode::toString() const
{
std::stringstream stream;
stream << "n" << this->index() << ": ";
stream << "render(convexity = " << convexity << ")";

View File

@ -206,7 +206,6 @@ PolySet *SurfaceNode::render_polyset(render_mode_e) const
std::string SurfaceNode::toString() const
{
std::stringstream stream;
stream << "n" << this->index() << ": ";
stream << "surface(file = \"" << this->filename
<< "\", center = " << (this->center ? "true" : "false") << ")";

View File

@ -266,7 +266,6 @@ CSGTerm *TransformNode::render_csg_term(double c[20], QVector<CSGTerm*> *highlig
std::string TransformNode::toString() const
{
std::stringstream stream;
stream << "n" << this->index() << ": ";
if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0 || m[19] >= 0) {
stream << "color([" << m[16] << ", " << m[17] << ", " << m[18] << ", " << m[19] << "])";

View File

@ -1,10 +1,10 @@
#include "CSGTextRenderer.h"
#include <string>
#include <map>
#include <list>
#include "visitor.h"
#include "state.h"
#include "nodecache.h"
#include "module.h" // FIXME: Temporarily for ModuleInstantiation
#include "csgnode.h"
@ -15,23 +15,9 @@
#include <assert.h>
#include <QRegExp>
string CSGTextRenderer::getCSGString() const
{
assert(this->root);
// FIXME: assert that cache contains root
return this->cache[mk_cache_id(*this->root)];
}
// CGAL_Nef_polyhedron CSGTextRenderer::getCGALMesh() const
// {
// assert(this->root);
// // FIXME: assert that cache contains root
// return this->cache[*this->root];
// }
bool CSGTextRenderer::isCached(const AbstractNode &node)
{
return this->cache.contains(mk_cache_id(node));
return this->cache.contains(this->tree.getString(node));
}
/*!
@ -118,7 +104,7 @@ void CSGTextRenderer::applyToChildren(const AbstractNode &node, CSGTextRenderer:
iter != this->visitedchildren[node.index()].end();
iter++) {
const AbstractNode *chnode = iter->first;
const QString &chcacheid = iter->second;
const string &chcacheid = iter->second;
// FIXME: Don't use deep access to modinst members
if (chnode->modinst->tag_background) continue;
if (first) {
@ -132,8 +118,7 @@ void CSGTextRenderer::applyToChildren(const AbstractNode &node, CSGTextRenderer:
}
N += ")";
}
QString cacheid = mk_cache_id(node);
this->cache.insert(cacheid, N);
this->cache.insert(this->tree.getString(node), N);
}
/*
@ -235,8 +220,7 @@ Response CSGTextRenderer::visit(const State &state, const AbstractPolyNode &node
// }
string N = typeid(node).name();
QString cacheid = mk_cache_id(node);
this->cache.insert(cacheid, N);
this->cache.insert(this->tree.getString(node), N);
// std::cout << "Insert: " << N << "\n";
// std::cout << "Node: " << cacheid.toStdString() << "\n\n";
@ -254,33 +238,28 @@ Response CSGTextRenderer::visit(const State &state, const AbstractPolyNode &node
void CSGTextRenderer::addToParent(const State &state, const AbstractNode &node)
{
assert(state.isPostfix());
QString cacheid = mk_cache_id(node);
this->visitedchildren.erase(node.index());
if (!state.parent()) {
this->root = &node;
}
else {
this->visitedchildren[state.parent()->index()].push_back(std::make_pair(&node, cacheid));
if (state.parent()) {
this->visitedchildren[state.parent()->index()].push_back(std::make_pair(&node, this->tree.getString(node)));
}
}
/*!
Create a cache id of the entire tree under this node. This cache id
is a non-whitespace plaintext of the evaluated scad tree and is used
for lookup in cgal_nef_cache.
*/
QString CSGTextRenderer::mk_cache_id(const AbstractNode &node) const
static uint hash(const uchar *p, int n)
{
// FIXME: should we keep a cache of cache_id's to avoid recalculating this?
// -> check how often we recalculate it.
uint h = 0;
uint g;
// FIXME: Get dump from dump cache
// FIXME: assert that cache contains node
QString cache_id = QString::fromStdString(this->dumpcache[node]);
// Remove all node indices and whitespace
cache_id.remove(QRegExp("[a-zA-Z_][a-zA-Z_0-9]*:"));
cache_id.remove(' ');
cache_id.remove('\t');
cache_id.remove('\n');
return cache_id;
while (n--) {
h = (h << 4) + *p++;
if ((g = (h & 0xf0000000)) != 0)
h ^= g >> 23;
h &= ~g;
}
return h;
}
uint qHash(const string &str) {
return hash(reinterpret_cast<const uchar *>(str.c_str()), str.length());
}

View File

@ -1,11 +1,15 @@
#ifndef CSGTEXTRENDERER_H_
#define CSGTEXTRENDERER_H_
#include <qglobal.h>
#include <string>
extern uint qHash(const std::string &);
#include <map>
#include <list>
#include <QHash>
#include "visitor.h"
#include "nodecache.h"
#include "Tree.h"
using std::string;
using std::map;
@ -15,8 +19,8 @@ using std::pair;
class CSGTextRenderer : public Visitor
{
public:
enum CsgOp {UNION, INTERSECTION, DIFFERENCE, MINKOWSKI};
CSGTextRenderer(const NodeCache<string> &dumpcache) : root(NULL), dumpcache(dumpcache) {}
CSGTextRenderer(QHash<string, string> &cache, const Tree &tree) :
cache(cache), tree(tree) {}
virtual ~CSGTextRenderer() {}
virtual Response visit(const State &state, const AbstractNode &node);
@ -25,21 +29,19 @@ public:
virtual Response visit(const State &state, const TransformNode &node);
virtual Response visit(const State &state, const AbstractPolyNode &node);
string getCSGString() const;
private:
enum CsgOp {UNION, INTERSECTION, DIFFERENCE, MINKOWSKI};
void addToParent(const State &state, const AbstractNode &node);
bool isCached(const AbstractNode &node);
QString mk_cache_id(const AbstractNode &node) const;
void process(string &target, const string &src, CSGTextRenderer::CsgOp op);
void applyToChildren(const AbstractNode &node, CSGTextRenderer::CsgOp op);
string currindent;
const AbstractNode *root;
typedef list<pair<const AbstractNode *, QString> > ChildList;
typedef list<pair<const AbstractNode *, string> > ChildList;
map<int, ChildList> visitedchildren;
QHash<QString, string> cache;
const NodeCache<string> &dumpcache;
QHash<string, string> &cache;
const Tree &tree;
};
#endif

View File

@ -33,6 +33,7 @@
#include "nodedumper.h"
#include "CGALRenderer.h"
#include "PolySetCGALRenderer.h"
#include "Tree.h"
#include <QApplication>
#include <QFile>
@ -61,6 +62,24 @@ void handle_dep(QString filename)
}
}
// FIXME: enforce some maximum cache size (old version had 100K vertices as limit)
QHash<string, CGAL_Nef_polyhedron> cache;
void cgalTree(Tree &tree)
{
const AbstractNode *root = tree.root();
assert(root);
NodeCache<string> &cache = tree.cache();
NodeDumper dumper(cache, false);
Traverser trav(dumper, *root, Traverser::PRE_AND_POSTFIX);
trav.execute();
assert(!cache[*root].empty());
CSGTextRenderer renderer(csgcache, cache);
Traverser render(renderer, *root, Traverser::PRE_AND_POSTFIX);
render.execute();
}
int main(int argc, char **argv)
{
if (argc != 2) {
@ -146,11 +165,12 @@ int main(int argc, char **argv)
AbstractNode::resetIndexCounter();
root_node = root_module->evaluate(&root_ctx, &root_inst);
NodeDumper dumper;
Traverser trav(dumper, *root_node, Traverser::PRE_AND_POSTFIX);
trav.execute();
std::string dumpstdstr = dumper.getDump() + "\n";
std::cout << dumpstdstr << "\n";
Tree tree;
tree.setRoot(root_node);
cgalTree(tree);
std::cout << cache[tree.cache()[*root_node]] << "\n";
CGALRenderer cgalrenderer(dumper.getCache());
PolySetCGALRenderer psrenderer(cgalrenderer);

View File

@ -1,10 +1,10 @@
DEFINES += OPENSCAD_VERSION=test
TEMPLATE = app
OBJECTS_DIR = objects
MOC_DIR = objects
UI_DIR = objects
RCC_DIR = objects
OBJECTS_DIR = cgal-objects
MOC_DIR = cgal-objects
UI_DIR = cgal-objects
RCC_DIR = cgal-objects
INCLUDEPATH += ../src
macx {
@ -61,7 +61,8 @@ HEADERS += ../src/builtin.h \
../src/importnode.h \
../src/state.h \
../src/PolySetRenderer.h \
../src/PolySetCGALRenderer.h
../src/PolySetCGALRenderer.h \
../src/Tree.h
SOURCES += cgaltest.cc \
../src/export.cc \
@ -98,4 +99,5 @@ SOURCES += cgaltest.cc \
../src/CGALRenderer.cc \
../src/traverser.cc \
../src/PolySetRenderer.cc \
../src/PolySetCGALRenderer.cc
../src/PolySetCGALRenderer.cc \
../src/Tree.cc

View File

@ -23,6 +23,7 @@
*
*/
#include "CSGTextRenderer.h"
#include "openscad.h"
#include "node.h"
#include "module.h"
@ -30,14 +31,14 @@
#include "value.h"
#include "export.h"
#include "builtin.h"
#include "nodedumper.h"
#include "CSGTextRenderer.h"
#include "Tree.h"
#include <QApplication>
#include <QFile>
#include <QDir>
#include <QSet>
#include <getopt.h>
#include <assert.h>
#include <iostream>
QString commandline_commands;
@ -60,6 +61,17 @@ void handle_dep(QString filename)
}
}
QHash<string, string> csgcache;
void csgTree(Tree &tree)
{
assert(tree.root());
CSGTextRenderer renderer(csgcache, tree);
Traverser render(renderer, *tree.root(), Traverser::PRE_AND_POSTFIX);
render.execute();
}
int main(int argc, char **argv)
{
if (argc != 2) {
@ -145,17 +157,13 @@ int main(int argc, char **argv)
AbstractNode::resetIndexCounter();
root_node = root_module->evaluate(&root_ctx, &root_inst);
Tree tree;
tree.setRoot(root_node);
NodeDumper dumper;
Traverser trav(dumper, *root_node, Traverser::PRE_AND_POSTFIX);
trav.execute();
std::string dumpstdstr = dumper.getDump() + "\n";
std::cout << dumpstdstr << "\n";
csgTree(tree);
std::cout << tree.getString(*root_node) << "\n";
CSGTextRenderer renderer(dumper.getCache());
Traverser render(renderer, *root_node, Traverser::PRE_AND_POSTFIX);
render.execute();
std::cout << renderer.getCSGString() << "\n";
std::cout << csgcache[tree.getString(*root_node)] << "\n";
destroy_builtin_functions();
destroy_builtin_modules();

View File

@ -61,11 +61,12 @@ HEADERS += ../src/builtin.h \
../src/csgnode.h \
../src/visitor.h \
../src/nodedumper.h \
../src/CSGTextRenderer.h \
../src/nodecache.h \
../src/importnode.h \
../src/state.h \
../src/PolySetRenderer.h
../src/PolySetRenderer.h \
../src/Tree.h \
CSGTextRenderer.h
SOURCES += csgtexttest.cc \
../src/export.cc \
@ -96,6 +97,7 @@ SOURCES += csgtexttest.cc \
../src/printutils.cc \
../src/progress.cc \
../src/nodedumper.cc \
../src/CSGTextRenderer.cc \
../src/traverser.cc \
../src/PolySetRenderer.cc
../src/PolySetRenderer.cc \
../src/Tree.cc \
CSGTextRenderer.cc

View File

@ -31,12 +31,17 @@
#include "export.h"
#include "builtin.h"
#include "nodedumper.h"
#include "Tree.h"
#include <QApplication>
#include <QFile>
#include <QDir>
#include <QSet>
#include <getopt.h>
#include <assert.h>
#include <iostream>
using std::string;
QString commandline_commands;
const char *make_command = NULL;
@ -144,23 +149,15 @@ int main(int argc, char **argv)
root_node = root_module->evaluate(&root_ctx, &root_inst);
// Cache test
QString dumpstr = root_node->dump();
QString dumpstr_cached = root_node->dump();
if (dumpstr != dumpstr_cached) rc = 1;
QString teststr("test");
Tree tree;
tree.setRoot(root_node);
NodeDumper dumper;
Traverser trav(dumper, *root_node, Traverser::PRE_AND_POSTFIX);
trav.execute();
std::string dumpstdstr = dumper.getDump() + "\n";
trav.execute();
std::string dumpstdstr_cached = dumper.getDump() + "\n";
string dumpstdstr = tree.getString(*root_node);
string dumpstdstr_cached = tree.getString(*root_node);
if (dumpstdstr != dumpstdstr_cached) rc = 1;
if (QString::fromStdString(dumpstdstr) != dumpstr) {
printf(dumpstr.toUtf8());
printf(dumpstdstr.c_str());
rc = 1;
}
std::cout << dumpstdstr << "\n";
destroy_builtin_functions();
destroy_builtin_modules();

View File

@ -67,7 +67,8 @@ HEADERS += ../src/builtin.h \
../src/nodecache.h \
../src/importnode.h \
../src/state.h \
../src/PolySetRenderer.h
../src/PolySetRenderer.h \
../src/Tree.h
SOURCES += dumptest.cc \
../src/export.cc \
@ -99,4 +100,5 @@ SOURCES += dumptest.cc \
../src/progress.cc \
../src/nodedumper.cc \
../src/traverser.cc \
../src/PolySetRenderer.cc
../src/PolySetRenderer.cc \
../src/Tree.cc