refactoring
authorJohn Tsiombikas <nuclear@mutantstargoat.com>
Thu, 20 Oct 2016 19:04:17 +0000 (22:04 +0300)
committerJohn Tsiombikas <nuclear@mutantstargoat.com>
Thu, 20 Oct 2016 19:04:17 +0000 (22:04 +0300)
15 files changed:
.gitignore
Makefile
src/gear.cc [deleted file]
src/gear.h [deleted file]
src/machine.cc [deleted file]
src/machine.h [deleted file]
src/machine/gear.cc [new file with mode: 0644]
src/machine/gear.h [new file with mode: 0644]
src/machine/machine.cc [new file with mode: 0644]
src/machine/machine.h [new file with mode: 0644]
src/machine/mparser.cc [new file with mode: 0644]
src/machine/mparser.h [new file with mode: 0644]
src/main.cc
src/mparser.cc [deleted file]
src/mparser.h [deleted file]

index fe1e9f6..6871807 100644 (file)
@@ -1,4 +1,6 @@
 *.o
 *.swp
 *.d
-anti
+demo
+data/
+.clang_complete
index 228e095..3c0cb0a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,20 +1,32 @@
-src = $(wildcard src/*.cc)
-csrc = $(wildcard src/*.c)
+src = $(wildcard src/*.cc) $(wildcard src/machine/*.cc)
+csrc = $(wildcard src/*.c) $(wildcard src/machine/*.c)
 obj = $(src:.cc=.o) $(csrc:.c=.o)
 dep = $(obj:.o=.d)
-bin = anti
+bin = demo
 
-CFLAGS = -pedantic -Wall -g -I/usr/local/include
-CXXFLAGS = -std=c++11 -pedantic -Wall -g -I/usr/local/include
-LDFLAGS = -L/usr/local/lib $(libgl_$(sys)) -lm -lgmath -lvmath -limago -lresman -lpthread
+opt = -O0
+dbg = -g
+
+incpath = -Isrc -Isrc/machine -I/usr/local/include
+libpath = -L/usr/local/lib
+
+warn = -pedantic -Wall
+
+CFLAGS = $(warn) $(opt) $(dbg) $(incpath)
+CXXFLAGS = -std=c++11 $(warn) $(opt) $(dbg) $(incpath)
+LDFLAGS = $(libpath) $(libgl_$(sys)) -lm -lgmath -lvmath -limago -lresman -lpthread
 
 sys = $(shell uname -s)
 libgl_Linux = -lGL -lGLU -lglut -lGLEW
 libgl_Darwin = -framework OpenGL -framework GLUT -lGLEW
 
-$(bin): $(obj)
+$(bin): .clang_complete $(obj)
        $(CXX) -o $@ $(obj) $(LDFLAGS)
 
+.clang_complete: Makefile
+       rm -f $@
+       for i in $(CXXFLAGS); do echo $$i >>$@; done
+
 -include $(dep)
 
 %.d: %.c
diff --git a/src/gear.cc b/src/gear.cc
deleted file mode 100644 (file)
index 6a34815..0000000
+++ /dev/null
@@ -1,309 +0,0 @@
-#include <stdlib.h>
-#include <GL/glew.h>
-#include "gear.h"
-#include "meshgen.h"
-#include "app.h"
-
-Gear::Gear()
-       : axis(0, 0, 1)
-{
-       pdist = 0;
-       angle = 0;
-       teeth_length = 5;
-       thickness = 5;
-       bevel = 1.5;
-       init_angle = 0;
-       xform_valid = false;
-
-       set_teeth(42, 10);
-
-       supergear = 0;
-
-       mesh = 0;
-
-       color = Vec3(0.6, 0.6, 0.6);
-       roughness = 1.0;
-       metallic = false;
-}
-
-Gear::~Gear()
-{
-       delete mesh;
-}
-
-void Gear::attach(Gear *g)
-{
-       if(g->supergear) {
-               if(g->supergear == this) {
-                       return;
-               }
-               g->supergear->detach(g);
-       }
-       g->supergear = this;
-       subgears.push_back(g);
-
-       // make co-axial
-       g->axis = axis;
-       g->pos.x = pos.x;
-       g->pos.y = pos.y;
-}
-
-bool Gear::detach(Gear *g)
-{
-       int nsubgears = (int)subgears.size();
-       for(int i=0; i<nsubgears; i++) {
-               if(subgears[i] == g) {
-                       subgears.erase(subgears.begin() + i);
-                       g->supergear = 0;
-                       return true;
-               }
-       }
-       return false;
-}
-
-Gear *Gear::get_super() const
-{
-       return supergear;
-}
-
-void Gear::set_angular_offset(float offs)
-{
-       init_angle = offs;
-       xform_valid = false;
-}
-
-float Gear::get_angular_offset() const
-{
-       return init_angle;
-}
-
-void Gear::set_teeth(int nt, float tooth_pitch)
-{
-       if(tooth_pitch <= 0) {
-               tooth_pitch = this->tooth_pitch;
-       } else {
-               this->tooth_pitch = tooth_pitch;
-       }
-       float circ = tooth_pitch * nt;
-       radius = circ / (2.0 * M_PI);
-       nteeth = nt;
-}
-
-void Gear::set_axis(const Vec3 &axis)
-{
-       this->axis = normalize(axis);
-       xform_valid = false;
-}
-
-const Vec3 &Gear::get_axis() const
-{
-       return axis;
-}
-
-void Gear::set_position(const Vec3 &pos)
-{
-       if(!supergear) {
-               this->pos = pos;
-       } else {
-               this->pos = supergear->pos;
-               this->pos.z = pos.z;
-       }
-       xform_valid = false;
-
-       for(int i=0; i<(int)subgears.size(); i++) {
-               Vec3 subpos = this->pos;
-               subpos.z = subgears[i]->pos.z;
-               subgears[i]->set_position(subpos);
-       }
-}
-
-const Vec3 &Gear::get_position() const
-{
-       return pos;
-}
-
-Vec3 Gear::get_global_position() const
-{
-       const Mat4 &m = get_matrix();
-       return m * Vec3(0, 0, 0);
-}
-
-void Gear::set_angle(float angle)
-{
-       this->angle = angle;
-       xform_valid = false;
-}
-
-float Gear::get_angle() const
-{
-       return angle;
-}
-
-float Gear::get_vis_rotation() const
-{
-       return fmod(init_angle + angle, M_PI * 2.0);
-}
-
-const Mat4 &Gear::get_matrix() const
-{
-       if(!xform_valid) {
-               calc_matrix();
-               xform_valid = true;
-       }
-       return xform;
-}
-
-const Mat4 &Gear::get_dir_matrix() const
-{
-       if(!xform_valid) {
-               calc_matrix();
-               xform_valid = true;
-       }
-       return dir_xform;
-}
-
-float Gear::get_angular_pitch() const
-{
-       return 2.0 * M_PI / (float)nteeth;
-}
-
-void Gear::draw() const
-{
-       if(!mesh) {
-               if(!((Gear*)this)->gen_mesh()) {
-                       abort();
-               }
-       }
-       if(!xform_valid) {
-               calc_matrix();
-       }
-
-       glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_LIGHTING_BIT);
-
-       glPushMatrix();
-       glMultMatrixf(xform[0]);
-
-       if(opt_gear_wireframe) {
-               glPolygonOffset(1, 1);
-               glEnable(GL_POLYGON_OFFSET_FILL);
-       }
-
-       Vec3 diffuse = metallic ? Vec3(0, 0, 0) : color;
-       float col[] = {diffuse.x, diffuse.y, diffuse.z, 1.0};
-       glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, col);
-
-       Vec3 specular = (metallic ? color : Vec3(1, 1, 1)) * (1.0 - roughness);
-       float scol[] = {specular.x, specular.y, specular.z, 1.0};
-       glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, scol);
-       glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 1.0 + (1.0 - roughness) * 60.0);
-
-       mesh->draw();
-
-       glDisable(GL_LIGHTING);
-
-       if(opt_gear_wireframe) {
-               glColor3f(0.2, 0.4, 1.0);
-               mesh->draw_wire();
-       }
-
-       glPopMatrix();
-       glPopAttrib();
-}
-
-void Gear::draw_wire(float wire_width) const
-{
-       if(!mesh) {
-               if(!((Gear*)this)->gen_mesh()) {
-                       abort();
-               }
-       }
-       if(!xform_valid) {
-               calc_matrix();
-       }
-
-       glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT);
-       glLineWidth(wire_width);
-       glDisable(GL_LIGHTING);
-
-       glPushMatrix();
-       glMultMatrixf(xform[0]);
-
-       mesh->draw_wire();
-
-       glPopMatrix();
-       glPopAttrib();
-}
-
-static Vec2 rev_pos(float u, float v, void *cls)
-{
-       Gear *gear = (Gear*)cls;
-
-       float y = ((v - 1.0 / 3.0) / (1.0 / 3.0) - 0.5);
-       if(y < -0.5) y = -0.5;
-       if(y > 0.5) y = 0.5;
-       y *= gear->thickness;
-
-       if(v < 0.001 || v > 0.999) {
-               return Vec2(0, -y);
-       }
-
-       float nt = (float)gear->nteeth * 4.0;
-
-       int part = (int)(u * nt) % 4;   /* 4 parts to a tooth /#\_ */
-       float t = fmod(u * nt * 4.0, 1.0);
-
-       float offs;
-       switch(part) {
-       case 0:
-               offs = t;
-               break;
-       case 1:
-               offs = 1.0f;
-               break;
-       case 2:
-               offs = 1.0f - t;
-               break;
-       case 3:
-               offs = 0.0f;
-       }
-
-       float inner_rad = gear->radius - gear->teeth_length;
-       return Vec2(inner_rad + offs * gear->teeth_length, -y);
-}
-
-bool Gear::gen_mesh()
-{
-       mesh = new Mesh;
-       gen_revol(mesh, nteeth * 4, 3, rev_pos, 0, this);
-       mesh->explode();
-       mesh->calc_face_normals();
-
-       float fix_tooth_up = get_angular_pitch() * 3.0 / 8.0;
-
-       Mat4 rot;
-       rot.rotation_x(M_PI / 2.0);
-       rot.rotate_z(fix_tooth_up);
-       mesh->apply_xform(rot, rot);
-
-       mesh->set_vis_vecsize(6.0);
-       return true;
-}
-
-void Gear::calc_matrix() const
-{
-       Vec3 up = Vec3(0, 1, 0);
-       if(1.0 - fabs(dot(up, axis)) < 1e-4) {
-               up = Vec3(0, 0, -1);
-       }
-       Vec3 right = normalize(cross(up, axis));
-       up = cross(axis, right);
-
-       dir_xform = Mat4(right, up, axis);
-
-       xform = dir_xform;
-       xform.rotate_z(get_vis_rotation());
-       xform.translate(pos);
-
-       axel_xform = dir_xform;
-       axel_xform.translate(pos);
-}
diff --git a/src/gear.h b/src/gear.h
deleted file mode 100644 (file)
index fac245c..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-#ifndef GEAR_H_
-#define GEAR_H_
-
-#include <vector>
-#include <string>
-#include <gmath/gmath.h>
-#include "mesh.h"
-
-/* distance unit: millimeters
- * angle unit: radians
- */
-
-class Gear;
-struct GearPin;
-struct GearSlot;
-
-struct GearPin {
-       float radius;
-       float height;
-
-       Gear *parent;
-       /* position in polar coordinates on the parent gear */
-       float pos_dist, pos_angle;
-
-       GearSlot *conn_slot;    /* slot connection */
-};
-
-struct GearSlot {
-       float radius, length;
-
-       Gear *parent;
-       /* position in polar coordinates on the parent gear */
-       float pos_dist_min, pos_dist_max, pos_angle;
-
-       GearPin *conn_pin;              /* pin connection */
-};
-
-class Gear {
-private:
-       Mesh *mesh;
-
-       mutable Mat4 xform, dir_xform, axel_xform;
-       mutable bool xform_valid;
-       void calc_matrix() const;
-
-       float contour(float u);
-
-public:
-
-       /* TODO hide most of this shit, especially the stuff which invalidate
-        * the transformation matrices
-        */
-       std::string name;
-       Vec3 pos, axis; // implicitly defines a plane eqn.
-       float pdist;    // derived: distance of plane from origin
-
-       float init_angle;       // initial starting angle
-       float angle;    // current angle of the gear
-
-       int nteeth;             // number of teeth
-       float tooth_pitch;
-
-       float radius;   // total radius of the gear, including teeth
-       float teeth_length;     // how far teeth extend past the radius
-       float thickness;        // thickness of the gear along the Z axis
-
-       float bevel;    // bevel size
-
-       // visual surface properties
-       Vec3 color;
-       float roughness;
-       bool metallic;
-
-       Gear *supergear;
-       std::vector<Gear*> subgears;
-       std::vector<GearPin> pins;
-       std::vector<GearSlot> slots;
-
-       Gear();
-       ~Gear();
-
-       void attach(Gear *g);
-       bool detach(Gear *g);
-       Gear *get_super() const;
-
-       void set_angular_offset(float offs);
-       float get_angular_offset() const;
-
-       // sets the supplied number of teeth, and calculates the radius
-       // of the gear, to achieve the required tooth pitch
-       void set_teeth(int nt, float tooth_pitch = 0.0f);
-       void set_axis(const Vec3 &axis);
-       const Vec3 &get_axis() const;
-       void set_position(const Vec3 &pos);
-       const Vec3 &get_position() const;
-       Vec3 get_global_position() const;       // taking parent gear into account
-
-       Vec3 get_planar_position() const;       // 2D pos of gear on its plane
-
-       void set_angle(float angle);
-       float get_angle() const;
-
-       float get_vis_rotation() const;
-       const Mat4 &get_matrix() const;
-       const Mat4 &get_dir_matrix() const;
-
-       // returns the angle (in radians) from one tooth to the next
-       float get_angular_pitch() const;
-
-       void draw() const;
-       void draw_wire(float line_width = 1.0f) const;
-
-       bool gen_mesh();
-};
-
-#endif // GEAR_H_
diff --git a/src/machine.cc b/src/machine.cc
deleted file mode 100644 (file)
index 441826e..0000000
+++ /dev/null
@@ -1,269 +0,0 @@
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <float.h>
-#include <assert.h>
-#include "opengl.h"
-#include "machine.h"
-
-static float delta_angle(float a, float b);
-
-Machine::Machine()
-{
-       meshing = 0;
-       meshing_valid = false;
-       visited = 0;
-}
-
-Machine::~Machine()
-{
-       int ngears = (int)gears.size();
-       for(int i=0; i<ngears; i++) {
-               delete gears[i];
-       }
-
-       if(meshing) {
-               delete [] meshing[0];
-               delete [] meshing;
-       }
-       delete [] visited;
-}
-
-void Machine::add_gear(Gear *g)
-{
-       if(gearidx.find(g) != gearidx.end()) {
-               return; // already have this gear
-       }
-       gearidx[g] = gears.size();
-       gears.push_back(g);
-       meshing_valid = false;
-}
-
-void Machine::add_motor(int gearidx, float speed_hz)
-{
-       Motor m;
-       m.drive = gearidx;
-       m.speed = speed_hz;
-       motors.push_back(m);
-}
-
-int Machine::get_gear_index(Gear *g) const
-{
-       std::map<Gear*, int>::const_iterator it = gearidx.find(g);
-       if(it == gearidx.end()) {
-               return -1;
-       }
-       return it->second;
-}
-
-void Machine::invalidate_meshing()
-{
-       meshing_valid = false;
-}
-
-void Machine::calc_meshing()
-{
-       int ngears = (int)gears.size();
-
-       if(!meshing) {
-               meshing = new bool*[ngears];
-               meshing[0] = new bool[ngears * ngears];
-
-               for(int i=1; i<ngears; i++) {
-                       meshing[i] = meshing[i - 1] + ngears;
-               }
-       }
-
-       if(!visited) {
-               visited = new bool[ngears];
-       }
-
-       // we're going to need the planar position of each gear on its plane, so let's cache it
-       Vec3 *ppos = (Vec3*)alloca(ngears * sizeof *ppos);
-       for(int i=0; i<ngears; i++) {
-               ppos[i] = gears[i]->get_position();
-       }
-
-       for(int i=0; i<ngears; i++) {
-               for(int j=i; j<ngears; j++) {
-                       meshing[i][j] = meshing[j][i] = false;
-
-                       if(i == j || gears[i]->get_super() == gears[j] || gears[j]->get_super() == gears[i]) {
-                               // don't attempt meshing if it's the same gear, or they are attached to each other
-                               continue;
-                       }
-
-                       if(1.0 - fabs(dot(gears[i]->axis, gears[j]->axis)) < 1e-5) {
-                               // co-planar, just check Z range after inverse-transforming to the XY plane
-                               if(fabs(ppos[i].z - ppos[j].z) > (gears[i]->thickness + gears[j]->thickness) / 2.0) {
-                                       continue;
-                               }
-                               // Z interval match, check distance
-                               float dsq = length_sq(ppos[i].xy() - ppos[j].xy());
-
-                               float outer_rad_sum = gears[i]->radius + gears[j]->radius;
-                               float inner_rad_sum = outer_rad_sum - gears[i]->teeth_length - gears[j]->teeth_length;
-
-                               if(dsq <= outer_rad_sum * outer_rad_sum && dsq >= inner_rad_sum * inner_rad_sum) {
-                                       //printf("connecting co-planar gears %d - %d\n", i, j);
-                                       meshing[i][j] = meshing[j][i] = true;
-                               }
-
-                       } else {
-                               /* TODO: not co-planar
-                                * - calc line of intersection between the two planes
-                                * - find distance of each gear to that line
-                                * - profit...
-                                */
-                       }
-               }
-       }
-
-       // fix the initial angles so that teeth mesh as best as possible
-       // should work in one pass as long as the gear train is not impossible
-       for(int i=0; i<ngears; i++) {
-               /*float rnd = gears[i]->angle + gears[i]->get_angular_pitch() / 2.0;
-               float snap = rnd - fmod(rnd, gears[i]->get_angular_pitch());
-               gears[i]->set_angle(snap);*/
-               gears[i]->set_angular_offset(0);
-       }
-
-       for(int i=0; i<ngears; i++) {
-               for(int j=i; j<ngears; j++) {
-                       if(meshing[i][j]) {
-                               assert(i != j);
-
-                               Vec2 dir = normalize(ppos[j].xy() - ppos[i].xy());
-                               float rel_angle = atan2(dir.y, dir.x);
-
-                               float frac_i = fmod((gears[i]->init_angle + rel_angle) / gears[i]->get_angular_pitch() + 100.0, 1.0);
-                               float frac_j = fmod((gears[j]->init_angle - rel_angle) / gears[j]->get_angular_pitch() + 100.0, 1.0);
-                               assert(frac_i >= 0.0 && frac_j >= 0.0);
-                               float delta = frac_j - frac_i;
-
-                               float correction = 0.5 - delta;
-                               float prev_offs = gears[j]->get_angular_offset();
-                               gears[j]->set_angular_offset(prev_offs + correction * gears[j]->get_angular_pitch());
-                       }
-               }
-       }
-
-       /*
-       printf("meshing graph\n");
-       for(int i=0; i<ngears; i++) {
-               putchar(' ');
-               for(int j=0; j<ngears; j++) {
-                       printf("| %d ", meshing[i][j] ? 1 : 0);
-               }
-               printf("|\n");
-       }
-       */
-}
-
-void Machine::update_gear(int idx, float angle)
-{
-       Gear *gear = gears[idx];
-
-       if(visited[idx]) {
-               if(delta_angle(angle, gear->angle) > 0.25 / gear->nteeth) {
-                       fprintf(stderr, "warning: trying to transmit different values to gear %s (%d)\n",
-                                       gear->name.c_str(), idx);
-                       gear->angle = 0;
-               }
-               return;
-       }
-
-       gear->set_angle(angle);
-       visited[idx] = true;
-
-       // propagate to meshing gears (depth-first)
-       int ngears = (int)gears.size();
-       for(int i=0; i<ngears; i++) {
-               if(!meshing[idx][i]) continue;
-               assert(idx != i);
-
-               float ratio = -(float)gear->nteeth / (float)gears[i]->nteeth;
-               update_gear(i, angle * ratio);
-       }
-
-       // propagate to rigidly attached gears
-       if(gear->supergear) {
-               int supidx = gearidx[gear->supergear];
-               update_gear(supidx, angle);
-       }
-
-       int nsub = (int)gear->subgears.size();
-       for(int i=0; i<nsub; i++) {
-               int subidx = gearidx[gear->subgears[i]];
-               update_gear(subidx, angle);
-       }
-}
-
-void Machine::update(float dt)
-{
-       int ngears = (int)gears.size();
-
-       if(!meshing_valid) {
-               calc_meshing();
-               meshing_valid = true;
-       }
-
-       memset(visited, 0, ngears * sizeof *visited);
-       for(size_t i=0; i<motors.size(); i++) {
-               int gidx = motors[i].drive;
-               if(gidx < 0) continue;
-
-               update_gear(gidx, gears[gidx]->angle + dt * motors[i].speed);
-       }
-}
-
-void Machine::draw() const
-{
-       for(size_t i=0; i<gears.size(); i++) {
-               gears[i]->draw();
-       }
-
-       float dcol[] = {0.4, 0.4, 0.4, 1.0};
-       float scol[] = {0, 0, 0, 0};
-       glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, dcol);
-       glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, scol);
-
-       glBegin(GL_QUADS);
-       glNormal3f(0, 1, 0);
-       glVertex3f(-300, -100, 300);
-       glVertex3f(300, -100, 300);
-       glVertex3f(300, -100, -300);
-       glVertex3f(-300, -100, -300);
-       glEnd();
-}
-
-Gear *Machine::intersect_gear(const Ray &ray, HitPoint *hitp) const
-{
-       Gear *res = 0;
-       HitPoint nearest;
-       nearest.dist = FLT_MAX;
-
-       for(size_t i=0; i<gears.size(); i++) {
-               Vec3 pos = gears[i]->get_global_position();
-               float rad = gears[i]->radius;
-
-               Plane plane = Plane(pos, gears[i]->axis);
-
-               HitPoint hit;
-               if(plane.intersect(ray, &hit) && hit.dist < nearest.dist &&
-                               length_sq(hit.pos - pos) <= rad * rad) {
-                       nearest = hit;
-                       res = gears[i];
-               }
-       }
-
-       if(hitp) *hitp = nearest;
-       return res;
-}
-
-static float delta_angle(float a, float b)
-{
-       float api = fmod(a + M_PI, 2.0 * M_PI);
-       float bpi = fmod(b + M_PI, 2.0 * M_PI);
-       return std::min(fabs(a - b), fabs(api - bpi));
-}
diff --git a/src/machine.h b/src/machine.h
deleted file mode 100644 (file)
index 097157d..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-#ifndef MACHINE_H_
-#define MACHINE_H_
-
-#include <vector>
-#include <map>
-#include "gear.h"
-
-struct Motor {
-       float speed;    /* signed to denote direction, in Hz */
-       int drive;              /* which gear it drives */
-};
-
-class Machine {
-private:
-       std::vector<Gear*> gears;
-       std::map<Gear*, int> gearidx;
-       bool **meshing;
-       bool meshing_valid;
-       bool *visited;  /* used for update_gear */
-
-       std::vector<Motor> motors;
-
-       void update_gear(int idx, float angle);
-
-public:
-       Machine();
-       ~Machine();
-
-       void add_gear(Gear *g); /* takes ownership */
-       void add_motor(int gearidx, float speed_hz);
-
-       int get_gear_index(Gear *g) const;
-
-       void invalidate_meshing();
-       void calc_meshing();
-
-       void update(float dt);
-       void draw() const;
-
-       Gear *intersect_gear(const Ray &ray, HitPoint *hitp = 0) const;
-};
-
-#endif // MACHINE_H_
diff --git a/src/machine/gear.cc b/src/machine/gear.cc
new file mode 100644 (file)
index 0000000..6a34815
--- /dev/null
@@ -0,0 +1,309 @@
+#include <stdlib.h>
+#include <GL/glew.h>
+#include "gear.h"
+#include "meshgen.h"
+#include "app.h"
+
+Gear::Gear()
+       : axis(0, 0, 1)
+{
+       pdist = 0;
+       angle = 0;
+       teeth_length = 5;
+       thickness = 5;
+       bevel = 1.5;
+       init_angle = 0;
+       xform_valid = false;
+
+       set_teeth(42, 10);
+
+       supergear = 0;
+
+       mesh = 0;
+
+       color = Vec3(0.6, 0.6, 0.6);
+       roughness = 1.0;
+       metallic = false;
+}
+
+Gear::~Gear()
+{
+       delete mesh;
+}
+
+void Gear::attach(Gear *g)
+{
+       if(g->supergear) {
+               if(g->supergear == this) {
+                       return;
+               }
+               g->supergear->detach(g);
+       }
+       g->supergear = this;
+       subgears.push_back(g);
+
+       // make co-axial
+       g->axis = axis;
+       g->pos.x = pos.x;
+       g->pos.y = pos.y;
+}
+
+bool Gear::detach(Gear *g)
+{
+       int nsubgears = (int)subgears.size();
+       for(int i=0; i<nsubgears; i++) {
+               if(subgears[i] == g) {
+                       subgears.erase(subgears.begin() + i);
+                       g->supergear = 0;
+                       return true;
+               }
+       }
+       return false;
+}
+
+Gear *Gear::get_super() const
+{
+       return supergear;
+}
+
+void Gear::set_angular_offset(float offs)
+{
+       init_angle = offs;
+       xform_valid = false;
+}
+
+float Gear::get_angular_offset() const
+{
+       return init_angle;
+}
+
+void Gear::set_teeth(int nt, float tooth_pitch)
+{
+       if(tooth_pitch <= 0) {
+               tooth_pitch = this->tooth_pitch;
+       } else {
+               this->tooth_pitch = tooth_pitch;
+       }
+       float circ = tooth_pitch * nt;
+       radius = circ / (2.0 * M_PI);
+       nteeth = nt;
+}
+
+void Gear::set_axis(const Vec3 &axis)
+{
+       this->axis = normalize(axis);
+       xform_valid = false;
+}
+
+const Vec3 &Gear::get_axis() const
+{
+       return axis;
+}
+
+void Gear::set_position(const Vec3 &pos)
+{
+       if(!supergear) {
+               this->pos = pos;
+       } else {
+               this->pos = supergear->pos;
+               this->pos.z = pos.z;
+       }
+       xform_valid = false;
+
+       for(int i=0; i<(int)subgears.size(); i++) {
+               Vec3 subpos = this->pos;
+               subpos.z = subgears[i]->pos.z;
+               subgears[i]->set_position(subpos);
+       }
+}
+
+const Vec3 &Gear::get_position() const
+{
+       return pos;
+}
+
+Vec3 Gear::get_global_position() const
+{
+       const Mat4 &m = get_matrix();
+       return m * Vec3(0, 0, 0);
+}
+
+void Gear::set_angle(float angle)
+{
+       this->angle = angle;
+       xform_valid = false;
+}
+
+float Gear::get_angle() const
+{
+       return angle;
+}
+
+float Gear::get_vis_rotation() const
+{
+       return fmod(init_angle + angle, M_PI * 2.0);
+}
+
+const Mat4 &Gear::get_matrix() const
+{
+       if(!xform_valid) {
+               calc_matrix();
+               xform_valid = true;
+       }
+       return xform;
+}
+
+const Mat4 &Gear::get_dir_matrix() const
+{
+       if(!xform_valid) {
+               calc_matrix();
+               xform_valid = true;
+       }
+       return dir_xform;
+}
+
+float Gear::get_angular_pitch() const
+{
+       return 2.0 * M_PI / (float)nteeth;
+}
+
+void Gear::draw() const
+{
+       if(!mesh) {
+               if(!((Gear*)this)->gen_mesh()) {
+                       abort();
+               }
+       }
+       if(!xform_valid) {
+               calc_matrix();
+       }
+
+       glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_LIGHTING_BIT);
+
+       glPushMatrix();
+       glMultMatrixf(xform[0]);
+
+       if(opt_gear_wireframe) {
+               glPolygonOffset(1, 1);
+               glEnable(GL_POLYGON_OFFSET_FILL);
+       }
+
+       Vec3 diffuse = metallic ? Vec3(0, 0, 0) : color;
+       float col[] = {diffuse.x, diffuse.y, diffuse.z, 1.0};
+       glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, col);
+
+       Vec3 specular = (metallic ? color : Vec3(1, 1, 1)) * (1.0 - roughness);
+       float scol[] = {specular.x, specular.y, specular.z, 1.0};
+       glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, scol);
+       glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 1.0 + (1.0 - roughness) * 60.0);
+
+       mesh->draw();
+
+       glDisable(GL_LIGHTING);
+
+       if(opt_gear_wireframe) {
+               glColor3f(0.2, 0.4, 1.0);
+               mesh->draw_wire();
+       }
+
+       glPopMatrix();
+       glPopAttrib();
+}
+
+void Gear::draw_wire(float wire_width) const
+{
+       if(!mesh) {
+               if(!((Gear*)this)->gen_mesh()) {
+                       abort();
+               }
+       }
+       if(!xform_valid) {
+               calc_matrix();
+       }
+
+       glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT);
+       glLineWidth(wire_width);
+       glDisable(GL_LIGHTING);
+
+       glPushMatrix();
+       glMultMatrixf(xform[0]);
+
+       mesh->draw_wire();
+
+       glPopMatrix();
+       glPopAttrib();
+}
+
+static Vec2 rev_pos(float u, float v, void *cls)
+{
+       Gear *gear = (Gear*)cls;
+
+       float y = ((v - 1.0 / 3.0) / (1.0 / 3.0) - 0.5);
+       if(y < -0.5) y = -0.5;
+       if(y > 0.5) y = 0.5;
+       y *= gear->thickness;
+
+       if(v < 0.001 || v > 0.999) {
+               return Vec2(0, -y);
+       }
+
+       float nt = (float)gear->nteeth * 4.0;
+
+       int part = (int)(u * nt) % 4;   /* 4 parts to a tooth /#\_ */
+       float t = fmod(u * nt * 4.0, 1.0);
+
+       float offs;
+       switch(part) {
+       case 0:
+               offs = t;
+               break;
+       case 1:
+               offs = 1.0f;
+               break;
+       case 2:
+               offs = 1.0f - t;
+               break;
+       case 3:
+               offs = 0.0f;
+       }
+
+       float inner_rad = gear->radius - gear->teeth_length;
+       return Vec2(inner_rad + offs * gear->teeth_length, -y);
+}
+
+bool Gear::gen_mesh()
+{
+       mesh = new Mesh;
+       gen_revol(mesh, nteeth * 4, 3, rev_pos, 0, this);
+       mesh->explode();
+       mesh->calc_face_normals();
+
+       float fix_tooth_up = get_angular_pitch() * 3.0 / 8.0;
+
+       Mat4 rot;
+       rot.rotation_x(M_PI / 2.0);
+       rot.rotate_z(fix_tooth_up);
+       mesh->apply_xform(rot, rot);
+
+       mesh->set_vis_vecsize(6.0);
+       return true;
+}
+
+void Gear::calc_matrix() const
+{
+       Vec3 up = Vec3(0, 1, 0);
+       if(1.0 - fabs(dot(up, axis)) < 1e-4) {
+               up = Vec3(0, 0, -1);
+       }
+       Vec3 right = normalize(cross(up, axis));
+       up = cross(axis, right);
+
+       dir_xform = Mat4(right, up, axis);
+
+       xform = dir_xform;
+       xform.rotate_z(get_vis_rotation());
+       xform.translate(pos);
+
+       axel_xform = dir_xform;
+       axel_xform.translate(pos);
+}
diff --git a/src/machine/gear.h b/src/machine/gear.h
new file mode 100644 (file)
index 0000000..fac245c
--- /dev/null
@@ -0,0 +1,116 @@
+#ifndef GEAR_H_
+#define GEAR_H_
+
+#include <vector>
+#include <string>
+#include <gmath/gmath.h>
+#include "mesh.h"
+
+/* distance unit: millimeters
+ * angle unit: radians
+ */
+
+class Gear;
+struct GearPin;
+struct GearSlot;
+
+struct GearPin {
+       float radius;
+       float height;
+
+       Gear *parent;
+       /* position in polar coordinates on the parent gear */
+       float pos_dist, pos_angle;
+
+       GearSlot *conn_slot;    /* slot connection */
+};
+
+struct GearSlot {
+       float radius, length;
+
+       Gear *parent;
+       /* position in polar coordinates on the parent gear */
+       float pos_dist_min, pos_dist_max, pos_angle;
+
+       GearPin *conn_pin;              /* pin connection */
+};
+
+class Gear {
+private:
+       Mesh *mesh;
+
+       mutable Mat4 xform, dir_xform, axel_xform;
+       mutable bool xform_valid;
+       void calc_matrix() const;
+
+       float contour(float u);
+
+public:
+
+       /* TODO hide most of this shit, especially the stuff which invalidate
+        * the transformation matrices
+        */
+       std::string name;
+       Vec3 pos, axis; // implicitly defines a plane eqn.
+       float pdist;    // derived: distance of plane from origin
+
+       float init_angle;       // initial starting angle
+       float angle;    // current angle of the gear
+
+       int nteeth;             // number of teeth
+       float tooth_pitch;
+
+       float radius;   // total radius of the gear, including teeth
+       float teeth_length;     // how far teeth extend past the radius
+       float thickness;        // thickness of the gear along the Z axis
+
+       float bevel;    // bevel size
+
+       // visual surface properties
+       Vec3 color;
+       float roughness;
+       bool metallic;
+
+       Gear *supergear;
+       std::vector<Gear*> subgears;
+       std::vector<GearPin> pins;
+       std::vector<GearSlot> slots;
+
+       Gear();
+       ~Gear();
+
+       void attach(Gear *g);
+       bool detach(Gear *g);
+       Gear *get_super() const;
+
+       void set_angular_offset(float offs);
+       float get_angular_offset() const;
+
+       // sets the supplied number of teeth, and calculates the radius
+       // of the gear, to achieve the required tooth pitch
+       void set_teeth(int nt, float tooth_pitch = 0.0f);
+       void set_axis(const Vec3 &axis);
+       const Vec3 &get_axis() const;
+       void set_position(const Vec3 &pos);
+       const Vec3 &get_position() const;
+       Vec3 get_global_position() const;       // taking parent gear into account
+
+       Vec3 get_planar_position() const;       // 2D pos of gear on its plane
+
+       void set_angle(float angle);
+       float get_angle() const;
+
+       float get_vis_rotation() const;
+       const Mat4 &get_matrix() const;
+       const Mat4 &get_dir_matrix() const;
+
+       // returns the angle (in radians) from one tooth to the next
+       float get_angular_pitch() const;
+
+       void draw() const;
+       void draw_wire(float line_width = 1.0f) const;
+
+       bool gen_mesh();
+};
+
+#endif // GEAR_H_
diff --git a/src/machine/machine.cc b/src/machine/machine.cc
new file mode 100644 (file)
index 0000000..441826e
--- /dev/null
@@ -0,0 +1,269 @@
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <float.h>
+#include <assert.h>
+#include "opengl.h"
+#include "machine.h"
+
+static float delta_angle(float a, float b);
+
+Machine::Machine()
+{
+       meshing = 0;
+       meshing_valid = false;
+       visited = 0;
+}
+
+Machine::~Machine()
+{
+       int ngears = (int)gears.size();
+       for(int i=0; i<ngears; i++) {
+               delete gears[i];
+       }
+
+       if(meshing) {
+               delete [] meshing[0];
+               delete [] meshing;
+       }
+       delete [] visited;
+}
+
+void Machine::add_gear(Gear *g)
+{
+       if(gearidx.find(g) != gearidx.end()) {
+               return; // already have this gear
+       }
+       gearidx[g] = gears.size();
+       gears.push_back(g);
+       meshing_valid = false;
+}
+
+void Machine::add_motor(int gearidx, float speed_hz)
+{
+       Motor m;
+       m.drive = gearidx;
+       m.speed = speed_hz;
+       motors.push_back(m);
+}
+
+int Machine::get_gear_index(Gear *g) const
+{
+       std::map<Gear*, int>::const_iterator it = gearidx.find(g);
+       if(it == gearidx.end()) {
+               return -1;
+       }
+       return it->second;
+}
+
+void Machine::invalidate_meshing()
+{
+       meshing_valid = false;
+}
+
+void Machine::calc_meshing()
+{
+       int ngears = (int)gears.size();
+
+       if(!meshing) {
+               meshing = new bool*[ngears];
+               meshing[0] = new bool[ngears * ngears];
+
+               for(int i=1; i<ngears; i++) {
+                       meshing[i] = meshing[i - 1] + ngears;
+               }
+       }
+
+       if(!visited) {
+               visited = new bool[ngears];
+       }
+
+       // we're going to need the planar position of each gear on its plane, so let's cache it
+       Vec3 *ppos = (Vec3*)alloca(ngears * sizeof *ppos);
+       for(int i=0; i<ngears; i++) {
+               ppos[i] = gears[i]->get_position();
+       }
+
+       for(int i=0; i<ngears; i++) {
+               for(int j=i; j<ngears; j++) {
+                       meshing[i][j] = meshing[j][i] = false;
+
+                       if(i == j || gears[i]->get_super() == gears[j] || gears[j]->get_super() == gears[i]) {
+                               // don't attempt meshing if it's the same gear, or they are attached to each other
+                               continue;
+                       }
+
+                       if(1.0 - fabs(dot(gears[i]->axis, gears[j]->axis)) < 1e-5) {
+                               // co-planar, just check Z range after inverse-transforming to the XY plane
+                               if(fabs(ppos[i].z - ppos[j].z) > (gears[i]->thickness + gears[j]->thickness) / 2.0) {
+                                       continue;
+                               }
+                               // Z interval match, check distance
+                               float dsq = length_sq(ppos[i].xy() - ppos[j].xy());
+
+                               float outer_rad_sum = gears[i]->radius + gears[j]->radius;
+                               float inner_rad_sum = outer_rad_sum - gears[i]->teeth_length - gears[j]->teeth_length;
+
+                               if(dsq <= outer_rad_sum * outer_rad_sum && dsq >= inner_rad_sum * inner_rad_sum) {
+                                       //printf("connecting co-planar gears %d - %d\n", i, j);
+                                       meshing[i][j] = meshing[j][i] = true;
+                               }
+
+                       } else {
+                               /* TODO: not co-planar
+                                * - calc line of intersection between the two planes
+                                * - find distance of each gear to that line
+                                * - profit...
+                                */
+                       }
+               }
+       }
+
+       // fix the initial angles so that teeth mesh as best as possible
+       // should work in one pass as long as the gear train is not impossible
+       for(int i=0; i<ngears; i++) {
+               /*float rnd = gears[i]->angle + gears[i]->get_angular_pitch() / 2.0;
+               float snap = rnd - fmod(rnd, gears[i]->get_angular_pitch());
+               gears[i]->set_angle(snap);*/
+               gears[i]->set_angular_offset(0);
+       }
+
+       for(int i=0; i<ngears; i++) {
+               for(int j=i; j<ngears; j++) {
+                       if(meshing[i][j]) {
+                               assert(i != j);
+
+                               Vec2 dir = normalize(ppos[j].xy() - ppos[i].xy());
+                               float rel_angle = atan2(dir.y, dir.x);
+
+                               float frac_i = fmod((gears[i]->init_angle + rel_angle) / gears[i]->get_angular_pitch() + 100.0, 1.0);
+                               float frac_j = fmod((gears[j]->init_angle - rel_angle) / gears[j]->get_angular_pitch() + 100.0, 1.0);
+                               assert(frac_i >= 0.0 && frac_j >= 0.0);
+                               float delta = frac_j - frac_i;
+
+                               float correction = 0.5 - delta;
+                               float prev_offs = gears[j]->get_angular_offset();
+                               gears[j]->set_angular_offset(prev_offs + correction * gears[j]->get_angular_pitch());
+                       }
+               }
+       }
+
+       /*
+       printf("meshing graph\n");
+       for(int i=0; i<ngears; i++) {
+               putchar(' ');
+               for(int j=0; j<ngears; j++) {
+                       printf("| %d ", meshing[i][j] ? 1 : 0);
+               }
+               printf("|\n");
+       }
+       */
+}
+
+void Machine::update_gear(int idx, float angle)
+{
+       Gear *gear = gears[idx];
+
+       if(visited[idx]) {
+               if(delta_angle(angle, gear->angle) > 0.25 / gear->nteeth) {
+                       fprintf(stderr, "warning: trying to transmit different values to gear %s (%d)\n",
+                                       gear->name.c_str(), idx);
+                       gear->angle = 0;
+               }
+               return;
+       }
+
+       gear->set_angle(angle);
+       visited[idx] = true;
+
+       // propagate to meshing gears (depth-first)
+       int ngears = (int)gears.size();
+       for(int i=0; i<ngears; i++) {
+               if(!meshing[idx][i]) continue;
+               assert(idx != i);
+
+               float ratio = -(float)gear->nteeth / (float)gears[i]->nteeth;
+               update_gear(i, angle * ratio);
+       }
+
+       // propagate to rigidly attached gears
+       if(gear->supergear) {
+               int supidx = gearidx[gear->supergear];
+               update_gear(supidx, angle);
+       }
+
+       int nsub = (int)gear->subgears.size();
+       for(int i=0; i<nsub; i++) {
+               int subidx = gearidx[gear->subgears[i]];
+               update_gear(subidx, angle);
+       }
+}
+
+void Machine::update(float dt)
+{
+       int ngears = (int)gears.size();
+
+       if(!meshing_valid) {
+               calc_meshing();
+               meshing_valid = true;
+       }
+
+       memset(visited, 0, ngears * sizeof *visited);
+       for(size_t i=0; i<motors.size(); i++) {
+               int gidx = motors[i].drive;
+               if(gidx < 0) continue;
+
+               update_gear(gidx, gears[gidx]->angle + dt * motors[i].speed);
+       }
+}
+
+void Machine::draw() const
+{
+       for(size_t i=0; i<gears.size(); i++) {
+               gears[i]->draw();
+       }
+
+       float dcol[] = {0.4, 0.4, 0.4, 1.0};
+       float scol[] = {0, 0, 0, 0};
+       glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, dcol);
+       glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, scol);
+
+       glBegin(GL_QUADS);
+       glNormal3f(0, 1, 0);
+       glVertex3f(-300, -100, 300);
+       glVertex3f(300, -100, 300);
+       glVertex3f(300, -100, -300);
+       glVertex3f(-300, -100, -300);
+       glEnd();
+}
+
+Gear *Machine::intersect_gear(const Ray &ray, HitPoint *hitp) const
+{
+       Gear *res = 0;
+       HitPoint nearest;
+       nearest.dist = FLT_MAX;
+
+       for(size_t i=0; i<gears.size(); i++) {
+               Vec3 pos = gears[i]->get_global_position();
+               float rad = gears[i]->radius;
+
+               Plane plane = Plane(pos, gears[i]->axis);
+
+               HitPoint hit;
+               if(plane.intersect(ray, &hit) && hit.dist < nearest.dist &&
+                               length_sq(hit.pos - pos) <= rad * rad) {
+                       nearest = hit;
+                       res = gears[i];
+               }
+       }
+
+       if(hitp) *hitp = nearest;
+       return res;
+}
+
+static float delta_angle(float a, float b)
+{
+       float api = fmod(a + M_PI, 2.0 * M_PI);
+       float bpi = fmod(b + M_PI, 2.0 * M_PI);
+       return std::min(fabs(a - b), fabs(api - bpi));
+}
diff --git a/src/machine/machine.h b/src/machine/machine.h
new file mode 100644 (file)
index 0000000..097157d
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef MACHINE_H_
+#define MACHINE_H_
+
+#include <vector>
+#include <map>
+#include "gear.h"
+
+struct Motor {
+       float speed;    /* signed to denote direction, in Hz */
+       int drive;              /* which gear it drives */
+};
+
+class Machine {
+private:
+       std::vector<Gear*> gears;
+       std::map<Gear*, int> gearidx;
+       bool **meshing;
+       bool meshing_valid;
+       bool *visited;  /* used for update_gear */
+
+       std::vector<Motor> motors;
+
+       void update_gear(int idx, float angle);
+
+public:
+       Machine();
+       ~Machine();
+
+       void add_gear(Gear *g); /* takes ownership */
+       void add_motor(int gearidx, float speed_hz);
+
+       int get_gear_index(Gear *g) const;
+
+       void invalidate_meshing();
+       void calc_meshing();
+
+       void update(float dt);
+       void draw() const;
+
+       Gear *intersect_gear(const Ray &ray, HitPoint *hitp = 0) const;
+};
+
+#endif // MACHINE_H_
diff --git a/src/machine/mparser.cc b/src/machine/mparser.cc
new file mode 100644 (file)
index 0000000..260b303
--- /dev/null
@@ -0,0 +1,819 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string>
+#include <map>
+#include <stack>
+#include "mparser.h"
+#include "machine.h"
+
+enum ValueType { VAL_NUM, VAL_VEC, VAL_STR };
+struct Value {
+       ValueType type;
+       std::string s;
+       float v[4];
+};
+
+struct ParserState;
+
+typedef bool (*Func)(ParserState*);
+
+struct FuncDesc {
+       Func func;
+       int arity;
+};
+
+struct ParserState {
+       FILE *fp;
+       const char *fname;
+       int nline, nerrors;
+       Machine *mcn;
+
+       int nextc;
+       int savedc;
+
+       std::stack<Value> val;
+       std::map<std::string, Value> var;
+       std::map<std::string, FuncDesc> func;
+
+       std::map<std::string, Gear*> gears;
+       std::vector<Gear*> cur_gear;
+       Motor *cur_motor;
+};
+
+#define MAX_ID_LEN     31
+
+static bool machine(ParserState *ps);
+static bool expect(ParserState *ps, char c);
+static bool statement(ParserState *ps);
+static bool expression(ParserState *ps);
+static bool term(ParserState *ps);
+static bool vector(ParserState *ps);
+static bool function(ParserState *ps, const char *name);
+
+static char *get_ident(ParserState *ps, char *buf, int bsz);
+static bool get_number(ParserState *ps, float *num);
+static char *get_string(ParserState *ps, char *buf, int bsz);
+
+static bool nextchar(ParserState *ps);
+//static void putback(ParserState *ps, char c);
+
+static void errmsg(ParserState *ps, const char *fmt, ...);
+static void expected(ParserState *ps, const char *str);
+
+static void set_var(ParserState *ps, const char *name, const Value &value);
+static bool have_var(ParserState *ps, const char *name);
+static const Value &get_var(ParserState *ps, const char *name);
+
+static void set_func(ParserState *ps, const char *name, Func func, int nargs = 0);
+static bool have_func(ParserState *ps, const char *name);
+static Func get_func(ParserState *ps, const char *name);
+static int get_func_arity(ParserState *ps, const char *name);
+
+static Gear *begin_gear(ParserState *ps);
+static bool end_gear(ParserState *ps);
+static Gear *this_gear(ParserState *ps);
+static Gear *find_gear(ParserState *ps, const char *name);
+static void update_gear_vars(ParserState *ps, Gear *gear);
+static bool set_gear_var(Gear *gear, const char *name, const Value &val);
+
+static void update_motor_vars(ParserState *ps, Motor *motor);
+static bool set_motor_var(ParserState *ps, Motor *motor, const char *name, const Value &val);
+
+// built-in functions
+static bool func_adjacent(ParserState *ps);
+static bool func_coaxial(ParserState *ps);
+
+
+bool parse_machine(Machine *mcn, const char *fname)
+{
+       FILE *fp = fopen(fname, "rb");
+       if(!fp) return false;
+
+       ParserState pstate;
+       pstate.fname = fname;
+       pstate.nline = 1;
+       pstate.nerrors = 0;
+       pstate.fp = fp;
+       pstate.mcn = mcn;
+       pstate.nextc = 0;
+       pstate.savedc = -1;
+       pstate.cur_motor = 0;
+
+       // init built-in function table
+       set_func(&pstate, "adjacent", func_adjacent, 2);
+       set_func(&pstate, "coaxial", func_coaxial, 2);
+
+       nextchar(&pstate);
+       bool res = machine(&pstate);
+       fclose(fp);
+       return res;
+}
+
+static bool machine(ParserState *ps)
+{
+       char tok[MAX_ID_LEN + 1];
+       if(strcmp(get_ident(ps, tok, sizeof tok), "machine") != 0) {
+               expected(ps, "machine");
+               return false;
+       }
+       expect(ps, '{');
+
+       while(ps->nextc != '}') {
+               if(!statement(ps)) {
+                       return false;
+               }
+       }
+       expect(ps, '}');
+       return true;
+}
+
+static bool expect(ParserState *ps, char c)
+{
+       if(c != ps->nextc) {
+               char buf[2] = {0, 0};
+               buf[0] = c;
+               expected(ps, buf);
+               return false;
+       }
+       nextchar(ps);
+       return true;
+}
+
+static bool statement(ParserState *ps)
+{
+       char id[MAX_ID_LEN + 1];
+       if(!get_ident(ps, id, sizeof id)) {
+               return false;
+       }
+
+       if(strcmp(id, "gear") == 0) {
+               if(!expect(ps, '{')) {
+                       return false;
+               }
+
+               Gear *gear = begin_gear(ps);
+               if(!gear) return false;
+
+               update_gear_vars(ps, gear);
+
+               while(ps->nextc != '}') {
+                       if(!statement(ps)) {
+                               return false;
+                       }
+               }
+               expect(ps, '}');
+
+               if(!end_gear(ps)) {
+                       return false;
+               }
+               return true;
+       }
+
+       if(strcmp(id, "motor") == 0) {
+               if(!expect(ps, '{')) {
+                       return false;
+               }
+               if(ps->cur_motor) {
+                       errmsg(ps, "nested motors not allowed");
+                       return false;
+               }
+
+               ps->cur_motor = new Motor;
+               update_motor_vars(ps, ps->cur_motor);
+
+               while(ps->nextc != '}') {
+                       if(!statement(ps)) {
+                               return false;
+                       }
+               }
+               expect(ps, '}');
+
+               ps->mcn->add_motor(ps->cur_motor->drive, ps->cur_motor->speed);
+               delete ps->cur_motor;
+               ps->cur_motor = 0;
+               return true;
+       }
+
+       if(ps->nextc == '=') {
+               expect(ps, '=');
+               if(!expression(ps)) {
+                       return false;
+               }
+               expect(ps, ';');
+
+               set_var(ps, id, ps->val.top());
+               ps->val.pop();
+               return true;
+       }
+
+       expected(ps, "expression");
+       return false;
+}
+
+static bool expression(ParserState *ps)
+{
+       return term(ps);        // TODO expand
+}
+
+static bool term(ParserState *ps)
+{
+       if(ps->nextc == '+') {
+               expect(ps, '+');
+               return expression(ps);
+       }
+       if(ps->nextc == '-') {
+               expect(ps, '-');
+               if(!expression(ps)) {
+                       return false;
+               }
+
+               Value &v = ps->val.top();
+               switch(v.type) {
+               case VAL_VEC:
+                       v.v[3] = -v.v[3];
+                       v.v[2] = -v.v[2];
+                       v.v[1] = -v.v[1];
+               case VAL_NUM:
+                       v.v[0] = -v.v[0];
+                       break;
+
+               default:
+                       expected(ps, "number or vector after an unary -");
+                       return false;
+               }
+               return true;
+       }
+       if(isalpha(ps->nextc)) {
+               char buf[MAX_ID_LEN];
+               if(!get_ident(ps, buf, sizeof buf)) {
+                       return false;
+               }
+
+               if(ps->nextc == '(') {
+                       if(!function(ps, buf)) {
+                               return false;
+                       }
+               } else if(have_var(ps, buf)) {
+                       ps->val.push(get_var(ps, buf));
+               } else {
+                       errmsg(ps, "unknown identifier: %s", buf);
+                       return false;
+               }
+               return true;
+       }
+       if(isdigit(ps->nextc)) {
+               float num;
+               if(!get_number(ps, &num)) {
+                       return false;
+               }
+
+               Value v;
+               v.type = VAL_NUM;
+               v.v[0] = num;
+               ps->val.push(v);
+               return true;
+       }
+       if(ps->nextc == '[') {
+               return vector(ps);
+       }
+       if(ps->nextc == '"') {
+               char buf[MAX_ID_LEN];
+               if(!get_string(ps, buf, sizeof buf)) {
+                       return false;
+               }
+
+               Value v;
+               v.type = VAL_STR;
+               v.s = buf;
+               ps->val.push(v);
+               return true;
+       }
+
+       expected(ps, "term");
+       return false;
+}
+
+static bool vector(ParserState *ps)
+{
+       int nelem;
+       if(!expect(ps, '[') ||
+                       !expression(ps) ||
+                       !expect(ps, ',') ||
+                       !expression(ps) ||
+                       !expect(ps, ',') ||
+                       !expression(ps)) {
+               return false;
+       }
+
+       if(ps->nextc == ']') {
+               expect(ps, ']');
+               nelem = 3;
+       } else {
+               if(!expect(ps, ',') || !expression(ps) || !expect(ps, ']')) {
+                       return false;
+               }
+               nelem = 4;
+       }
+
+       Value vec;
+       vec.type = VAL_VEC;
+       vec.v[3] = 1.0f;
+
+       for(int i=0; i<nelem; i++) {
+               const Value &tmp = ps->val.top();
+               if(tmp.type != VAL_NUM) {
+                       expected(ps, "numbers as vector elements");
+                       return false;
+               }
+               vec.v[nelem - i - 1] = tmp.v[0];
+               ps->val.pop();
+       }
+
+       ps->val.push(vec);
+       return true;
+}
+
+static bool function(ParserState *ps, const char *name)
+{
+       if(!expect(ps, '(')) {
+               return false;
+       }
+
+       if(!have_func(ps, name)) {
+               errmsg(ps, "unknown function: %s", name);
+               return false;
+       }
+
+       int nargs = 0;
+       while(ps->nextc != ')') {
+               if(nargs && !expect(ps, ',')) {
+                       return false;
+               }
+               if(!expression(ps)) {
+                       return false;
+               }
+               ++nargs;
+       }
+       expect(ps, ')');
+
+       int arity = get_func_arity(ps, name);
+       if(arity != nargs) {
+               errmsg(ps, "function %s expects %d arguments", name, arity);
+               return false;
+       }
+       Func func = get_func(ps, name);
+       assert(func);
+
+       if(!func(ps)) {
+               errmsg(ps, "function %s failed", name);
+               return false;
+       }
+       return true;
+}
+
+static char *get_ident(ParserState *ps, char *buf, int bsz)
+{
+       char *ptr = buf;
+       if(!isalpha(ps->nextc)) {
+               expected(ps, "identifier");
+               return 0;
+       }
+       while(isalnum(ps->nextc) || ps->nextc == '_') {
+               if(bsz > 1) {
+                       *ptr++ = ps->nextc;
+                       --bsz;
+               }
+               nextchar(ps);
+       }
+       *ptr = 0;
+       printf("get_ident -> \"%s\"\n", buf);
+       return buf;
+}
+
+static bool get_number(ParserState *ps, float *num)
+{
+       char buf[256], *ptr = buf, *end = buf + sizeof buf;
+       bool found_point = false;
+
+       if(!isdigit(ps->nextc)) {
+               expected(ps, "number");
+               return false;
+       }
+
+       while(isdigit(ps->nextc) || (ps->nextc == '.' && !found_point)) {
+               if(ptr < end) {
+                       *ptr++ = ps->nextc;
+               }
+               if(ps->nextc == '.') {
+                       found_point = true;
+               }
+               nextchar(ps);
+       }
+       *ptr = 0;
+
+       *num = atof(buf);
+       printf("get_number -> %f\n", *num);
+       return true;
+}
+
+static char *get_string(ParserState *ps, char *buf, int bsz)
+{
+       char *ptr = buf;
+       if(!expect(ps, '"')) {
+               return 0;
+       }
+
+       while(ps->nextc != -1 && ps->nextc != '"') {
+               if(bsz > 1) {
+                       *ptr++ = ps->nextc;
+                       --bsz;
+               }
+               if((ps->nextc = fgetc(ps->fp)) == '\n') {
+                       ++ps->nline;
+               }
+       }
+       *ptr = 0;
+
+       if(ps->nextc == -1) {
+               return 0;
+       }
+       nextchar(ps);
+
+       printf("get_string -> \"%s\"\n", buf);
+       return buf;
+}
+
+static bool skip_line(ParserState *ps)
+{
+       int c;
+       while((c = fgetc(ps->fp)) != -1 && c != '\n');
+       if(c != -1) {
+               ps->nextc = fgetc(ps->fp);
+               return true;
+       }
+       return false;
+}
+
+static bool nextchar(ParserState *ps)
+{
+       if(ps->savedc != -1) {
+               ps->nextc = ps->savedc;
+               ps->savedc = -1;
+               return true;
+       }
+
+       while((ps->nextc = fgetc(ps->fp)) != -1) {
+               if(ps->nextc == '#') {
+                       if(!skip_line(ps)) {
+                               return false;
+                       }
+                       ++ps->nline;
+               }
+               if(!isspace(ps->nextc)) {
+                       break;
+               }
+               if(ps->nextc == '\n') {
+                       ++ps->nline;
+               }
+       }
+
+       /*if(ps->nextc != -1) {
+               printf("DBG: nextchar -> %c\n", ps->nextc);
+       } else {
+               printf("DBG: nextchar -> EOF\n");
+       }*/
+       return ps->nextc != -1;
+}
+
+/*static void putback(ParserState *ps, char c)
+{
+       ps->savedc = ps->nextc;
+       ps->nextc = c;
+}*/
+
+static void errmsg(ParserState *ps, const char *fmt, ...)
+{
+       fprintf(stderr, "%s line %d error: ", ps->fname, ps->nline);
+
+       va_list ap;
+       va_start(ap, fmt);
+       vfprintf(stderr, fmt, ap);
+       va_end(ap);
+
+       fputc('\n', stderr);
+}
+
+static void expected(ParserState *ps, const char *str)
+{
+       errmsg(ps, "expected: %s", str);
+       ++ps->nerrors;
+}
+
+static void set_var(ParserState *ps, const char *name, const Value &value)
+{
+       ps->var[name] = value;
+
+       Gear *gear = this_gear(ps);
+       if(gear) {
+               set_gear_var(gear, name, value);
+       }
+
+       if(ps->cur_motor) {
+               set_motor_var(ps, ps->cur_motor, name, value);
+       }
+}
+
+static bool have_var(ParserState *ps, const char *name)
+{
+       return ps->var.find(name) != ps->var.end();
+}
+
+static const Value &get_var(ParserState *ps, const char *name)
+{
+       return ps->var[name];
+}
+
+static void set_func(ParserState *ps, const char *name, Func func, int nargs)
+{
+       FuncDesc fdesc;
+       fdesc.func = func;
+       fdesc.arity = nargs;
+       ps->func[name] = fdesc;
+}
+
+static bool have_func(ParserState *ps, const char *name)
+{
+       return ps->func.find(name) != ps->func.end();
+}
+
+static Func get_func(ParserState *ps, const char *name)
+{
+       return ps->func[name].func;
+}
+
+static int get_func_arity(ParserState *ps, const char *name)
+{
+       return ps->func[name].arity;
+}
+
+static void print_value(const Value &val, FILE *fp)
+{
+       switch(val.type) {
+       case VAL_NUM:
+               fprintf(fp, "%f", val.v[0]);
+               break;
+
+       case VAL_VEC:
+               fprintf(fp, "[%f %f %f %f]", val.v[0], val.v[1], val.v[2], val.v[3]);
+               break;
+
+       case VAL_STR:
+               fprintf(fp, "\"%s\"", val.s.c_str());
+               break;
+
+       default:
+               fprintf(fp, "<invalid>");
+       }
+}
+
+// Gear-specific stuff
+
+static Gear *begin_gear(ParserState *ps)
+{
+       Gear *res = new Gear;
+       ps->cur_gear.push_back(res);
+       return res;
+}
+
+static bool end_gear(ParserState *ps)
+{
+       if(ps->cur_gear.empty()) {
+               errmsg(ps, "parser error: unbalanced end_gear");
+               return false;
+       }
+       Gear *gear = ps->cur_gear.back();
+       ps->cur_gear.pop_back();
+
+       printf("DBG: end_gear: %s\n", gear->name.c_str());
+
+       if(gear->name.empty() || ps->gears[gear->name]) {
+               char buf[32];
+               sprintf(buf, "gear%04d", (int)ps->gears.size());
+               gear->name = buf;
+       }
+       printf("DBG: adding gear: %s\n", gear->name.c_str());
+       ps->gears[gear->name] = gear;
+       ps->mcn->add_gear(gear);
+
+       if(!ps->cur_gear.empty()) {
+               ps->cur_gear.back()->attach(gear);
+       }
+       return true;
+}
+
+static Gear *this_gear(ParserState *ps)
+{
+       return ps->cur_gear.empty() ? 0 : ps->cur_gear.back();
+}
+
+static Gear *find_gear(ParserState *ps, const char *name)
+{
+       // search progressively wider lexical scopes
+       std::vector<Gear*>::const_reverse_iterator it = ps->cur_gear.rbegin();
+       while(it != ps->cur_gear.rend()) {
+               Gear *g = *it++;
+               if(g->name == std::string(name)) {
+                       return g;
+               }
+       }
+
+       return ps->gears[name];
+}
+
+static void update_gear_vars(ParserState *ps, Gear *gear)
+{
+       std::map<std::string, Value>::const_iterator it = ps->var.begin();
+       while(it != ps->var.end()) {
+               set_gear_var(gear, it->first.c_str(), it->second);
+               ++it;
+       }
+}
+
+#define ASSERT_TYPE(v, t) \
+       do { \
+               if((v).type != (t)) { \
+                       fprintf(stderr, "type mismatch while trying to set %s to value: ", name); \
+                       print_value(val, stderr); \
+                       fputc('\n', stderr); \
+                       return false; \
+               } \
+       } while(0)
+
+#define VVEC3(val) Vec3((val).v[0], (val).v[1], (val).v[2])
+#define VVEC4(val) Vec4((val).v[0], (val).v[1], (val).v[2], (val).v[3])
+
+static bool set_gear_var(Gear *gear, const char *name, const Value &val)
+{
+       if(strcmp(name, "name") == 0) {
+               ASSERT_TYPE(val, VAL_STR);
+               gear->name = val.s;
+
+       } else if(strcmp(name, "position") == 0) {
+               ASSERT_TYPE(val, VAL_VEC);
+               gear->pos = VVEC3(val);
+
+       } else if(strcmp(name, "plane") == 0) {
+               ASSERT_TYPE(val, VAL_VEC);
+               gear->axis = VVEC3(val);
+               gear->pdist = val.v[3];
+               printf("setting plane eqn: %f %f %f  %f\n", val.v[0], val.v[1], val.v[2], val.v[3]);
+
+       } else if(strcmp(name, "thickness") == 0) {
+               ASSERT_TYPE(val, VAL_NUM);
+               gear->thickness = val.v[0];
+
+       } else if(strcmp(name, "teeth") == 0) {
+               ASSERT_TYPE(val, VAL_NUM);
+               gear->set_teeth(val.v[0]);      // set teeth and recalc radius
+
+       } else if(strcmp(name, "teeth_length") == 0) {
+               ASSERT_TYPE(val, VAL_NUM);
+               gear->teeth_length = val.v[0];
+
+       } else if(strcmp(name, "pitch") == 0) {
+               ASSERT_TYPE(val, VAL_NUM);
+               gear->set_teeth(gear->nteeth, val.v[0]); // set pitch and recalc radius
+
+       } else if(strcmp(name, "color") == 0) {
+               ASSERT_TYPE(val, VAL_VEC);
+               gear->color = VVEC3(val);
+
+       } else if(strcmp(name, "roughness") == 0) {
+               ASSERT_TYPE(val, VAL_NUM);
+               gear->roughness = val.v[0];
+
+       } else {
+               return false;
+       }
+
+       return true;
+}
+
+
+// motor stuff
+
+static void update_motor_vars(ParserState *ps, Motor *motor)
+{
+       std::map<std::string, Value>::const_iterator it = ps->var.begin();
+       while(it != ps->var.end()) {
+               set_motor_var(ps, motor, it->first.c_str(), it->second);
+               ++it;
+       }
+}
+
+static bool set_motor_var(ParserState *ps, Motor *motor, const char *name, const Value &val)
+{
+       if(strcmp(name, "drive") == 0) {
+               ASSERT_TYPE(val, VAL_STR);
+               Gear *gear = ps->gears[val.s];
+               if(!gear) {
+                       errmsg(ps, "undefined gear: %s", val.s.c_str());
+                       return false;
+               }
+               int idx = ps->mcn->get_gear_index(gear);
+               assert(idx >= 0);
+               motor->drive = idx;
+
+       } else if(strcmp(name, "speed") == 0) {
+               ASSERT_TYPE(val, VAL_NUM);
+               motor->speed = val.v[0];
+
+       } else {
+               return false;
+       }
+       return true;
+}
+
+
+// built-in functions
+static bool func_adjacent(ParserState *ps)
+{
+       Value val_angle = ps->val.top(); ps->val.pop();
+       Value val_gear_name = ps->val.top(); ps->val.pop();
+
+       if(val_gear_name.type != VAL_STR) {
+               expected(ps, "gear name (string) as 1st arg to adjacent");
+               return false;
+       }
+       if(val_angle.type != VAL_NUM) {
+               expected(ps, "angle as 2nd arg to adjacent");
+               return false;
+       }
+
+       Gear *gthis = this_gear(ps);
+       if(!gthis) {
+               errmsg(ps, "adjacent: called outside of a gear block");
+               return false;
+       }
+
+       Gear *gother = ps->gears[val_gear_name.s];
+       if(!gother) {
+               errmsg(ps, "adjacent: gear \"%s\" not found", val_gear_name.s.c_str());
+               return false;
+       }
+
+       Mat4 xform;
+       xform.rotation(deg_to_rad(val_angle.v[0]), gother->axis);
+       Vec3 dir = xform * Vec3(0, 1, 0);
+
+       float sum_radii = gthis->radius + gother->radius;
+       float avg_teeth_len = (gthis->teeth_length + gother->teeth_length) * 0.5;
+       Vec3 pos = gother->pos + dir * (sum_radii - avg_teeth_len * 0.75);
+
+       Value res;
+       res.type = VAL_VEC;
+       res.v[0] = pos.x;
+       res.v[1] = pos.y;
+       res.v[2] = pos.z;
+       ps->val.push(res);
+       return true;
+}
+
+static bool func_coaxial(ParserState *ps)
+{
+       Value val_dist = ps->val.top(); ps->val.pop();
+       Value val_gear_name = ps->val.top(); ps->val.pop();
+
+       if(val_gear_name.type != VAL_STR) {
+               expected(ps, "gear name (string) as 1st arg to func_coaxial");
+               return false;
+       }
+       if(val_dist.type != VAL_NUM) {
+               expected(ps, "stacking distance as 2nd arg to func_coaxial");
+               return false;
+       }
+
+       Gear *gthis = this_gear(ps);
+       if(!gthis) {
+               errmsg(ps, "coaxial: called outside of a gear block");
+               return false;
+       }
+
+       Gear *gother = find_gear(ps, val_gear_name.s.c_str());
+       if(!gother) {
+               errmsg(ps, "coaxial: gear \"%s\" not found", val_gear_name.s.c_str());
+               return false;
+       }
+
+       float avg_thickness = (gthis->thickness + gother->thickness) * 0.5;
+       Vec3 pos = gother->pos + gother->axis * val_dist.v[0] * avg_thickness;
+
+       Value res;
+       res.type = VAL_VEC;
+       res.v[0] = pos.x;
+       res.v[1] = pos.y;
+       res.v[2] = pos.z;
+       ps->val.push(res);
+       return true;
+}
diff --git a/src/machine/mparser.h b/src/machine/mparser.h
new file mode 100644 (file)
index 0000000..eb64f1b
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef MPARSER_H_
+#define MPARSER_H_
+
+#include <stdio.h>
+#include "machine.h"
+
+bool parse_machine(Machine *mcn, const char *fname);
+
+#endif /* MPARSER_H_ */
index 6a296b9..4ef90c0 100644 (file)
@@ -57,7 +57,7 @@ int main(int argc, char **argv)
        glutInitWindowSize(1024, 768);
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE | GLUT_MULTISAMPLE);
-       glutCreateWindow("Antikythera");
+       glutCreateWindow("demo");
 
        glutDisplayFunc(display);
        glutIdleFunc(idle);
