Refactoring: Moved color functionality into separate module and node

stl_dim
Marius Kintel 2011-09-02 23:35:10 +02:00
parent 287c20d2ff
commit ed8a99ed55
15 changed files with 192 additions and 247 deletions

View File

@ -144,6 +144,7 @@ HEADERS += src/renderer.h \
src/projectionnode.h \
src/importnode.h \
src/transformnode.h \
src/colornode.h \
src/rendernode.h \
src/openscad.h \
src/polyset.h \
@ -178,6 +179,7 @@ SOURCES += src/openscad.cc \
src/polyset.cc \
src/csgops.cc \
src/transform.cc \
src/color.cc \
src/primitives.cc \
src/projection.cc \
src/cgaladv.cc \

View File

@ -5,6 +5,7 @@
#include "module.h"
#include "csgnode.h"
#include "transformnode.h"
#include "colornode.h"
#include "rendernode.h"
#include "printutils.h"
@ -82,14 +83,14 @@ Response CSGTermEvaluator::visit(State &state, const AbstractIntersectionNode &n
return ContinueTraversal;
}
static CSGTerm *evaluate_csg_term_from_ps(const double m[20],
static CSGTerm *evaluate_csg_term_from_ps(const State &state,
vector<CSGTerm*> &highlights,
vector<CSGTerm*> &background,
PolySet *ps,
const ModuleInstantiation *modinst,
const AbstractPolyNode &node)
{
CSGTerm *t = new CSGTerm(ps, m, QString("%1%2").arg(node.name().c_str()).arg(node.index()));
CSGTerm *t = new CSGTerm(ps, state.matrix(), state.color(), QString("%1%2").arg(node.name().c_str()).arg(node.index()));
if (modinst->tag_highlight)
highlights.push_back(t->link());
if (modinst->tag_background) {
@ -105,7 +106,7 @@ Response CSGTermEvaluator::visit(State &state, const AbstractPolyNode &node)
CSGTerm *t1 = NULL;
PolySet *ps = node.evaluate_polyset(AbstractPolyNode::RENDER_OPENCSG, this->psevaluator);
if (ps) {
t1 = evaluate_csg_term_from_ps(state.matrix(), this->highlights, this->background,
t1 = evaluate_csg_term_from_ps(state, this->highlights, this->background,
ps, node.modinst, node);
}
this->stored_term[node.index()] = t1;
@ -138,7 +139,7 @@ Response CSGTermEvaluator::visit(State &state, const CsgNode &node)
Response CSGTermEvaluator::visit(State &state, const TransformNode &node)
{
if (state.isPrefix()) {
double m[20];
double m[16];
for (int i = 0; i < 16; i++)
{
@ -149,11 +150,6 @@ Response CSGTermEvaluator::visit(State &state, const TransformNode &node)
m[i] += state.matrix()[c_row + j*4] * node.matrix[m_col*4 + j];
}
}
for (int i = 16; i < 20; i++) {
m[i] = node.matrix[i] < 0 ? state.matrix()[i] : node.matrix[i];
}
state.setMatrix(m);
}
if (state.isPostfix()) {
@ -163,6 +159,18 @@ Response CSGTermEvaluator::visit(State &state, const TransformNode &node)
return ContinueTraversal;
}
Response CSGTermEvaluator::visit(State &state, const ColorNode &node)
{
if (state.isPrefix()) {
state.setColor(node.color);
}
if (state.isPostfix()) {
applyToChildren(node, CSGT_UNION);
addToParent(state, node);
}
return ContinueTraversal;
}
// FIXME: If we've got CGAL support, render this node as a CGAL union into a PolySet
Response CSGTermEvaluator::visit(State &state, const RenderNode &node)
{
@ -186,168 +194,3 @@ void CSGTermEvaluator::addToParent(const State &state, const AbstractNode &node)
this->visitedchildren[state.parent()->index()].push_back(&node);
}
}
#if 0
// FIXME: #ifdef ENABLE_CGAL
#if 0
CSGTerm *CgaladvNode::evaluate_csg_term(double m[20], QVector<CSGTerm*> &highlights, QVector<CSGTerm*> &background) const
{
if (type == MINKOWSKI)
return evaluate_csg_term_from_nef(m, highlights, background, "minkowski", this->convexity);
if (type == GLIDE)
return evaluate_csg_term_from_nef(m, highlights, background, "glide", this->convexity);
if (type == SUBDIV)
return evaluate_csg_term_from_nef(m, highlights, background, "subdiv", this->convexity);
if (type == HULL)
return evaluate_csg_term_from_nef(m, highlights, background, "hull", this->convexity);
return NULL;
}
#else // ENABLE_CGAL
CSGTerm *CgaladvNode::evaluate_csg_term(double m[20], QVector<CSGTerm*> &highlights, QVector<CSGTerm*> &background) const
{
PRINT("WARNING: Found minkowski(), glide(), subdiv() or hull() statement but compiled without CGAL support!");
return NULL;
}
#endif // ENABLE_CGAL
// FIXME: #ifdef ENABLE_CGAL
#if 0
CSGTerm *AbstractNode::evaluate_csg_term_from_nef(double m[20], QVector<CSGTerm*> &highlights, QVector<CSGTerm*> &background, const char *statement, int convexity) const
{
QString key = mk_cache_id();
if (PolySet::ps_cache.contains(key)) {
PRINT(PolySet::ps_cache[key]->msg);
return AbstractPolyNode::evaluate_csg_term_from_ps(m, highlights, background,
PolySet::ps_cache[key]->ps->link(), modinst, idx);
}
print_messages_push();
CGAL_Nef_polyhedron N;
QString cache_id = mk_cache_id();
if (cgal_nef_cache.contains(cache_id))
{
PRINT(cgal_nef_cache[cache_id]->msg);
N = cgal_nef_cache[cache_id]->N;
}
else
{
PRINTF_NOCACHE("Processing uncached %s statement...", statement);
// PRINTA("Cache ID: %1", cache_id);
QApplication::processEvents();
QTime t;
t.start();
N = this->evaluateCSGMesh();
int s = t.elapsed() / 1000;
PRINTF_NOCACHE("..processing time: %d hours, %d minutes, %d seconds", s / (60*60), (s / 60) % 60, s % 60);
}
PolySet *ps = NULL;
if (N.dim == 2)
{
DxfData dd(N);
ps = new PolySet();
ps->is2d = true;
dxf_tesselate(ps, dd, 0, true, false, 0);
dxf_border_to_ps(ps, &dd);
}
if (N.dim == 3)
{
if (!N.p3.is_simple()) {
PRINTF("WARNING: Result of %s() isn't valid 2-manifold! Modify your design..", statement);
return NULL;
}
ps = new PolySet();
CGAL_Polyhedron P;
N.p3.convert_to_Polyhedron(P);
typedef CGAL_Polyhedron::Vertex Vertex;
typedef CGAL_Polyhedron::Vertex_const_iterator VCI;
typedef CGAL_Polyhedron::Facet_const_iterator FCI;
typedef CGAL_Polyhedron::Halfedge_around_facet_const_circulator HFCC;
for (FCI fi = P.facets_begin(); fi != P.facets_end(); ++fi) {
HFCC hc = fi->facet_begin();
HFCC hc_end = hc;
ps->append_poly();
do {
Vertex v = *VCI((hc++)->vertex());
double x = CGAL::to_double(v.point().x());
double y = CGAL::to_double(v.point().y());
double z = CGAL::to_double(v.point().z());
ps->append_vertex(x, y, z);
} while (hc != hc_end);
}
}
if (ps)
{
ps->convexity = convexity;
PolySet::ps_cache.insert(key, new PolySet::ps_cache_entry(ps->link()));
CSGTerm *term = new CSGTerm(ps, m, QString("n%1").arg(idx));
if (modinst->tag_highlight)
highlights.push_back(term->link());
if (modinst->tag_background) {
background.push_back(term);
return NULL;
}
return term;
}
print_messages_pop();
return NULL;
}
CSGTerm *RenderNode::evaluate_csg_term(double m[20], QVector<CSGTerm*> &highlights, QVector<CSGTerm*> &background) const
{
return evaluate_csg_term_from_nef(m, highlights, background, "render", this->convexity);
}
#else
CSGTerm *RenderNode::evaluate_csg_term(double m[20], QVector<CSGTerm*> &highlights, QVector<CSGTerm*> &background) const
{
CSGTerm *t1 = NULL;
PRINT("WARNING: Found render() statement but compiled without CGAL support!");
foreach(AbstractNode * v, children) {
CSGTerm *t2 = v->evaluate_csg_term(m, highlights, background);
if (t2 && !t1) {
t1 = t2;
} else if (t2 && t1) {
t1 = new CSGTerm(CSGTerm::TYPE_UNION, t1, t2);
}
}
if (modinst->tag_highlight)
highlights.push_back(t1->link());
if (t1 && modinst->tag_background) {
background.push_back(t1);
return NULL;
}
return t1;
}
#endif
#endif

View File

@ -27,6 +27,7 @@ public:
virtual Response visit(State &state, const AbstractPolyNode &node);
virtual Response visit(State &state, const CsgNode &node);
virtual Response visit(State &state, const TransformNode &node);
virtual Response visit(State &state, const ColorNode &node);
virtual Response visit(State &state, const RenderNode &node);
class CSGTerm *evaluateCSGTerm(const AbstractNode &node,

View File

@ -85,6 +85,7 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo,
if (shaderinfo) glUseProgram(shaderinfo[0]);
for (; j < i; j++) {
double *m = chain->matrices[j];
double *c = chain->colors[j];
glPushMatrix();
glMultMatrixd(m);
int csgmode = chain->types[j] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL;
@ -92,12 +93,12 @@ void OpenCSGRenderer::renderCSGChain(CSGChain *chain, GLint *shaderinfo,
chain->polysets[j]->render_surface(PolySet::COLORMODE_HIGHLIGHT, PolySet::csgmode_e(csgmode + 20), m, shaderinfo);
} else if (background) {
chain->polysets[j]->render_surface(PolySet::COLORMODE_BACKGROUND, PolySet::csgmode_e(csgmode + 10), m, shaderinfo);
} else if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0 || m[19] >= 0) {
} else if (c[0] >= 0 || c[1] >= 0 || c[2] >= 0 || c[3] >= 0) {
// User-defined color from source
glColor4d(m[16], m[17], m[18], m[19]);
glColor4dv(c);
if (shaderinfo) {
glUniform4f(shaderinfo[1], m[16], m[17], m[18], m[19]);
glUniform4f(shaderinfo[2], (m[16]+1)/2, (m[17]+1)/2, (m[18]+1)/2, 1.0);
glUniform4f(shaderinfo[1], c[0], c[1], c[2], c[3]);
glUniform4f(shaderinfo[2], (c[0]+1)/2, (c[1]+1)/2, (c[2]+1)/2, 1.0);
}
chain->polysets[j]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m, shaderinfo);
} else if (chain->types[j] == CSGTerm::TYPE_DIFFERENCE) {

View File

@ -66,6 +66,7 @@ void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight,
if (polySetVisitMark[QPair<PolySet*,double*>(chain->polysets[i], chain->matrices[i])]++ > 0)
continue;
double *m = chain->matrices[i];
double *c = chain->colors[i];
glPushMatrix();
glMultMatrixd(m);
int csgmode = chain->types[i] == CSGTerm::TYPE_DIFFERENCE ? PolySet::CSGMODE_DIFFERENCE : PolySet::CSGMODE_NORMAL;
@ -91,12 +92,12 @@ void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight,
} else {
chain->polysets[i]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m);
}
} else if (m[16] >= 0 || m[17] >= 0 || m[18] >= 0 || m[19] >= 0) {
glColor4d(m[16], m[17], m[18], m[19]);
} else if (c[0] >= 0 || c[1] >= 0 || c[2] >= 0 || c[3] >= 0) {
glColor4dv(c);
chain->polysets[i]->render_surface(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode), m);
if (showedges) {
glDisable(GL_LIGHTING);
glColor4d((m[16]+1)/2, (m[17]+1)/2, (m[18]+1)/2, 1.0);
glColor4d((c[0]+1)/2, (c[1]+1)/2, (c[2]+1)/2, 1.0);
chain->polysets[i]->render_edges(PolySet::COLORMODE_NONE, PolySet::csgmode_e(csgmode));
glEnable(GL_LIGHTING);
}

View File

@ -13,6 +13,7 @@ extern void destroy_builtin_modules();
extern void register_builtin_csgops();
extern void register_builtin_transform();
extern void register_builtin_color();
extern void register_builtin_primitives();
extern void register_builtin_surface();
extern void register_builtin_control();

108
src/color.cc Normal file
View File

@ -0,0 +1,108 @@
/*
* OpenSCAD (www.openscad.org)
* Copyright (C) 2009-2011 Clifford Wolf <clifford@clifford.at> and
* Marius Kintel <marius@kintel.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* As a special exception, you have permission to link this program
* with the CGAL library and distribute executables, as long as you
* follow the requirements of the GNU GPL in regard to all of the
* software in the executable aside from CGAL.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "colornode.h"
#include "module.h"
#include "context.h"
#include "builtin.h"
#include "printutils.h"
#include "visitor.h"
#include <sstream>
#include <assert.h>
#include <QColor>
class ColorModule : public AbstractModule
{
public:
ColorModule() { }
virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
};
using std::string;
AbstractNode *ColorModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
{
ColorNode *node = new ColorNode(inst);
for (int i = 0; i < 4; i++) node->color[i] = -1;
QVector<QString> argnames;
QVector<Expression*> argexpr;
argnames = QVector<QString>() << "c" << "alpha";
Context c(ctx);
c.args(argnames, argexpr, inst->argnames, inst->argvalues);
Value v = c.lookup_variable("c");
if (v.type == Value::VECTOR) {
for (int i = 0; i < 4; i++)
node->color[i] = i < v.vec.size() ? v.vec[i]->num : 1.0;
} else if (v.type == Value::STRING) {
QString colorname = QString::fromStdString(v.text);
QColor color;
color.setNamedColor(colorname);
if (color.isValid()) {
node->color[0] = color.redF();
node->color[1] = color.greenF();
node->color[2] = color.blueF();
} else {
PRINTA_NOCACHE("WARNING: Color name \"%1\" unknown. Please see", colorname);
PRINTF_NOCACHE("WARNING: http://en.wikipedia.org/wiki/Web_colors");
}
}
Value alpha = c.lookup_variable("alpha");
if (alpha.type == Value::NUMBER) {
node->color[3] = alpha.num;
}
foreach (ModuleInstantiation *v, inst->children) {
AbstractNode *n = v->evaluate(inst->ctx);
if (n != NULL)
node->children.push_back(n);
}
return node;
}
string ColorNode::toString() const
{
std::stringstream stream;
stream << "color([" << this->color[0] << ", " << this->color[1] << ", " << this->color[2] << ", " << this->color[3] << "])";
return stream.str();
}
string ColorNode::name() const
{
return "color";
}
void register_builtin_color()
{
builtin_modules["color"] = new ColorModule();
}

20
src/colornode.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef COLORNODE_H_
#define COLORNODE_H_
#include "node.h"
#include "visitor.h"
class ColorNode : public AbstractNode
{
public:
ColorNode(const ModuleInstantiation *mi) : AbstractNode(mi) { }
virtual Response accept(class State &state, Visitor &visitor) const {
return visitor.visit(state, *this);
}
virtual std::string toString() const;
virtual std::string name() const;
double color[4];
};
#endif

View File

@ -46,15 +46,15 @@
*/
CSGTerm::CSGTerm(PolySet *polyset, const double m[20], QString label)
CSGTerm::CSGTerm(PolySet *polyset, const double matrix[16], const double color[4], QString label)
{
this->type = TYPE_PRIMITIVE;
this->polyset = polyset;
this->label = label;
this->left = NULL;
this->right = NULL;
for (int i = 0; i < 20; i++)
this->m[i] = m[i];
for (int i = 0; i < 16; i++) this->m[i] = matrix[i];
for (int i = 0; i < 4; i++) this->color[i] = color[i];
refcounter = 1;
}
@ -191,10 +191,11 @@ CSGChain::CSGChain()
{
}
void CSGChain::add(PolySet *polyset, double *m, CSGTerm::type_e type, QString label)
void CSGChain::add(PolySet *polyset, double *m, double *color, CSGTerm::type_e type, QString label)
{
polysets.append(polyset);
matrices.append(m);
colors.append(color);
types.append(type);
labels.append(label);
}
@ -202,7 +203,7 @@ void CSGChain::add(PolySet *polyset, double *m, CSGTerm::type_e type, QString la
void CSGChain::import(CSGTerm *term, CSGTerm::type_e type)
{
if (term->type == CSGTerm::TYPE_PRIMITIVE) {
add(term->polyset, term->m, type, term->label);
add(term->polyset, term->m, term->color, type, term->label);
} else {
import(term->left, type);
import(term->right, term->type);

View File

@ -20,10 +20,11 @@ public:
QString label;
CSGTerm *left;
CSGTerm *right;
double m[20];
double m[16];
double color[4];
int refcounter;
CSGTerm(PolySet *polyset, const double m[20], QString label);
CSGTerm(PolySet *polyset, const double matrix[16], const double color[4], QString label);
CSGTerm(type_e type, CSGTerm *left, CSGTerm *right);
CSGTerm *normalize();
@ -39,12 +40,13 @@ class CSGChain
public:
QVector<PolySet*> polysets;
QVector<double*> matrices;
QVector<double*> colors;
QVector<CSGTerm::type_e> types;
QVector<QString> labels;
CSGChain();
void add(PolySet *polyset, double *m, CSGTerm::type_e type, QString label);
void add(PolySet *polyset, double *m, double *color, CSGTerm::type_e type, QString label);
void import(CSGTerm *term, CSGTerm::type_e type = CSGTerm::TYPE_UNION);
QString dump();

View File

@ -206,6 +206,7 @@ void initialize_builtin_modules()
register_builtin_csgops();
register_builtin_transform();
register_builtin_color();
register_builtin_primitives();
register_builtin_surface();
register_builtin_control();

View File

@ -9,7 +9,7 @@ public:
State(const class AbstractNode *parent)
: parentnode(parent), isprefix(false), ispostfix(false), numchildren(0) {
for (int i=0;i<16;i++) this->m[i] = i % 5 == 0 ? 1.0 : 0.0;
for (int i=16;i<20;i++) this->m[i] = -1.0;
for (int i=0;i<4;i++) this->c[i] = -1.0;
}
virtual ~State() {}
@ -17,13 +17,15 @@ public:
void setPostfix(bool on) { this->ispostfix = on; }
void setNumChildren(unsigned int numc) { this->numchildren = numc; }
void setParent(const AbstractNode *parent) { this->parentnode = parent; }
void setMatrix(const double m[20]) { memcpy(this->m, m, 20*sizeof(m)); }
void setMatrix(const double m[16]) { memcpy(this->m, m, 16*sizeof(double)); }
void setColor(const double c[4]) { memcpy(this->c, c, 4*sizeof(double)); }
bool isPrefix() const { return this->isprefix; }
bool isPostfix() const { return this->ispostfix; }
unsigned int numChildren() const { return this->numchildren; }
const AbstractNode *parent() const { return this->parentnode; }
const double *matrix() const { return this->m; }
const double *color() const { return this->c; }
private:
const AbstractNode * parentnode;
@ -31,8 +33,9 @@ private:
bool ispostfix;
unsigned int numchildren;
// Transformation matrix incl. color. FIXME: Generalize such state variables?
double m[20];
// Transformation matrix and color. FIXME: Generalize such state variables?
double m[16];
double c[4];
};
#endif

View File

@ -43,8 +43,7 @@ enum transform_type_e {
ROTATE,
MIRROR,
TRANSLATE,
MULTMATRIX,
COLOR
MULTMATRIX
};
class TransformModule : public AbstractModule
@ -86,8 +85,6 @@ AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstanti
for (int i = 0; i < 16; i++)
node->matrix[i] = i % 5 == 0 ? 1.0 : 0.0;
for (int i = 16; i < 20; i++)
node->matrix[i] = -1;
QVector<QString> argnames;
QVector<Expression*> argexpr;
@ -108,9 +105,6 @@ AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstanti
case MULTMATRIX:
argnames = QVector<QString>() << "m";
break;
case COLOR:
argnames = QVector<QString>() << "c" << "alpha";
break;
default:
assert(false);
}
@ -242,36 +236,6 @@ AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstanti
}
}
}
else if (this->type == COLOR)
{
Value v = c.lookup_variable("c");
if (v.type == Value::VECTOR) {
for (int i = 0; i < 4; i++)
node->matrix[16+i] = i < v.vec.size() ? v.vec[i]->num : 1.0;
// FIXME: Port to non-Qt
#if 0
} else if (v.type == Value::STRING) {
QString colorname = v.text;
QColor color;
color.setNamedColor(colorname);
if (color.isValid()) {
node->matrix[16+0] = color.redF();
node->matrix[16+1] = color.greenF();
node->matrix[16+2] = color.blueF();
} else {
PRINTF_NOCACHE("WARNING: Color name \"%s\" unknown. Please see",v.text.toUtf8().data());
PRINTF_NOCACHE("WARNING: http://en.wikipedia.org/wiki/Web_colors");
}
#endif
}
// FIXME: Only lookup alpha if color was set
Value alpha = c.lookup_variable("alpha");
if (alpha.type == Value::NUMBER) {
node->matrix[16+3] = alpha.num;
} else {
node->matrix[16+3] = 1.0;
}
}
foreach (ModuleInstantiation *v, inst->children) {
AbstractNode *n = v->evaluate(inst->ctx);
@ -286,23 +250,18 @@ string TransformNode::toString() const
{
std::stringstream stream;
if (this->matrix[16] >= 0 || this->matrix[17] >= 0 || this->matrix[18] >= 0 || this->matrix[19] >= 0) {
stream << "color([" << this->matrix[16] << ", " << this->matrix[17] << ", " << this->matrix[18] << ", " << this->matrix[19] << "])";
}
else {
stream << "multmatrix([";
for (int j=0;j<4;j++) {
stream << "[";
for (int i=0;i<4;i++) {
// FIXME: The 0 test is to avoid a leading minus before a single 0 (cosmetics)
stream << ((this->matrix[i*4+j]==0)?0:this->matrix[i*4+j]);
if (i != 3) stream << ", ";
}
stream << "]";
if (j != 3) stream << ", ";
stream << "multmatrix([";
for (int j=0;j<4;j++) {
stream << "[";
for (int i=0;i<4;i++) {
// FIXME: The 0 test is to avoid a leading minus before a single 0 (cosmetics)
stream << ((this->matrix[i*4+j]==0)?0:this->matrix[i*4+j]);
if (i != 3) stream << ", ";
}
stream << "])";
stream << "]";
if (j != 3) stream << ", ";
}
stream << "])";
return stream.str();
}
@ -319,5 +278,4 @@ void register_builtin_transform()
builtin_modules["mirror"] = new TransformModule(MIRROR);
builtin_modules["translate"] = new TransformModule(TRANSLATE);
builtin_modules["multmatrix"] = new TransformModule(MULTMATRIX);
builtin_modules["color"] = new TransformModule(COLOR);
}

View File

@ -14,7 +14,7 @@ public:
virtual std::string toString() const;
virtual std::string name() const;
double matrix[20];
double matrix[16];
};
#endif

View File

@ -46,6 +46,9 @@ public:
virtual Response visit(class State &state, const class TransformNode &node) {
return visit(state, (const class AbstractNode &)node);
}
virtual Response visit(class State &state, const class ColorNode &node) {
return visit(state, (const class AbstractNode &)node);
}
// Add visit() methods for new visitable subtypes of AbstractNode here
};