gears meshing, machines of gears, machines of MADNESS
authorJohn Tsiombikas <nuclear@mutantstargoat.com>
Fri, 16 Sep 2016 22:26:05 +0000 (01:26 +0300)
committerJohn Tsiombikas <nuclear@mutantstargoat.com>
Fri, 16 Sep 2016 22:26:05 +0000 (01:26 +0300)
src/gear.cc
src/gear.h
src/machine.cc [new file with mode: 0644]
src/machine.h [new file with mode: 0644]
src/main.cc

index 6450c65..865679c 100644 (file)
@@ -12,6 +12,7 @@ Gear::Gear()
        teeth_length = 5;
        thickness = 5;
        bevel = 1.5;
+       init_angle = 0;
 
        mesh = 0;
 }
@@ -21,6 +22,24 @@ Gear::~Gear()
        delete mesh;
 }
 
+void Gear::set_teeth(int nt, float tooth_pitch)
+{
+       float circ = tooth_pitch * nt;
+       radius = circ / (2.0 * M_PI);
+       nteeth = nt;
+       init_angle = get_angular_pitch() * 3.0 / 8.0;
+}
+
+float Gear::get_rotation() const
+{
+       return fmod(init_angle + angle, M_PI * 2.0);
+}
+
+float Gear::get_angular_pitch() const
+{
+       return 2.0 * M_PI / (float)nteeth;
+}
+
 void Gear::draw() const
 {
        if(!mesh) {
@@ -35,13 +54,16 @@ void Gear::draw() const
 
        mesh->draw();
 
-       /*
        glPushAttrib(GL_ENABLE_BIT);
        glDisable(GL_LIGHTING);
-       glColor3f(0, 1, 0);
-       mesh->draw_normals();
+
+       glLineWidth(2.0);
+       glBegin(GL_LINES);
+       glColor3f(0, 0, 1);
+       glVertex3f(0, 0, -10);
+       glVertex3f(0, 0, 10);
+       glEnd();
        glPopAttrib();
-       */
 
        glPopMatrix();
 }
@@ -108,5 +130,6 @@ void Gear::calc_matrix() const
        up = cross(axis, right);
 
        xform = Mat4(right, up, axis);
+       xform.rotate_z(get_rotation());
        xform.translate(pos);
 }
index 5fde21f..12d9ba2 100644 (file)
@@ -2,6 +2,7 @@
 #define GEAR_H_
 
 #include <vector>
+#include <string>
 #include <gmath/gmath.h>
 #include "mesh.h"
 
@@ -44,9 +45,11 @@ private:
        float contour(float u);
 
 public:
+       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 */
@@ -63,6 +66,15 @@ public:
        Gear();
        ~Gear();
 
+       // 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);
+
+       float get_rotation() const;
+
+       // returns the angle (in radians) from one tooth to the next
+       float get_angular_pitch() const;
+
        void draw() const;
 
        bool gen_mesh();