@@ -94,41 +94,6 @@ static bool init()
                return false;
        }
 
-       /*
-       const float pitch = 10.0f;
-
-       Gear *gear1 = new Gear;
-       gear1->pos = Vec3(-50, 0, 0);
-       gear1->set_teeth(16, pitch);
-       gear1->gen_mesh();
-       gear1->color = Vec3(0.35, 0.6, 0.8);
-       machine->add_gear(gear1);
-
-       Gear *gear2 = new Gear;
-       gear2->set_teeth(32, pitch);
-       gear2->pos = gear1->pos + Vec3(gear1->radius + gear2->radius - gear1->teeth_length * 0.75, 0, 0);
-       gear2->thickness = 5;
-       gear2->gen_mesh();
-       machine->add_gear(gear2);
-
-       Gear *gear3 = new Gear;
-       gear3->set_teeth(8, pitch);
-       gear3->pos = gear2->pos + Vec3(0, gear2->radius + gear3->radius - gear2->teeth_length * 0.75, 0);
-       gear3->gen_mesh();
-       machine->add_gear(gear3);
-       gear3->color = Vec3(0.5, 0.8, 0.6);
-
-       Gear *subgear = new Gear;
-       subgear->set_teeth(10, pitch);
-       subgear->pos = Vec3(0, 0, (gear2->thickness + subgear->thickness) / 2 + 1);
-       subgear->gen_mesh();
-       gear2->attach(subgear);
-       machine->add_gear(subgear);
-       subgear->color = Vec3(0.8, 0.7, 0.5);
-       */
-
-       //machine->add_motor(0, 1.0);
-
        // shadows
        init_shadow(2048);
 
