home *** CD-ROM | disk | FTP | other *** search
- /*
- * Interpreter.C - methods for turtle interpreter.
- *
- * Copyright (C) 1992, Christoph Streit (streit@iam.unibe.ch)
- * University of Berne, Switzerland
- * All rights reserved.
- *
- * This software may be freely copied, modified, and redistributed
- * provided that this copyright notice is preserved on all copies.
- *
- * You may not distribute this software, in whole or in part, as part of
- * any commercial product without the express consent of the authors.
- *
- * There is no warranty or other guarantee of fitness of this software
- * for any purpose. It is provided solely "as is".
- *
- */
-
- #include "Interpreter.h"
- #include "Options.h"
- #include "Polygon.h"
- #include "Turtle.h"
- #include "DeviceDriver.h"
- #include "Module.h"
- #include "Hull.h"
- #include "boolean.h"
-
- #define startBranchSymbol "["
- #define endBranchSymbol "]"
-
- #ifdef OLD_STYLE_CPP
- InterpreterFunctions Interpreter::FuncTable[] = {
- #else
- Interpreter::InterpreterFunctions Interpreter::FuncTable[] = {
- #endif
-
- {"F", &Interpreter::forward},
- {"wi", &Interpreter::width},
- {"G", &Interpreter::go},
- {"f", &Interpreter::go},
- {"pt", &Interpreter::pitch},
- {"&", &Interpreter::pitch_negativ},
- {"^", &Interpreter::pitch},
- {"ro", &Interpreter::roll},
- {"\\", &Interpreter::roll_negativ},
- {"/", &Interpreter::roll},
- {"tu", &Interpreter::turn},
- {"+", &Interpreter::turn_negativ},
- {"-", &Interpreter::turn},
- {"rv", &Interpreter::rotate_vertical},
- {"$", &Interpreter::rotate_vertical},
- {"|", &Interpreter::reverse},
- {startBranchSymbol, &Interpreter::push},
- {endBranchSymbol, &Interpreter::pop},
- {"{", &Interpreter::startPolygon},
- {"sv", &Interpreter::saveVertex},
- {".", &Interpreter::saveVertex},
- {"}", &Interpreter::endPolygon},
- {"t", &Interpreter::tropism},
- {"we", &Interpreter::weight},
- {"co", &Interpreter::color},
- {"sm", &Interpreter::beginMacro},
- {"em", &Interpreter::endMacro},
- {"xm", &Interpreter::executeMacro},
- {"lib", &Interpreter::libraryObject},
- {"s", &Interpreter::sphere},
- {"tri", &Interpreter::triangle},
- {"poly", &Interpreter::polygon},
- {"ah", &Interpreter::activateHull},
- {"dh", &Interpreter::deactivateHull},
- {"cb", &Interpreter::cutBranchWhenHit},
- {"%", &Interpreter::cutBranch},
- {"texture", &Interpreter::texture},
- {"LastFunc", NULL}
- };
-
- FP* Interpreter::functable = NULL;
- Value Interpreter::turtleX;
- Value Interpreter::turtleY;
- Value Interpreter::turtleZ;
-
- typedef Turtle* TurtlePtr;
- declareList(TurtleStack, TurtlePtr)
- implementList(TurtleStack, TurtlePtr)
-
- //___________________________________________________________ Interpreter
-
- Interpreter::Interpreter(DeviceDriver* device, Options* theOptions)
- : d(device), options(theOptions),
- t(NULL), m(NULL), ml(NULL), currentModule(-1), saveTurtle(NULL),
- polyStack(new PolygonList(100)), turtleStack(new TurtleStack(100)),
- definingMacro(FALSE), reflected(FALSE), deleteBranchWhenHit(FALSE)
- {
-
- /*
- * Set up function table if not already done.
- */
- if (functable == NULL) {
-
- /*
- * Add all the function names to global name space.
- */
- for (register int i=0; FuncTable[i].function != NULL; i++)
- FuncTable[i].index = Name::addname(FuncTable[i].name);
-
- functable = new FP[Name::namesCount()];
-
- /*
- * Generate and initialize hash table for function pointers.
- */
- for (i=0; i<Name::namesCount(); i++)
- functable[i] = NULL;
- for (i=0; FuncTable[i].function != NULL; i++)
- functable[FuncTable[i].index] = FuncTable[i].function;
- }
-
- /*
- * Set up analytic tropism functions and hulls.
- */
- tropismX = options->tropismX;
- tropismY = options->tropismY;
- tropismZ = options->tropismZ;
- tropismFunction =
- (tropismX != NULL) || (tropismY != NULL) || (tropismZ != NULL);
- tropismWeight = options->weight;
- weightFunction = (tropismWeight != NULL);
- h = options->hulls;
- }
-
- Interpreter::~Interpreter()
- {
- delete polyStack;
- delete turtleStack;
- }
-
- /*
- * Interpret the module string modules using the given dive driver.
- */
-
- void Interpreter::interpret(ModuleList* modules)
- {
- t = new Turtle;
- ml = modules;
-
- /*
- * Start interpretation of module string ml.
- */
- if (!options->quiet)
- cerr << "Start of Interpretation ...\n\n";
-
- d->begin();
-
- /*
- * For each module lookup corresponding interpreter function, call it
- * if not NULL.
- */
- for (currentModule = 0; currentModule < ml->count(); currentModule++) {
- m = ml->item(currentModule);
- if (functable[ml->item(currentModule)->name().index()] != NULL) {
- FP currentFunc = functable[ml->item(currentModule)->name().index()];
- (this->*currentFunc)();
- }
-
- if (options->deleteModules)
- delete ml->item(currentModule);
-
- /*
- * If we hit any hull primitive and deleteBranchWhenHit option is
- * set, ignore all modules up to the next end branch command ("]").
- * Note that we have to skip all the subbranches also.
- */
- if (deleteBranchWhenHit && reflected) {
- reflected = FALSE; // reset
- cutBranch();
- }
- }
-
- flush();
-
- /*
- * Visualize the defined hulls.
- */
- if (options->showHulls) {
- PolygonList* polys;
-
- Color hullColor("HullColor");
- d->color(hullColor);
- for (HullSymtab_Iterator hullItr(*h); hullItr.more(); hullItr.next()) {
- polys = hullItr.cur_value()->convertToPolygonList(t->bbox());
- for (register long i=0; i<polys->count(); i++)
- d->polygon(polys->item(i));
- delete polys;
- }
- }
-
- if (options->verbose) {
- cerr << "BoundingBox: " << t->bbox() << "\n\n";
- cerr << "intersection tests: "
- << GeoObject::getIntersectionTests() <<"\n"
- << "intersection hits: "
- << GeoObject::getIntersections() << "\n\n";
- }
-
- d->end(t->bbox());
-
- delete t;
- }
-
- //___________________________________________________________ interpreter functions
-
- /*
- * Flush checks if there is a segment in the pipe which has't been draw yet
- * (lastWidth > 0) and draws it in that case.
- */
-
- void Interpreter::flush()
- {
- /*
- * A segment left in the pipe?
- */
- if (t->getLastWidth() > 0) {
- if (tropismFunction || weightFunction)
- computeTropism();
-
- d->color(t->getLastColor());
- d->texture(t->getLastTexture());
-
- if (equal(t->getLastWidth(), t->getWidth()))
- d->cylinder(t->lastPos(), t->pos(), t->getWidth());
- else if (!options->coneSpheres)
- d->cone(t->lastPos(), t->getLastWidth(), t->pos(), t->getWidth());
- else
- createConeBetweenSpheres(t->lastPos(), t->getLastWidth(),
- t->pos(), t->getWidth());
- if (options->coneSpheres)
- d->sphere(t->pos(), t->getWidth());
- }
- t->setLastWidth(-1); // no segments left in the pipe
- }
-
- /*
- * Go forward and draw a line.
- * lastWidth < 0: no move has been previously done
- */
-
- void Interpreter::forward()
- {
- real step;
-
- /*
- * Is there an argument for the move?
- */
- if (m->argCount() <= 0 || !m->arg(0)->toReal(step))
- step = options->defaultForward;
-
- /*
- * Do we really have to move?
- */
- if (equal(step, 0)) {
- Error(ERR_ADVISE, "Interpreter::forward with argument 0");
- return;
- }
-
- /*
- * Recompute tropism and weight functions if any present.
- */
- if (tropismFunction || weightFunction)
- computeTropism();
-
- if (t->getLastWidth() > 0) {
- d->color(t->getLastColor());
- d->texture(t->getLastTexture());
- }
-
- real lastWidth = t->getLastWidth();
- real currentWidth = t->getWidth();
- Vector lastPos = t->lastPos();
- Vector currentPos = t->pos();
- reflected = t->forward(step);
- Vector newPos = t->pos();
-
- /*
- * Regular line segment or cone spheres?
- */
- if (lastWidth > 0) {
- if (equal(lastWidth, currentWidth))
- d->cylinder(lastPos, currentPos, lastWidth);
- else if (!options->coneSpheres)
- d->cone(lastPos, lastWidth, currentPos, currentWidth);
- else
- createConeBetweenSpheres(lastPos, lastWidth, currentPos, currentWidth);
-
- /*
- * Add sphere when there's an angle between the segments.
- */
- if (options->coneSpheres && !((lastPos-currentPos)*(currentPos-newPos)).zero())
- d->sphere(currentPos, currentWidth);
- }
- }
-
- /*
- * Go forward without drawing a line.
- */
-
- void Interpreter::go()
- {
- real step;
-
- flush();
- if (tropismFunction || weightFunction)
- computeTropism();
-
- if (m->argCount()>0 && m->arg(0)->toReal(step))
- reflected = t->forward(step);
- else
- reflected = t->forward(options->defaultForward);
-
- /*
- * There's no segment in the pipe left.
- */
- t->setLastWidth(-1);
- }
-
- /*
- * Set line width.
- */
-
- void Interpreter::width()
- {
- real width;
- if (m->argCount()>0 && m->arg(0)->toReal(width) && width>0)
- t->setWidth(width);
- }
-
- /*
- * Pitch turtle in positive direction.
- */
-
- void Interpreter::pitch()
- {
- real a;
- if (m->argCount()>0 && m->arg(0)->toReal(a))
- t->pitch(dtor(a));
- else
- t->pitch(options->defaultPitch);
- }
-
- /*
- * Pitch turtle in negative direction.
- */
-
- void Interpreter::pitch_negativ()
- {
- real a;
- if (m->argCount()>0 && m->arg(0)->toReal(a))
- t->pitch(-dtor(a));
- else
- t->pitch(-options->defaultPitch);
- }
-
- /*
- * Turn turtle in positive direction.
- */
-
- void Interpreter::turn()
- {
- real a;
- if (m->argCount()>0 && m->arg(0)->toReal(a))
- t->turn(dtor(a));
- else
- t->turn(options->defaultTurn);
- }
-
- /*
- * Turn turtle in negative direction.
- */
-
- void Interpreter::turn_negativ()
- {
- real a;
- if (m->argCount()>0 && m->arg(0)->toReal(a))
- t->turn(-dtor(a));
- else
- t->turn(-options->defaultTurn);
- }
-
- /*
- * Roll turtle in positive direction.
- */
-
- void Interpreter::roll()
- {
- real a;
- if (m->argCount()>0 && m->arg(0)->toReal(a))
- t->roll(dtor(a));
- else
- t->roll(options->defaultRoll);
- }
-
- /*
- * Roll turtle in negative direction.
- */
-
- void Interpreter::roll_negativ()
- {
- real a;
- if (m->argCount()>0 && m->arg(0)->toReal(a))
- t->roll(-dtor(a));
- else
- t->roll(-options->defaultRoll);
- }
-
- /*
- * Align turtle vertical.
- */
-
- void Interpreter::rotate_vertical()
- {
- t->rotate_vertical();
- }
-
- /*
- * Reverse heading of the turtle.
- */
-
- void Interpreter::reverse()
- {
- t->reverse();
- }
-
- /*
- * Save current turtle state on the stack.
- */
-
- void Interpreter::push()
- {
- turtleStack->prepend(t);
- t = new Turtle(*t);
- t->setLastWidth(-1);
- }
-
- /*
- * Restore turtle state from stack.
- */
-
- void Interpreter::pop()
- {
- flush();
-
- if (turtleStack->count() > 0) {
- turtleStack->item(0)->expandBBoxBy(t->bbox());
- delete t;
- t = turtleStack->item(0);
- turtleStack->remove(0);
- }
- else
- Error(ERR_WARN, "Interpreter::pop stack is empty");
- }
-
- /*
- * Start a new polygon.
- */
-
- void Interpreter::startPolygon()
- {
- polyStack->prepend(new Polygon);
- }
-
- /*
- * Add vertex to current polygon.
- */
-
- void Interpreter::saveVertex()
- {
- if (polyStack->count() > 0)
- polyStack->item(0)->addVertex(t->pos());
- else
- Error(ERR_WARN, "Interpreter::saveVertex polygon stack is empty");
- }
-
- /*
- * End of polygon definition.
- */
-
- void Interpreter::endPolygon()
- {
- if (polyStack->count() <= 0) {
- Error(ERR_WARN, "Interpreter::endPolygon polygon stack is empty");
- return;
- }
-
- Polygon* p = polyStack->item(0);
-
- if (p->numVertices() <= 0) {
- Error(ERR_WARN, "Interpreter::endPolygon no vertex in polygon");
- delete p;
- polyStack->remove(0);
- return;
- }
-
- /*
- * Start and endpoint should not be the same.
- */
- if (p->numVertices()>1 && p->vertex(0) == p->vertex(p->numVertices()-1))
- p->removeVertex(p->numVertices()-1);
-
- /*
- * Set color and texture.
- */
- d->color(t->getColor());
- d->texture(t->getTexture());
-
- d->polygon(p);
-
- polyStack->remove(0);
- }
-
- /*
- * Set tropism vector.
- */
-
- void Interpreter::tropism()
- {
- real x, y, z, w;
- if (m->argCount() >= 3 &&
- m->arg(0)->toReal(x) && m->arg(1)->toReal(y) && m->arg(2)->toReal(z)) {
- t->setTropism(x,y,z);
-
- if (m->argCount() >= 4 && m->arg(3)->toReal(w))
- t->setWeight(w);
- }
- }
-
- /*
- * Set weight for tropism calculations.
- */
-
- void Interpreter::weight()
- {
- real w;
- if (m->argCount()>0 && m->arg(0)->toReal(w))
- t->setWeight(w);
- }
-
- /*
- * Set color.
- */
-
- void Interpreter::color()
- {
- if (m->argCount()>0) {
- Color c(m->arg(0)->toString());
- t->setColor(c);
- }
- }
-
- /*
- * Start macro definition.
- */
-
- void Interpreter::beginMacro()
- {
- if (definingMacro)
- Error(ERR_PANIC, "Interpreter::beginMacro nested macro definition");
-
- if (m->argCount() <= 0)
- Error(ERR_PANIC, "Interpreter::beginMacro macro definition without a name");
-
- rcString macroName(m->arg(0)->toString());
-
- if (!d->addMacroName(macroName))
- Error(ERR_PANIC, "Interpreter::beginMacro macro \""
- + macroName + "\" already defined");
-
- saveTurtle = t;
- t = new Turtle;
- definingMacro = TRUE;
- d->beginMacro(macroName);
- }
-
- /*
- * End of macro definition.
- */
-
- void Interpreter::endMacro()
- {
- if (definingMacro == FALSE) {
- Error(ERR_WARN, "Interpreter::endMacro missing begin macro");
- return;
- }
-
- flush();
- d->endMacro();
- delete t;
- t = saveTurtle; saveTurtle = NULL;
- definingMacro = FALSE;
- }
-
- /*
- * Execute a already defined macro.
- */
-
- void Interpreter::executeMacro()
- {
- if (m->argCount() <= 0) {
- Error(ERR_WARN, "Interpreter::executeMacro macro without a name");
- return;
- }
-
- TransMatrix tmat;
- real scale;
-
- tmat(0,0) = t->vecU()[0]; tmat(0,1) = t->vecU()[1]; tmat(0,2) = t->vecU()[2];
- tmat(1,0) = t->vecL()[0]; tmat(1,1) = t->vecL()[1]; tmat(1,2) = t->vecL()[2];
- tmat(2,0) = t->vecH()[0]; tmat(2,1) = t->vecH()[1]; tmat(2,2) = t->vecH()[2];
-
- if (m->argCount()>1 && m->arg(1)->toReal(scale) && !equal(scale, 1))
- tmat.scale(scale, scale, scale);
- tmat.translate(t->pos());
-
- d->executeMacro(m->arg(0)->toString(), tmat);
- }
-
- /*
- * Include a predefined object.
- */
-
- void Interpreter::libraryObject()
- {
- if (m->argCount() <= 0) {
- Error(ERR_WARN, "Interpreter::libraryObject object without a name");
- return;
- }
-
- TransMatrix tmat;
- real scale;
-
- rcString libName(m->arg(0)->toString());
-
- d->addLibraryObjectName(libName);
-
- tmat(0,0) = t->vecU()[0]; tmat(0,1) = t->vecU()[1]; tmat(0,2) = t->vecU()[2];
- tmat(1,0) = t->vecL()[0]; tmat(1,1) = t->vecL()[1]; tmat(1,2) = t->vecL()[2];
- tmat(2,0) = t->vecH()[0]; tmat(2,1) = t->vecH()[1]; tmat(2,2) = t->vecH()[2];
-
- if (m->argCount()>1 && m->arg(1)->toReal(scale) && !equal(scale, 1))
- tmat.scale(scale, scale, scale);
- tmat.translate(t->pos());
-
- d->libraryObject(libName, tmat);
- }
-
- /*
- * Generate sphere at turtle position with given radius.
- */
-
- void Interpreter::sphere()
- {
- real r;
-
- /*
- * Set color and texture.
- */
- d->color(t->getColor());
- d->texture(t->getTexture());
-
- if (m->argCount()>0 && m->arg(0)->toReal(r))
- d->sphere(t->pos(), r);
- else
- d->sphere(t->pos(), 1);
- }
-
- /*
- * Generate triangle with given vertices.
- */
-
- void Interpreter::triangle()
- {
- if (m->argCount() < 9)
- return;
-
- real coords[9];
- for (register int i=0; i<9; i++)
- if (!m->arg(i)->toReal(coords[i]))
- return;
-
- Vector p1(coords[0], coords[1], coords[2]);
- Vector p2(coords[3], coords[4], coords[5]);
- Vector p3(coords[6], coords[7], coords[8]);
-
- BoundingBox tmpBBox;
- tmpBBox.expand(p1);
- tmpBBox.expand(p2);
- tmpBBox.expand(p3);
- t->expandBBoxBy(tmpBBox);
-
- /*
- * Set color and texture.
- */
- d->color(t->getColor());
- d->texture(t->getTexture());
-
- d->polygon(new Polygon(p1, p2, p3));
- }
-
- /*
- * Generate polygon with given vertices.
- */
-
- void Interpreter::polygon()
- {
- if (m->argCount() < 9)
- return;
-
- real x, y, z;
- Polygon* p = new Polygon(3);
- BoundingBox tmpBBox;
-
- for (register int i=0; i<m->argCount()-2; i+=3) {
- if (m->arg(i)->toReal(x) &&
- m->arg(i+1)->toReal(y) &&
- m->arg(i+2)->toReal(z))
- {
- p->addVertex(Vector(x,y,z));
- tmpBBox.expand(Vector(x,y,z));
- }
- }
-
- if (p->numVertices() > 2) {
-
- /*
- * Set color and texture.
- */
- d->color(t->getColor());
- d->texture(t->getTexture());
-
- d->polygon(p);
- t->expandBBoxBy(tmpBBox);
- }
- else
- delete p;
- }
-
- /*
- * Activate a hull. Turtle will move with respect to the primitives
- * of the hull.
- */
-
- void Interpreter::activateHull()
- {
- if (m->argCount()<=0) {
- Error(ERR_WARN, "Interpreter::activateHull no hull name");
- return;
- }
-
- rcString hullName(m->arg(0)->toString());
-
- Hull* hull;
- if (!h->lookup(hullName, hull)) {
- Error(ERR_WARN, "Interpreter::activateHull unknown hull " + hullName);
- return;
- }
- real reflectanceFactor = 1;
- if (m->argCount()>1)
- m->arg(1)->toReal(reflectanceFactor);
-
- t->setHull(hull, reflectanceFactor);
- }
-
- void Interpreter::deactivateHull()
- {
- t->unsetHull();
- }
-
- /*
- * Set/Unset of delete branch when conflict with current hull.
- */
-
- void Interpreter::cutBranchWhenHit()
- {
- real flag = TRUE;
- if (m->argCount() > 0)
- m->arg(0)->toReal(flag);
-
- if ((int)flag) {
- deleteBranchWhenHit = TRUE;
- t->setStopOnHit();
- }
- else {
- deleteBranchWhenHit = FALSE;
- t->unsetStopOnHit();
- }
- }
-
- /*
- * Ignore all the modules until we find a end branch symbol (ignore
- * subbranches also).
- */
-
- void Interpreter::cutBranch()
- {
- static Name startBranch(startBranchSymbol);
- static Name endBranch(endBranchSymbol);
- int nested = 0;
-
- /*
- * Ignore all modules up to the next end branch command ("]").
- * Note that we have to skip all the subbranches also.
- */
- while (++currentModule < ml->count()) {
- if (ml->item(currentModule)->name() == startBranch)
- nested++;
- else if (ml->item(currentModule)->name() == endBranch) {
- if (nested-- == 0) {
- currentModule--;
- break;
- }
- }
- if (options->deleteModules)
- delete ml->item(currentModule);
- }
- }
-
- /*
- * Define texture for the following primitives.
- */
- void Interpreter::texture()
- {
- if (m->argCount()>0) {
- rcString theTexture(m->arg(0)->toString());
- t->setTexture(theTexture);
- }
- else
- t->setTexture(""); // disable textures
- }
-
- /*
- * Generate a cone between two given spheres.
- */
-
- void Interpreter::createConeBetweenSpheres(const Vector& p1, real r1,
- const Vector& p2, real r2)
- {
- real a = r1-r2;
- real r = r1;
- real c = p1.distance(p2);
- int secondIsLarger = FALSE;
-
- if (c<r1 || c<r2) {
- Error(ERR_ADVISE, rcString("Interpreter::createConeBetweenSpheres\n") +
- "\tdistance between spheres is too small -> can't create cone\n");
- return;
- }
-
- if (a < 0) {
- r = r2;
- a = -a;
- secondIsLarger = TRUE;
- }
-
- real p = a*a/c;
- real ps = p*r/a;
-
- real h = sqrt(p*(c-p));
- real hs = h*r/a;
-
- Vector dir = p2 - p1;
-
- if (secondIsLarger)
- d->cone(p1-dir*((ps-p)/c), hs-h, p2-dir*(ps/c), hs);
- else
- d->cone(p1+dir*(ps/c), hs, p2+dir*((ps-p)/c), hs-h);
- }
-
- /*
- * If tropism vector depends on turtle position, we recompute the
- * tropism vector after each move.
- */
-
- void Interpreter::computeTropism()
- {
- turtleX = t->pos()[0];
- turtleY = t->pos()[1];
- turtleZ = t->pos()[2];
-
- if (tropismFunction)
- t->setTropism(tropismX->evaluate(),
- tropismY->evaluate(),
- tropismZ->evaluate());
-
- if (weightFunction)
- t->setWeight(tropismWeight->evaluate());
- }
-