*.o
*.swp
*.d
-anti
+demo
+data/
+.clang_complete
-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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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_
+++ /dev/null
-#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));
-}
+++ /dev/null
-#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_
--- /dev/null
+#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);
+}
--- /dev/null
+#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_
--- /dev/null
+#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));
+}
--- /dev/null
+#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_
--- /dev/null
+#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;
+}
--- /dev/null
+#ifndef MPARSER_H_
+#define MPARSER_H_
+
+#include <stdio.h>
+#include "machine.h"
+
+bool parse_machine(Machine *mcn, const char *fname);
+
+#endif /* MPARSER_H_ */
glutInitWindowSize(1024, 768);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE | GLUT_MULTISAMPLE);
- glutCreateWindow("Antikythera");
+ glutCreateWindow("demo");
glutDisplayFunc(display);
glutIdleFunc(idle);
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);
+++ /dev/null
-#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;
-}
+++ /dev/null
-#ifndef MPARSER_H_
-#define MPARSER_H_
-
-#include <stdio.h>
-#include "machine.h"
-
-bool parse_machine(Machine *mcn, const char *fname);
-
-#endif /* MPARSER_H_ */