diff --git a/src/mparser.cc b/src/mparser.cc
deleted file mode 100644 (file)
index 4079533..0000000
+++ /dev/null
@@ -1,819 +0,0 @@
-#include <stdio.h>
-#include <stdarg.h>
-#include <assert.h>
-#include <string>
-#include <map>
-#include <stack>
-#include "mparser.h"
-#include "machine.h"
-
-enum ValueType { VAL_NUM, VAL_VEC, VAL_STR };
-struct Value {
-       ValueType type;
-       std::string s;
-       float v[4];
-};
-
-struct ParserState;
-
-typedef bool (*Func)(ParserState*);
-
-struct FuncDesc {
-       Func func;
-       int arity;
-};
-
-struct ParserState {
-       FILE *fp;
-       const char *fname;
-       int nline, nerrors;
-       Machine *mcn;
-
-       int nextc;
-       int savedc;
-
-       std::stack<Value> val;
-       std::map<std::string, Value> var;
-       std::map<std::string, FuncDesc> func;
-
-       std::map<std::string, Gear*> gears;
-       std::vector<Gear*> cur_gear;
-       Motor *cur_motor;
-};
-
-#define MAX_ID_LEN     31
-
-static bool machine(ParserState *ps);
-static bool expect(ParserState *ps, char c);
-static bool statement(ParserState *ps);
-static bool expression(ParserState *ps);
-static bool term(ParserState *ps);
-static bool vector(ParserState *ps);
-static bool function(ParserState *ps, const char *name);
-
-static char *get_ident(ParserState *ps, char *buf, int bsz);
-static bool get_number(ParserState *ps, float *num);
-static char *get_string(ParserState *ps, char *buf, int bsz);
-
-static bool nextchar(ParserState *ps);
-static void putback(ParserState *ps, char c);
-
-static void errmsg(ParserState *ps, const char *fmt, ...);
-static void expected(ParserState *ps, const char *str);
-
-static void set_var(ParserState *ps, const char *name, const Value &value);
-static bool have_var(ParserState *ps, const char *name);
-static const Value &get_var(ParserState *ps, const char *name);
-
-static void set_func(ParserState *ps, const char *name, Func func, int nargs = 0);
-static bool have_func(ParserState *ps, const char *name);
-static Func get_func(ParserState *ps, const char *name);
-static int get_func_arity(ParserState *ps, const char *name);
-
-static Gear *begin_gear(ParserState *ps);
-static bool end_gear(ParserState *ps);
-static Gear *this_gear(ParserState *ps);
-static Gear *find_gear(ParserState *ps, const char *name);
-static void update_gear_vars(ParserState *ps, Gear *gear);
-static bool set_gear_var(Gear *gear, const char *name, const Value &val);
-
-static void update_motor_vars(ParserState *ps, Motor *motor);
-static bool set_motor_var(ParserState *ps, Motor *motor, const char *name, const Value &val);
-
-// built-in functions
-static bool func_adjacent(ParserState *ps);
-static bool func_coaxial(ParserState *ps);
-
-
-bool parse_machine(Machine *mcn, const char *fname)
-{
-       FILE *fp = fopen(fname, "rb");
-       if(!fp) return false;
-
-       ParserState pstate;
-       pstate.fname = fname;
-       pstate.nline = 1;
-       pstate.nerrors = 0;
-       pstate.fp = fp;
-       pstate.mcn = mcn;
-       pstate.nextc = 0;
-       pstate.savedc = -1;
-       pstate.cur_motor = 0;
-
-       // init built-in function table
-       set_func(&pstate, "adjacent", func_adjacent, 2);
-       set_func(&pstate, "coaxial", func_coaxial, 2);
-
-       nextchar(&pstate);
-       bool res = machine(&pstate);
-       fclose(fp);
-       return res;
-}
-
-static bool machine(ParserState *ps)
-{
-       char tok[MAX_ID_LEN + 1];
-       if(strcmp(get_ident(ps, tok, sizeof tok), "machine") != 0) {
-               expected(ps, "machine");
-               return false;
-       }
-       expect(ps, '{');
-
-       while(ps->nextc != '}') {
-               if(!statement(ps)) {
-                       return false;
-               }
-       }
-       expect(ps, '}');
-       return true;
-}
-
-static bool expect(ParserState *ps, char c)
-{
-       if(c != ps->nextc) {
-               char buf[2] = {0, 0};
-               buf[0] = c;
-               expected(ps, buf);
-               return false;
-       }
-       nextchar(ps);
-       return true;
-}
-
-static bool statement(ParserState *ps)
-{
-       char id[MAX_ID_LEN + 1];
-       if(!get_ident(ps, id, sizeof id)) {
-               return false;
-       }
-
-       if(strcmp(id, "gear") == 0) {
-               if(!expect(ps, '{')) {
-                       return false;
-               }
-
-               Gear *gear = begin_gear(ps);
-               if(!gear) return false;
-
-               update_gear_vars(ps, gear);
-
-               while(ps->nextc != '}') {
-                       if(!statement(ps)) {
-                               return false;
-                       }
-               }
-               expect(ps, '}');
-
-               if(!end_gear(ps)) {
-                       return false;
-               }
-               return true;
-       }
-
-       if(strcmp(id, "motor") == 0) {
-               if(!expect(ps, '{')) {
-                       return false;
-               }
-               if(ps->cur_motor) {
-                       errmsg(ps, "nested motors not allowed");
-                       return false;
-               }
-
-               ps->cur_motor = new Motor;
-               update_motor_vars(ps, ps->cur_motor);
-
-               while(ps->nextc != '}') {
-                       if(!statement(ps)) {
-                               return false;
-                       }
-               }
-               expect(ps, '}');
-
-               ps->mcn->add_motor(ps->cur_motor->drive, ps->cur_motor->speed);
-               delete ps->cur_motor;
-               ps->cur_motor = 0;
-               return true;
-       }
-
-       if(ps->nextc == '=') {
-               expect(ps, '=');
-               if(!expression(ps)) {
-                       return false;
-               }
-               expect(ps, ';');
-
-               set_var(ps, id, ps->val.top());
-               ps->val.pop();
-               return true;
-       }
-
-       expected(ps, "expression");
-       return false;
-}
-
-static bool expression(ParserState *ps)
-{
-       return term(ps);        // TODO expand
-}
-
-static bool term(ParserState *ps)
-{
-       if(ps->nextc == '+') {
-               expect(ps, '+');
-               return expression(ps);
-       }
-       if(ps->nextc == '-') {
-               expect(ps, '-');
-               if(!expression(ps)) {
-                       return false;
-               }
-
-               Value &v = ps->val.top();
-               switch(v.type) {
-               case VAL_VEC:
-                       v.v[3] = -v.v[3];
-                       v.v[2] = -v.v[2];
-                       v.v[1] = -v.v[1];
-               case VAL_NUM:
-                       v.v[0] = -v.v[0];
-                       break;
-
-               default:
-                       expected(ps, "number or vector after an unary -");
-                       return false;
-               }
-               return true;
-       }
-       if(isalpha(ps->nextc)) {
-               char buf[MAX_ID_LEN];
-               if(!get_ident(ps, buf, sizeof buf)) {
-                       return false;
-               }
-
-               if(ps->nextc == '(') {
-                       if(!function(ps, buf)) {
-                               return false;
-                       }
-               } else if(have_var(ps, buf)) {
-                       ps->val.push(get_var(ps, buf));
-               } else {
-                       errmsg(ps, "unknown identifier: %s", buf);
-                       return false;
-               }
-               return true;
-       }
-       if(isdigit(ps->nextc)) {
-               float num;
-               if(!get_number(ps, &num)) {
-                       return false;
-               }
-
-               Value v;
-               v.type = VAL_NUM;
-               v.v[0] = num;
-               ps->val.push(v);
-               return true;
-       }
-       if(ps->nextc == '[') {
-               return vector(ps);
-       }
-       if(ps->nextc == '"') {
-               char buf[MAX_ID_LEN];
-               if(!get_string(ps, buf, sizeof buf)) {
-                       return false;
-               }
-
-               Value v;
-               v.type = VAL_STR;
-               v.s = buf;
-               ps->val.push(v);
-               return true;
-       }
-
-       expected(ps, "term");
-       return false;
-}
-
-static bool vector(ParserState *ps)
-{
-       int nelem;
-       if(!expect(ps, '[') ||
-                       !expression(ps) ||
-                       !expect(ps, ',') ||
-                       !expression(ps) ||
-                       !expect(ps, ',') ||
-                       !expression(ps)) {
-               return false;
-       }
-
-       if(ps->nextc == ']') {
-               expect(ps, ']');
-               nelem = 3;
-       } else {
-               if(!expect(ps, ',') || !expression(ps) || !expect(ps, ']')) {
-                       return false;
-               }
-               nelem = 4;
-       }
-
-       Value vec;
-       vec.type = VAL_VEC;
-       vec.v[3] = 1.0f;
-
-       for(int i=0; i<nelem; i++) {
-               const Value &tmp = ps->val.top();
-               if(tmp.type != VAL_NUM) {
-                       expected(ps, "numbers as vector elements");
-                       return false;
-               }
-               vec.v[nelem - i - 1] = tmp.v[0];
-               ps->val.pop();
-       }
-
-       ps->val.push(vec);
-       return true;
-}
-
-static bool function(ParserState *ps, const char *name)
-{
-       if(!expect(ps, '(')) {
-               return false;
-       }
-
-       if(!have_func(ps, name)) {
-               errmsg(ps, "unknown function: %s", name);
-               return false;
-       }
-
-       int nargs = 0;
-       while(ps->nextc != ')') {
-               if(nargs && !expect(ps, ',')) {
-                       return false;
-               }
-               if(!expression(ps)) {
-                       return false;
-               }
-               ++nargs;
-       }
-       expect(ps, ')');
-
-       int arity = get_func_arity(ps, name);
-       if(arity != nargs) {
-               errmsg(ps, "function %s expects %d arguments", name, arity);
-               return false;
-       }
-       Func func = get_func(ps, name);
-       assert(func);
-
-       if(!func(ps)) {
-               errmsg(ps, "function %s failed", name);
-               return false;
-       }
-       return true;
-}
-
-static char *get_ident(ParserState *ps, char *buf, int bsz)
-{
-       char *ptr = buf;
-       if(!isalpha(ps->nextc)) {
-               expected(ps, "identifier");
-               return 0;
-       }
-       while(isalnum(ps->nextc) || ps->nextc == '_') {
-               if(bsz > 1) {
-                       *ptr++ = ps->nextc;
-                       --bsz;
-               }
-               nextchar(ps);
-       }
-       *ptr = 0;
-       printf("get_ident -> \"%s\"\n", buf);
-       return buf;
-}
-
-static bool get_number(ParserState *ps, float *num)
-{
-       char buf[256], *ptr = buf, *end = buf + sizeof buf;
-       bool found_point = false;
-
-       if(!isdigit(ps->nextc)) {
-               expected(ps, "number");
-               return false;
-       }
-
-       while(isdigit(ps->nextc) || (ps->nextc == '.' && !found_point)) {
-               if(ptr < end) {
-                       *ptr++ = ps->nextc;
-               }
-               if(ps->nextc == '.') {
-                       found_point = true;
-               }
-               nextchar(ps);
-       }
-       *ptr = 0;
-
-       *num = atof(buf);
-       printf("get_number -> %f\n", *num);
-       return true;
-}
-
-static char *get_string(ParserState *ps, char *buf, int bsz)
-{
-       char *ptr = buf;
-       if(!expect(ps, '"')) {
-               return 0;
-       }
-
-       while(ps->nextc != -1 && ps->nextc != '"') {
-               if(bsz > 1) {
-                       *ptr++ = ps->nextc;
-                       --bsz;
-               }
-               if((ps->nextc = fgetc(ps->fp)) == '\n') {
-                       ++ps->nline;
-               }
-       }
-       *ptr = 0;
-
-       if(ps->nextc == -1) {
-               return 0;
-       }
-       nextchar(ps);
-
-       printf("get_string -> \"%s\"\n", buf);
-       return buf;
-}
-
-static bool skip_line(ParserState *ps)
-{
-       int c;
-       while((c = fgetc(ps->fp)) != -1 && c != '\n');
-       if(c != -1) {
-               ps->nextc = fgetc(ps->fp);
-               return true;
-       }
-       return false;
-}
-
-static bool nextchar(ParserState *ps)
-{
-       if(ps->savedc != -1) {
-               ps->nextc = ps->savedc;
-               ps->savedc = -1;
-               return true;
-       }
-
-       while((ps->nextc = fgetc(ps->fp)) != -1) {
-               if(ps->nextc == '#') {
-                       if(!skip_line(ps)) {
-                               return false;
-                       }
-                       ++ps->nline;
-               }
-               if(!isspace(ps->nextc)) {
-                       break;
-               }
-               if(ps->nextc == '\n') {
-                       ++ps->nline;
-               }
-       }
-
-       /*if(ps->nextc != -1) {
-               printf("DBG: nextchar -> %c\n", ps->nextc);
-       } else {
-               printf("DBG: nextchar -> EOF\n");
-       }*/
-       return ps->nextc != -1;
-}
-
-static void putback(ParserState *ps, char c)
-{
-       ps->savedc = ps->nextc;
-       ps->nextc = c;
-}
-
-static void errmsg(ParserState *ps, const char *fmt, ...)
-{
-       fprintf(stderr, "%s line %d error: ", ps->fname, ps->nline);
-
-       va_list ap;
-       va_start(ap, fmt);
-       vfprintf(stderr, fmt, ap);
-       va_end(ap);
-
-       fputc('\n', stderr);
-}
-
-static void expected(ParserState *ps, const char *str)
-{
-       errmsg(ps, "expected: %s", str);
-       ++ps->nerrors;
-}
-
-static void set_var(ParserState *ps, const char *name, const Value &value)
-{
-       ps->var[name] = value;
-
-       Gear *gear = this_gear(ps);
-       if(gear) {
-               set_gear_var(gear, name, value);
-       }
-
-       if(ps->cur_motor) {
-               set_motor_var(ps, ps->cur_motor, name, value);
-       }
-}
-
-static bool have_var(ParserState *ps, const char *name)
-{
-       return ps->var.find(name) != ps->var.end();
-}
-
-static const Value &get_var(ParserState *ps, const char *name)
-{
-       return ps->var[name];
-}
-
-static void set_func(ParserState *ps, const char *name, Func func, int nargs)
-{
-       FuncDesc fdesc;
-       fdesc.func = func;
-       fdesc.arity = nargs;
-       ps->func[name] = fdesc;
-}
-
-static bool have_func(ParserState *ps, const char *name)
-{
-       return ps->func.find(name) != ps->func.end();
-}
-
-static Func get_func(ParserState *ps, const char *name)
-{
-       return ps->func[name].func;
-}
-
-static int get_func_arity(ParserState *ps, const char *name)
-{
-       return ps->func[name].arity;
-}
-
-static void print_value(const Value &val, FILE *fp)
-{
-       switch(val.type) {
-       case VAL_NUM:
-               fprintf(fp, "%f", val.v[0]);
-               break;
-
-       case VAL_VEC:
-               fprintf(fp, "[%f %f %f %f]", val.v[0], val.v[1], val.v[2], val.v[3]);
-               break;
-
-       case VAL_STR:
-               fprintf(fp, "\"%s\"", val.s.c_str());
-               break;
-
-       default:
-               fprintf(fp, "<invalid>");
-       }
-}
-
-// Gear-specific stuff
-
-static Gear *begin_gear(ParserState *ps)
-{
-       Gear *res = new Gear;
-       ps->cur_gear.push_back(res);
-       return res;
-}
-
-static bool end_gear(ParserState *ps)
-{
-       if(ps->cur_gear.empty()) {
-               errmsg(ps, "parser error: unbalanced end_gear");
-               return false;
-       }
-       Gear *gear = ps->cur_gear.back();
-       ps->cur_gear.pop_back();
-
-       printf("DBG: end_gear: %s\n", gear->name.c_str());
-
-       if(gear->name.empty() || ps->gears[gear->name]) {
-               char buf[32];
-               sprintf(buf, "gear%04d", (int)ps->gears.size());
-               gear->name = buf;
-       }
-       printf("DBG: adding gear: %s\n", gear->name.c_str());
-       ps->gears[gear->name] = gear;
-       ps->mcn->add_gear(gear);
-
-       if(!ps->cur_gear.empty()) {
-               ps->cur_gear.back()->attach(gear);
-       }
-       return true;
-}
-
-static Gear *this_gear(ParserState *ps)
-{
-       return ps->cur_gear.empty() ? 0 : ps->cur_gear.back();
-}
-
-static Gear *find_gear(ParserState *ps, const char *name)
-{
-       // search progressively wider lexical scopes
-       std::vector<Gear*>::const_reverse_iterator it = ps->cur_gear.rbegin();
-       while(it != ps->cur_gear.rend()) {
-               Gear *g = *it++;
-               if(g->name == std::string(name)) {
-                       return g;
-               }
-       }
-
-       return ps->gears[name];
-}
-
-static void update_gear_vars(ParserState *ps, Gear *gear)
-{
-       std::map<std::string, Value>::const_iterator it = ps->var.begin();
-       while(it != ps->var.end()) {
-               set_gear_var(gear, it->first.c_str(), it->second);
-               ++it;
-       }
-}
-
-#define ASSERT_TYPE(v, t) \
-       do { \
-               if((v).type != (t)) { \
-                       fprintf(stderr, "type mismatch while trying to set %s to value: ", name); \
-                       print_value(val, stderr); \
-                       fputc('\n', stderr); \
-                       return false; \
-               } \
-       } while(0)
-
-#define VVEC3(val) Vec3((val).v[0], (val).v[1], (val).v[2])
-#define VVEC4(val) Vec4((val).v[0], (val).v[1], (val).v[2], (val).v[3])
-
-static bool set_gear_var(Gear *gear, const char *name, const Value &val)
-{
-       if(strcmp(name, "name") == 0) {
-               ASSERT_TYPE(val, VAL_STR);
-               gear->name = val.s;
-
-       } else if(strcmp(name, "position") == 0) {
-               ASSERT_TYPE(val, VAL_VEC);
-               gear->pos = VVEC3(val);
-
-       } else if(strcmp(name, "plane") == 0) {
-               ASSERT_TYPE(val, VAL_VEC);
-               gear->axis = VVEC3(val);
-               gear->pdist = val.v[3];
-               printf("setting plane eqn: %f %f %f  %f\n", val.v[0], val.v[1], val.v[2], val.v[3]);
-
-       } else if(strcmp(name, "thickness") == 0) {
-               ASSERT_TYPE(val, VAL_NUM);
-               gear->thickness = val.v[0];
-
-       } else if(strcmp(name, "teeth") == 0) {
-               ASSERT_TYPE(val, VAL_NUM);
-               gear->set_teeth(val.v[0]);      // set teeth and recalc radius
-
-       } else if(strcmp(name, "teeth_length") == 0) {
-               ASSERT_TYPE(val, VAL_NUM);
-               gear->teeth_length = val.v[0];
-
-       } else if(strcmp(name, "pitch") == 0) {
-               ASSERT_TYPE(val, VAL_NUM);
-               gear->set_teeth(gear->nteeth, val.v[0]); // set pitch and recalc radius
-
-       } else if(strcmp(name, "color") == 0) {
-               ASSERT_TYPE(val, VAL_VEC);
-               gear->color = VVEC3(val);
-
-       } else if(strcmp(name, "roughness") == 0) {
-               ASSERT_TYPE(val, VAL_NUM);
-               gear->roughness = val.v[0];
-
-       } else {
-               return false;
-       }
-
-       return true;
-}
-
-
-// motor stuff
-
-static void update_motor_vars(ParserState *ps, Motor *motor)
-{
-       std::map<std::string, Value>::const_iterator it = ps->var.begin();
-       while(it != ps->var.end()) {
-               set_motor_var(ps, motor, it->first.c_str(), it->second);
-               ++it;
-       }
-}
-
-static bool set_motor_var(ParserState *ps, Motor *motor, const char *name, const Value &val)
-{
-       if(strcmp(name, "drive") == 0) {
-               ASSERT_TYPE(val, VAL_STR);
-               Gear *gear = ps->gears[val.s];
-               if(!gear) {
-                       errmsg(ps, "undefined gear: %s", val.s.c_str());
-                       return false;
-               }
-               int idx = ps->mcn->get_gear_index(gear);
-               assert(idx >= 0);
-               motor->drive = idx;
-
-       } else if(strcmp(name, "speed") == 0) {
-               ASSERT_TYPE(val, VAL_NUM);
-               motor->speed = val.v[0];
-
-       } else {
-               return false;
-       }
-       return true;
-}
-
-
-// built-in functions
-static bool func_adjacent(ParserState *ps)
-{
-       Value val_angle = ps->val.top(); ps->val.pop();
-       Value val_gear_name = ps->val.top(); ps->val.pop();
-
-       if(val_gear_name.type != VAL_STR) {
-               expected(ps, "gear name (string) as 1st arg to adjacent");
-               return false;
-       }
-       if(val_angle.type != VAL_NUM) {
-               expected(ps, "angle as 2nd arg to adjacent");
-               return false;
-       }
-
-       Gear *gthis = this_gear(ps);
-       if(!gthis) {
-               errmsg(ps, "adjacent: called outside of a gear block");
-               return false;
-       }
-
-       Gear *gother = ps->gears[val_gear_name.s];
-       if(!gother) {
-               errmsg(ps, "adjacent: gear \"%s\" not found", val_gear_name.s.c_str());
-               return false;
-       }
-
-       Mat4 xform;
-       xform.rotation(deg_to_rad(val_angle.v[0]), gother->axis);
-       Vec3 dir = xform * Vec3(0, 1, 0);
-
-       float sum_radii = gthis->radius + gother->radius;
-       float avg_teeth_len = (gthis->teeth_length + gother->teeth_length) * 0.5;
-       Vec3 pos = gother->pos + dir * (sum_radii - avg_teeth_len * 0.75);
-
-       Value res;
-       res.type = VAL_VEC;
-       res.v[0] = pos.x;
-       res.v[1] = pos.y;
-       res.v[2] = pos.z;
-       ps->val.push(res);
-       return true;
-}
-
-static bool func_coaxial(ParserState *ps)
-{
-       Value val_dist = ps->val.top(); ps->val.pop();
-       Value val_gear_name = ps->val.top(); ps->val.pop();
-
-       if(val_gear_name.type != VAL_STR) {
-               expected(ps, "gear name (string) as 1st arg to func_coaxial");
-               return false;
-       }
-       if(val_dist.type != VAL_NUM) {
-               expected(ps, "stacking distance as 2nd arg to func_coaxial");
-               return false;
-       }
-
-       Gear *gthis = this_gear(ps);
-       if(!gthis) {
-               errmsg(ps, "coaxial: called outside of a gear block");
-               return false;
-       }
-
-       Gear *gother = find_gear(ps, val_gear_name.s.c_str());
-       if(!gother) {
-               errmsg(ps, "coaxial: gear \"%s\" not found", val_gear_name.s.c_str());
-               return false;
-       }
-
-       float avg_thickness = (gthis->thickness + gother->thickness) * 0.5;
-       Vec3 pos = gother->pos + gother->axis * val_dist.v[0] * avg_thickness;
-
-       Value res;
-       res.type = VAL_VEC;
-       res.v[0] = pos.x;
-       res.v[1] = pos.y;
-       res.v[2] = pos.z;
-       ps->val.push(res);
-       return true;
-}
diff --git a/src/mparser.h b/src/mparser.h
deleted file mode 100644 (file)
index eb64f1b..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef MPARSER_H_
-#define MPARSER_H_
-
-#include <stdio.h>
-#include "machine.h"
-
-bool parse_machine(Machine *mcn, const char *fname);
-
-#endif /* MPARSER_H_ */