diff --git a/src/machine.cc b/src/machine.cc
new file mode 100644 (file)
index 0000000..716fc89
--- /dev/null
@@ -0,0 +1,151 @@
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+#include "machine.h"
+
+static float delta_angle(float a, float b);
+static float signed_delta_angle(float a, float b);
+
+Machine::Machine()
+{
+       meshing = 0;
+       visited = 0;
+}
+
+Machine::~Machine()
+{
+       int ngears = (int)gears.size();
+       for(int i=0; i<ngears; i++) {
+               delete gears[i];
+       }
+
+       if(meshing) {
+               for(int i=0; i<ngears; i++) {
+                       delete [] meshing[i];
+               }
+               delete [] meshing;
+       }
+       delete [] visited;
+}
+
+void Machine::add_gear(Gear *g)
+{
+       gears.push_back(g);
+}
+
+void Machine::add_motor(int gearidx, float speed_hz)
+{
+       Motor m;
+       m.drive = gearidx;
+       m.speed = speed_hz;
+       motors.push_back(m);
+}
+
+void Machine::calc_meshing()
+{
+       int ngears = (int)gears.size();
+
+       if(!meshing) {
+               meshing = new bool*[ngears];
+               for(int i=0; i<ngears; i++) {
+                       meshing[i] = new bool[ngears];
+               }
+       }
+
+       if(!visited) {
+               visited = new bool[ngears];
+       }
+
+       // let's mesh everything together just for shits and giggles
+       for(int i=0; i<ngears; i++) {
+               for(int j=0; j<ngears; j++) {
+                       meshing[i][j] = abs(i - j) & 1 ? true : false;
+                       if(meshing[i][j]) {
+                               printf("connecting %d - %d\n", i, j);
+                       }
+               }
+       }
+
+       // 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++) {
+               for(int j=1; j<ngears; j++) {
+                       if(meshing[i][j]) {
+                               assert(i != j);
+
+                               float tarc = gears[i]->get_angular_pitch(); // assumed to be the same for meshing gears
+
+                               float frac_i = fmod(gears[i]->init_angle / gears[i]->get_angular_pitch() + 1.0, 1.0);
+                               float frac_j = fmod(gears[j]->init_angle / gears[j]->get_angular_pitch() + 1.0, 1.0);
+                               float delta = frac_j - frac_i;
+
+                               float correction = 0.5 - delta;
+                               gears[j]->init_angle += correction * gears[j]->get_angular_pitch();
+                       }
+               }
+       }
+}
+
+void Machine::update_gear(int idx, float angle)
+{
+       if(visited[idx]) {
+               if(delta_angle(angle, gears[idx]->angle) > 0.25 / gears[idx]->nteeth) {
+                       fprintf(stderr, "warning: trying to transmit different values to gear %s (%d)\n",
+                                       gears[idx]->name.c_str(), idx);
+               }
+               return;
+       }
+
+       gears[idx]->angle = angle;
+       visited[idx] = true;
+
+       int ngears = (int)gears.size();
+       for(int i=0; i<ngears; i++) {
+               if(!meshing[idx][i]) continue;
+               assert(idx != i);
+
+               float ratio = -(float)gears[idx]->nteeth / (float)gears[i]->nteeth;
+               update_gear(i, angle * ratio);
+       }
+}
+
+void Machine::update(float dt)
+{
+       int ngears = (int)gears.size();
+
+       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();
+       }
+}
+
+
+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));
+}
+
+
+static float signed_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);
+
+       if(fabs(a - b) < fabs(api - bpi)) {
+               return a - b;
+       }
+       return api - bpi;
+}
diff --git a/src/machine.h b/src/machine.h
new file mode 100644 (file)
index 0000000..f222cd7
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef MACHINE_H_
+#define MACHINE_H_
+
+#include <vector>
+#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;
+       bool **meshing;
+       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);
+
+       void calc_meshing();
+
+       void update(float dt);
+       void draw() const;
+};
+
+#endif // MACHINE_H_
index c4696f6..0447748 100644 (file)
@@ -7,11 +7,12 @@
 #else
 #include <GL/glut.h>
 #endif
-#include "gear.h"
+#include "machine.h"
 
 bool init();
 void cleanup();
 void display();
+void idle();
 void draw_gears();
 void reshape(int x, int y);
 void keyb(unsigned char key, int x, int y);
@@ -23,16 +24,18 @@ static float cam_theta, cam_phi;
 static int prev_mx, prev_my;
 static bool bnstate[8];
 
-static Gear *test_gear;
+static unsigned int start_time, prev_msec;
+static Machine *machine;
 
 int main(int argc, char **argv)
 {
-       glutInit(&argc, argv);
        glutInitWindowSize(1024, 768);
+       glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
        glutCreateWindow("Antikythera");
 
        glutDisplayFunc(display);
+       glutIdleFunc(idle);
        glutReshapeFunc(reshape);
        glutKeyboardFunc(keyb);
        glutMouseFunc(mouse);
@@ -59,19 +62,49 @@ bool init()
 
        Mesh::use_custom_sdr_attr = false;
 
-       test_gear = new Gear;
-       test_gear->gen_mesh();
+       machine = new Machine;
+
+       const float pitch = 10.0f;
+
+       Gear *gear1 = new Gear;
+       gear1->pos = Vec3(-50, 0, 0);
+       gear1->set_teeth(16, pitch);
+       gear1->gen_mesh();
+       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);
 
+       machine->add_motor(0, 1.0);
+       machine->calc_meshing();
+
+       start_time = glutGet(GLUT_ELAPSED_TIME);
        return true;
 }
 
 void cleanup()
 {
-       delete test_gear;
+       delete machine;
 }
 
 void display()
 {
+       unsigned int msec = glutGet(GLUT_ELAPSED_TIME) - start_time;
+       float dt = (float)(msec - prev_msec) / 1000.0f;
+       prev_msec = msec;
+
+       machine->update(dt);
+
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
        glMatrixMode(GL_MODELVIEW);
@@ -86,13 +119,18 @@ void display()
        assert(glGetError() == GL_NO_ERROR);
 }
 
+void idle()
+{
+       glutPostRedisplay();
+}
+
 void draw_gears()
 {
        /* world scale is in meters, gears are in millimeters, sclae by 1/1000 */
        glPushMatrix();
        glScalef(0.001, 0.001, 0.001);
 
-       test_gear->draw();
+       machine->draw();
 
        glPopMatrix();
 }