teeth_length = 5;
thickness = 5;
bevel = 1.5;
+ init_angle = 0;
mesh = 0;
}
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) {
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();
}
up = cross(axis, right);
xform = Mat4(right, up, axis);
+ xform.rotate_z(get_rotation());
xform.translate(pos);
}
#define GEAR_H_
#include <vector>
+#include <string>
#include <gmath/gmath.h>
#include "mesh.h"
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 */
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();
--- /dev/null
+#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;
+}
--- /dev/null
+#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_
#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);
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);
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);
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();
}