-/* pinion, Copyright (c) 2004 Jamie Zawinski <jwz@jwz.org>
+/* pinion, Copyright (c) 2004-2011 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* implied warranty.
*/
-#include <X11/Intrinsic.h>
-
-extern XtAppContext app;
-
-#define PROGCLASS "Pinion"
-#define HACK_INIT init_pinion
-#define HACK_DRAW draw_pinion
-#define HACK_RESHAPE reshape_pinion
-#define HACK_HANDLE_EVENT pinion_handle_event
-#define EVENT_MASK PointerMotionMask
-#define sws_opts xlockmore_opts
-
-#define DEF_SPIN_SPEED "1.0"
-#define DEF_SCROLL_SPEED "1.0"
-#define DEF_GEAR_SIZE "1.0"
-#define DEF_MAX_RPM "900"
-
#define DEFAULTS "*delay: 15000 \n" \
"*showFPS: False \n" \
"*wireframe: False \n" \
- "*titleFont: -*-times-bold-r-normal-*-180-*\n" \
- "*titleFont2: -*-times-bold-r-normal-*-120-*\n" \
- "*titleFont3: -*-times-bold-r-normal-*-80-*\n" \
+ "*titleFont: -*-helvetica-medium-r-normal-*-180-*\n" \
+ "*titleFont2: -*-helvetica-medium-r-normal-*-120-*\n" \
+ "*titleFont3: -*-helvetica-medium-r-normal-*-80-*\n" \
+# define refresh_pinion 0
+# define release_pinion 0
#undef countof
#define countof(x) (sizeof((x))/sizeof((*x)))
#include "normals.h"
#include "gltrackball.h"
#include "glxfonts.h"
+#include "involute.h"
#include <ctype.h>
#ifdef USE_GL /* whole file */
-#include <GL/glu.h>
-
-typedef struct {
- unsigned long id; /* unique name */
- double x, y, z; /* position */
- double r; /* radius of the gear, at middle of teeth */
- double th; /* rotation (degrees) */
-
- GLint nteeth; /* how many teeth */
- double tooth_w, tooth_h; /* size of teeth */
-
- double inner_r; /* radius of the (larger) inside hole */
- double inner_r2; /* radius of the (smaller) inside hole, if any */
- double inner_r3; /* yet another */
-
- double thickness; /* height of the edge */
- double thickness2; /* height of the (smaller) inside disc if any */
- double thickness3; /* yet another */
- int spokes; /* how many spokes inside, if any */
- int nubs; /* how many little nubbly bits, if any */
- double spoke_thickness; /* spoke versus hole */
- GLfloat wobble; /* factory defect! */
- int motion_blur_p; /* whether it's spinning too fast to draw */
- int polygons; /* how many polys in this gear */
-
- double ratio; /* gearing ratio with previous gears */
- double rpm; /* approximate revolutions per minute */
-
- Bool base_p; /* whether this gear begins a new train */
- int coax_p; /* whether this is one of a pair of bound gears.
- 1 for first, 2 for second. */
- double coax_thickness; /* thickness of the other gear in the pair */
- enum { SMALL, MEDIUM, LARGE } size; /* Controls complexity of mesh. */
- GLfloat color[4];
- GLfloat color2[4];
-
- GLuint dlist;
-} gear;
-
+#define DEF_SPIN_SPEED "1.0"
+#define DEF_SCROLL_SPEED "1.0"
+#define DEF_GEAR_SIZE "1.0"
+#define DEF_MAX_RPM "900"
typedef struct {
GLXContext *glx_context;
XFontStruct *xfont1, *xfont2, *xfont3;
GLuint font1_dlist, font2_dlist, font3_dlist;
GLuint title_list;
+ int draw_tick;
+
+ GLfloat plane_displacement; /* distance between coaxial gears */
+
+ int debug_size_failures; /* for debugging messages */
+ int debug_position_failures;
+ unsigned long current_length; /* gear count in current train */
+ unsigned long current_blur_length; /* how long have we been blurring? */
} pinion_configuration;
static pinion_configuration *pps = NULL;
-static GLfloat spin_speed, scroll_speed, max_rpm, gear_size;
-static GLfloat plane_displacement = 0.1; /* distance between coaxial gears */
+
+/* command line arguments */
+static GLfloat spin_speed, scroll_speed, gear_size, max_rpm;
static Bool verbose_p = False; /* print progress on stderr */
-static Bool debug_placement_p = False; /* extreme verbosity on stderr */
static Bool debug_p = False; /* render as flat schematic */
-static Bool debug_one_gear_p = False; /* draw one big stationary gear */
-static Bool wire_all_p = False; /* in wireframe, do not abbreviate */
-static int debug_size_failures; /* for debugging messages */
-static int debug_position_failures;
-static unsigned long current_length; /* gear count in current train */
-static unsigned long current_blur_length; /* how long have we been blurring? */
+/* internal debugging variables */
+static Bool debug_placement_p = False; /* extreme verbosity on stderr */
+static Bool debug_one_gear_p = False; /* draw one big stationary gear */
static XrmOptionDescRec opts[] = {
{&verbose_p, "verbose", "Verbose", "False", t_Bool},
};
-ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
+ENTRYPOINT ModeSpecOpt pinion_opts = {countof(opts), opts, countof(vars), vars, NULL};
\f
/* Font stuff
*label = 0;
else
{
- sprintf (label, "%d teeth\n", g->nteeth);
+ sprintf (label, "%d teeth\n", (int) g->nteeth);
rpm_string (g->rpm, label + strlen(label));
if (debug_p)
sprintf (label + strlen (label), "\nPolys: %d\nModel: %s (%.2f)\n",
g->polygons,
- (g->size == SMALL ? "small" : g->size == MEDIUM ? "medium"
+ (g->size == INVOLUTE_SMALL ? "small" :
+ g->size == INVOLUTE_MEDIUM ? "medium"
: "large"),
g->tooth_h * MI_HEIGHT(mi));
}
print_gl_string (mi->dpy, f, fl,
mi->xgwa.width, mi->xgwa.height,
10, mi->xgwa.height - 10,
- label);
+ label, False);
}
glEndList ();
}
}
-
-/* Which of the gear's inside rings is the biggest?
- */
-static int
-biggest_ring (gear *g, double *posP, double *sizeP, double *heightP)
-{
- double r0 = (g->r - g->tooth_h/2);
- double r1 = g->inner_r;
- double r2 = g->inner_r2;
- double r3 = g->inner_r3;
- double w1 = (r1 ? r0 - r1 : r0);
- double w2 = (r2 ? r1 - r2 : 0);
- double w3 = (r3 ? r2 - r3 : 0);
- double h1 = g->thickness;
- double h2 = g->thickness2;
- double h3 = g->thickness3;
-
- if (g->spokes) w2 = 0;
-
- if (w1 > w2 && w1 > w3)
- {
- if (posP) *posP = (r0+r1)/2;
- if (sizeP) *sizeP = w1;
- if (heightP) *heightP = h1;
- return 0;
- }
- else if (w2 > w1 && w2 > w3)
- {
- if (posP) *posP = (r1+r2)/2;
- if (sizeP) *sizeP = w2;
- if (heightP) *heightP = h2;
- return 1;
- }
- else
- {
- if (posP) *posP = (r2+r3)/2;
- if (sizeP) *sizeP = w3;
- if (heightP) *heightP = h3;
- return 1;
- }
-}
-
\f
/* Layout and stuff.
*/
static gear *
new_gear (ModeInfo *mi, gear *parent, Bool coaxial_p)
{
+ pinion_configuration *pp = &pps[MI_SCREEN(mi)];
gear *g = (gear *) calloc (1, sizeof (*g));
int loop_count = 0;
- static unsigned long id = 0;
+ static unsigned long id = 0; /* only used in debugging output */
if (!g) return 0;
if (coaxial_p && !parent) abort();
g->id = ++id;
+ g->coax_displacement = pp->plane_displacement;
+
while (1)
{
loop_count++;
if (parent->r < g->r * 0.6) break; /* g much larger than parent */
}
+ /* g->tooth_slope = (parent ? -parent->tooth_slope : 4); */
+
/* Colorize
*/
g->color[0] = 0.5 + frand(0.5);
if (g->nteeth > 5)
{
double size = 0;
- biggest_ring (g, 0, &size, 0);
+ involute_biggest_ring (g, 0, &size, 0);
if (size > g->r * 0.2 && (random() % 5) == 0)
{
g->nubs = 1 + (random() % 16);
*/
{
double pix = g->tooth_h * MI_HEIGHT(mi); /* approx. tooth size in pixels */
- if (pix <= 2.5) g->size = SMALL;
- else if (pix <= 3.5) g->size = MEDIUM;
- else g->size = LARGE;
+ if (pix <= 2.5) g->size = INVOLUTE_SMALL;
+ else if (pix <= 3.5) g->size = INVOLUTE_MEDIUM;
+ else g->size = INVOLUTE_LARGE;
}
g->base_p = !parent;
progname,
(g->r + g->tooth_h), gear_size,
pp->vp_width, pp->vp_height);
- debug_size_failures++;
+ pp->debug_size_failures++;
return False;
}
}
else if (coaxial_p)
{
- double off = plane_displacement;
+ double off = pp->plane_displacement;
g->x = parent->x;
g->y = parent->y;
if (verbose_p && debug_placement_p)
fprintf (stderr, "%s: placement: bad depth: %.2f\n",
progname, g->z);
- debug_position_failures++;
+ pp->debug_position_failures++;
return False;
}
}
if (verbose_p && debug_placement_p)
fprintf (stderr, "%s: placement: out of bounds: %s\n",
progname, (g->y > pp->vp_top ? "top" : "bottom"));
- debug_position_failures++;
+ pp->debug_position_failures++;
return False;
}
{
if (verbose_p && debug_placement_p)
fprintf (stderr, "%s: placement: out of bounds: left\n", progname);
- debug_position_failures++;
+ pp->debug_position_failures++;
return False;
}
if (verbose_p && debug_placement_p)
fprintf (stderr, "%s: placement: collision with %lu\n",
progname, og->id);
- debug_position_failures++;
+ pp->debug_position_failures++;
return False;
}
}
/* Make deeper gears be darker.
*/
{
- double depth = g->z / plane_displacement;
+ double depth = g->z / pp->plane_displacement;
double brightness = 1 + (depth / 6);
double limit = 0.4;
if (brightness < limit) brightness = limit;
Bool last_ditch_coax_p = False;
int loop_count = 0;
- debug_size_failures = 0;
- debug_position_failures = 0;
+ pp->debug_size_failures = 0;
+ pp->debug_position_failures = 0;
AGAIN:
loop_count++;
it's a safe guess that we've wandered off into the woods and aren't
coming back. Bail on this train.
*/
- if (current_blur_length >= 10)
+ if (pp->current_blur_length >= 10)
{
if (verbose_p)
fprintf (stderr, "%s: it's a blurpocalypse!\n\n", progname);
"%s: placement: resetting growth zone! "
"failed: %d size, %d pos\n",
progname,
- debug_size_failures, debug_position_failures);
+ pp->debug_size_failures, pp->debug_position_failures);
for (i = pp->ngears-1; i >= 0; i--)
{
gear *g = pp->gears[i];
if (g->coax_p)
{
+ if (!parent) abort();
if (g->x != parent->x) abort();
if (g->y != parent->y) abort();
if (g->z == parent->z) abort();
fprintf (stderr, " %2d%%",
(int) (g->r * 2 * 100 / pp->vp_height));
- fprintf (stderr, " %2d teeth", g->nteeth);
+ fprintf (stderr, " %2d teeth", (int) g->nteeth);
fprintf (stderr, " %3.0f rpm;", g->rpm);
{
char buf1[50], buf2[50], buf3[100];
*buf1 = 0; *buf2 = 0; *buf3 = 0;
- if (debug_size_failures)
- sprintf (buf1, "%3d sz", debug_size_failures);
- if (debug_position_failures)
- sprintf (buf2, "%2d pos", debug_position_failures);
+ if (pp->debug_size_failures)
+ sprintf (buf1, "%3d sz", pp->debug_size_failures);
+ if (pp->debug_position_failures)
+ sprintf (buf2, "%2d pos", pp->debug_position_failures);
if (*buf1 || *buf2)
sprintf (buf3, " tries: %-7s%s", buf1, buf2);
fprintf (stderr, "%-21s", buf3);
}
- if (g->base_p) fprintf (stderr, " RESET %lu", current_length);
+ if (g->base_p) fprintf (stderr, " RESET %lu", pp->current_length);
fprintf (stderr, "\n");
}
if (g->base_p)
- current_length = 1;
+ pp->current_length = 1;
else
- current_length++;
+ pp->current_length++;
if (g->motion_blur_p)
- current_blur_length++;
+ pp->current_blur_length++;
else
- current_blur_length = 0;
+ pp->current_blur_length = 0;
}
\f
-/* Rendering the 3D objects into the scene.
- */
-
-
-/* Draws an uncapped tube of the given radius extending from top to bottom,
- with faces pointing either in or out.
- */
-static int
-draw_ring (ModeInfo *mi, int segments,
- GLfloat r, GLfloat top, GLfloat bottom, Bool in_p)
-{
- int i;
- int polys = 0;
- Bool wire_p = MI_IS_WIREFRAME(mi);
- GLfloat width = M_PI * 2 / segments;
-
- if (top != bottom)
- {
- glFrontFace (in_p ? GL_CCW : GL_CW);
- glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
- for (i = 0; i < segments + (wire_p ? 0 : 1); i++)
- {
- GLfloat th = i * width;
- GLfloat cth = cos(th);
- GLfloat sth = sin(th);
- if (in_p)
- glNormal3f (-cth, -sth, 0);
- else
- glNormal3f (cth, sth, 0);
- glVertex3f (cth * r, sth * r, top);
- glVertex3f (cth * r, sth * r, bottom);
- }
- polys += segments;
- glEnd();
- }
-
- if (wire_p)
- {
- glBegin (GL_LINE_LOOP);
- for (i = 0; i < segments; i++)
- {
- GLfloat th = i * width;
- glVertex3f (cos(th) * r, sin(th) * r, top);
- }
- glEnd();
- glBegin (GL_LINE_LOOP);
- for (i = 0; i < segments; i++)
- {
- GLfloat th = i * width;
- glVertex3f (cos(th) * r, sin(th) * r, bottom);
- }
- glEnd();
- }
-
- return polys;
-}
-
-
-/* Draws a donut-shaped disc between the given radii,
- with faces pointing either up or down.
- The first radius may be 0, in which case, a filled disc is drawn.
- */
-static int
-draw_disc (ModeInfo *mi, int segments,
- GLfloat ra, GLfloat rb, GLfloat z, Bool up_p)
-{
- int i;
- int polys = 0;
- Bool wire_p = MI_IS_WIREFRAME(mi);
- GLfloat width = M_PI * 2 / segments;
-
- if (ra < 0) abort();
- if (rb <= 0) abort();
-
- if (ra == 0)
- glFrontFace (up_p ? GL_CW : GL_CCW);
- else
- glFrontFace (up_p ? GL_CCW : GL_CW);
-
- if (ra == 0)
- glBegin (wire_p ? GL_LINES : GL_TRIANGLE_FAN);
- else
- glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
-
- glNormal3f (0, 0, (up_p ? -1 : 1));
-
- if (ra == 0 && !wire_p)
- glVertex3f (0, 0, z);
-
- for (i = 0; i < segments + (wire_p ? 0 : 1); i++)
- {
- GLfloat th = i * width;
- GLfloat cth = cos(th);
- GLfloat sth = sin(th);
- if (wire_p || ra != 0)
- glVertex3f (cth * ra, sth * ra, z);
- glVertex3f (cth * rb, sth * rb, z);
- }
- polys += segments;
- glEnd();
- return polys;
-}
-
-
-/* Draws N thick radial lines between the given radii,
- with faces pointing either up or down.
- */
-static int
-draw_spokes (ModeInfo *mi, int n, GLfloat thickness, int segments,
- GLfloat ra, GLfloat rb, GLfloat z1, GLfloat z2)
-{
- int i;
- int polys = 0;
- Bool wire_p = MI_IS_WIREFRAME(mi);
- GLfloat width;
- int segments2 = 0;
- int insegs, outsegs;
- int tick;
- int state;
-
- if (ra <= 0 || rb <= 0) abort();
-
- segments *= 3;
-
- while (segments2 < segments) /* need a multiple of N >= segments */
- segments2 += n; /* (yes, this is a moronic way to find that) */
-
- insegs = ((float) (segments2 / n) + 0.5) / thickness;
- outsegs = (segments2 / n) - insegs;
- if (insegs <= 0) insegs = 1;
- if (outsegs <= 0) outsegs = 1;
-
- segments2 = (insegs + outsegs) * n;
- width = M_PI * 2 / segments2;
-
- tick = 0;
- state = 0;
- for (i = 0; i < segments2; i++, tick++)
- {
- GLfloat th1 = i * width;
- GLfloat th2 = th1 + width;
- GLfloat cth1 = cos(th1);
- GLfloat sth1 = sin(th1);
- GLfloat cth2 = cos(th2);
- GLfloat sth2 = sin(th2);
- GLfloat orb = rb;
-
- int changed = (i == 0);
-
- if (state == 0 && tick == insegs)
- tick = 0, state = 1, changed = 1;
- else if (state == 1 && tick == outsegs)
- tick = 0, state = 0, changed = 1;
-
- if ((state == 1 || /* in */
- (state == 0 && changed)) &&
- (!wire_p || wire_all_p))
- {
- /* top */
- glFrontFace (GL_CCW);
- glBegin (wire_p ? GL_LINES : GL_QUADS);
- glNormal3f (0, 0, -1);
- glVertex3f (cth1 * ra, sth1 * ra, z1);
- glVertex3f (cth1 * rb, sth1 * rb, z1);
- glVertex3f (cth2 * rb, sth2 * rb, z1);
- glVertex3f (cth2 * ra, sth2 * ra, z1);
- polys++;
- glEnd();
-
- /* bottom */
- glFrontFace (GL_CW);
- glBegin (wire_p ? GL_LINES : GL_QUADS);
- glNormal3f (0, 0, 1);
- glVertex3f (cth1 * ra, sth1 * ra, z2);
- glVertex3f (cth1 * rb, sth1 * rb, z2);
- glVertex3f (cth2 * rb, sth2 * rb, z2);
- glVertex3f (cth2 * ra, sth2 * ra, z2);
- polys++;
- glEnd();
- }
-
- if (state == 1 && changed) /* entering "in" state */
- {
- /* left */
- glFrontFace (GL_CW);
- glBegin (wire_p ? GL_LINES : GL_QUADS);
- do_normal (cth1 * rb, sth1 * rb, z1,
- cth1 * ra, sth1 * ra, z1,
- cth1 * rb, sth1 * rb, z2);
- glVertex3f (cth1 * ra, sth1 * ra, z1);
- glVertex3f (cth1 * rb, sth1 * rb, z1);
- glVertex3f (cth1 * rb, sth1 * rb, z2);
- glVertex3f (cth1 * ra, sth1 * ra, z2);
- polys++;
- glEnd();
- }
-
- if (state == 0 && changed) /* entering "out" state */
- {
- /* right */
- glFrontFace (GL_CCW);
- glBegin (wire_p ? GL_LINES : GL_QUADS);
- do_normal (cth2 * ra, sth2 * ra, z1,
- cth2 * rb, sth2 * rb, z1,
- cth2 * rb, sth2 * rb, z2);
- glVertex3f (cth2 * ra, sth2 * ra, z1);
- glVertex3f (cth2 * rb, sth2 * rb, z1);
- glVertex3f (cth2 * rb, sth2 * rb, z2);
- glVertex3f (cth2 * ra, sth2 * ra, z2);
- polys++;
- glEnd();
- }
-
- rb = orb;
- }
- glEnd();
- return polys;
-}
-
-
-/* Draws some bumps (embedded cylinders) on the gear.
- */
-static int
-draw_gear_nubs (ModeInfo *mi, gear *g)
-{
- Bool wire_p = MI_IS_WIREFRAME(mi);
- int polys = 0;
- int i;
- int steps = (g->size != LARGE ? 5 : 20);
- double r, size, height;
- GLfloat *cc;
- int which;
- GLfloat width, off;
-
- if (! g->nubs) return 0;
-
- which = biggest_ring (g, &r, &size, &height);
- size /= 5;
- height *= 0.7;
-
- cc = (which == 1 ? g->color : g->color2);
- glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, cc);
-
- width = M_PI * 2 / g->nubs;
- off = M_PI / (g->nteeth * 2); /* align first nub with a tooth */
-
- for (i = 0; i < g->nubs; i++)
- {
- GLfloat th = (i * width) + off;
- glPushMatrix();
- glTranslatef (cos(th) * r, sin(th) * r, 0);
-
- if (wire_p && !wire_all_p)
- polys += draw_ring (mi, (g->size == LARGE ? steps/2 : steps),
- size, 0, 0, False);
- else
- {
- polys += draw_disc (mi, steps, 0, size, -height, True);
- polys += draw_disc (mi, steps, 0, size, height, False);
- polys += draw_ring (mi, steps, size, -height, height, False);
- }
- glPopMatrix();
- }
- return polys;
-}
-
-
-
-/* Draws a much simpler representation of a gear.
- */
-static int
-draw_gear_schematic (ModeInfo *mi, gear *g)
-{
- Bool wire_p = MI_IS_WIREFRAME(mi);
- int polys = 0;
- int i;
- GLfloat width = M_PI * 2 / g->nteeth;
-
- if (!wire_p) glDisable(GL_LIGHTING);
- glColor3f (g->color[0] * 0.6, g->color[1] * 0.6, g->color[2] * 0.6);
-
- glBegin (GL_LINES);
- for (i = 0; i < g->nteeth; i++)
- {
- GLfloat th = (i * width) + (width/4);
- glVertex3f (0, 0, -g->thickness/2);
- glVertex3f (cos(th) * g->r, sin(th) * g->r, -g->thickness/2);
- }
- polys += g->nteeth;
- glEnd();
-
- glBegin (GL_LINE_LOOP);
- for (i = 0; i < g->nteeth; i++)
- {
- GLfloat th = (i * width) + (width/4);
- glVertex3f (cos(th) * g->r, sin(th) * g->r, -g->thickness/2);
- }
- polys += g->nteeth;
- glEnd();
-
- if (!wire_p) glEnable(GL_LIGHTING);
- return polys;
-}
-
-
-/* Renders all the interior (non-toothy) parts of a gear:
- the discs, axles, etc.
- */
-static int
-draw_gear_interior (ModeInfo *mi, gear *g)
-{
- Bool wire_p = MI_IS_WIREFRAME(mi);
- int polys = 0;
-
- int steps = g->nteeth * 2;
- if (steps < 10) steps = 10;
- if ((wire_p && !wire_all_p) || g->size != LARGE) steps /= 2;
- if (g->size != LARGE && steps > 16) steps = 16;
-
- /* ring 1 (facing in) is done in draw_gear_teeth */
-
- /* ring 2 (facing in) and disc 2
- */
- if (g->inner_r2)
- {
- GLfloat ra = g->inner_r * 1.04; /* slightly larger than inner_r */
- GLfloat rb = g->inner_r2; /* since the points don't line up */
- GLfloat za = -g->thickness2/2;
- GLfloat zb = g->thickness2/2;
-
- glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color2);
-
- if ((g->coax_p != 1 && !g->inner_r3) ||
- (wire_p && wire_all_p))
- polys += draw_ring (mi, steps, rb, za, zb, True); /* ring facing in */
-
- if (wire_p && wire_all_p)
- polys += draw_ring (mi, steps, ra, za, zb, True); /* ring facing in */
-
- if (g->spokes)
- polys += draw_spokes (mi, g->spokes, g->spoke_thickness,
- steps, ra, rb, za, zb);
- else if (!wire_p || wire_all_p)
- {
- polys += draw_disc (mi, steps, ra, rb, za, True); /* top plate */
- polys += draw_disc (mi, steps, ra, rb, zb, False); /* bottom plate */
- }
- }
-
- /* ring 3 (facing in and out) and disc 3
- */
- if (g->inner_r3)
- {
- GLfloat ra = g->inner_r2;
- GLfloat rb = g->inner_r3;
- GLfloat za = -g->thickness3/2;
- GLfloat zb = g->thickness3/2;
-
- glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
-
- polys += draw_ring (mi, steps, ra, za, zb, False); /* ring facing out */
-
- if (g->coax_p != 1 || (wire_p && wire_all_p))
- polys += draw_ring (mi, steps, rb, za, zb, True); /* ring facing in */
-
- if (!wire_p || wire_all_p)
- {
- polys += draw_disc (mi, steps, ra, rb, za, True); /* top plate */
- polys += draw_disc (mi, steps, ra, rb, zb, False); /* bottom plate */
- }
- }
-
- /* axle tube
- */
- if (g->coax_p == 1)
- {
- GLfloat cap_height = g->coax_thickness/3;
-
- GLfloat ra = (g->inner_r3 ? g->inner_r3 :
- g->inner_r2 ? g->inner_r2 :
- g->inner_r);
- GLfloat za = -(g->thickness/2 + cap_height);
- GLfloat zb = g->coax_thickness/2 + plane_displacement + cap_height;
-
- glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
-
- if (wire_p && !wire_all_p) steps /= 2;
-
- polys += draw_ring (mi, steps, ra, za, zb, False); /* ring facing out */
-
- if (!wire_p || wire_all_p)
- {
- polys += draw_disc (mi, steps, 0, ra, za, True); /* top plate */
- polys += draw_disc (mi, steps, 0, ra, zb, False); /* bottom plate */
- }
- }
- return polys;
-}
-
-
-/* gear_teeth_geometry computes the vertices and normals of the teeth
- of a gear. This is the heavy lifting: there are a ton of polygons
- around the perimiter of a gear, and the normals are difficult (not
- radial or right angles.)
-
- It would be nice if we could cache this, but the numbers are
- different for essentially every gear:
-
- - Every gear has a different inner_r, so the vertices of the
- inner ring (and thus, the triangle fans on the top and bottom
- faces) are different in a non-scalable way.
-
- - If the ratio between tooth_w and tooth_h changes, the normals
- on the outside edges of the teeth are invalid (this can happen
- every time we start a new train.)
-
- So instead, we rely on OpenGL display lists to do the cacheing for
- us -- we only compute all these normals once per gear, instead of
- once per gear per frame.
- */
-
-typedef struct {
- int npoints;
- XYZ *points;
- XYZ *fnormals; /* face normals */
- XYZ *pnormals; /* point normals */
-} tooth_face;
-
-
-static void
-tooth_normals (tooth_face *f)
-{
- int i;
-
- /* Compute the face normals for each facet. */
- for (i = 0; i < f->npoints; i++)
- {
- XYZ p1, p2, p3;
- int a = i;
- int b = (i == f->npoints-1 ? 0 : i+1);
- p1 = f->points[a];
- p2 = f->points[b];
- p3 = p1;
- p3.z++;
- f->fnormals[i] = calc_normal (p1, p2, p3);
- }
-
- /* From the face normals, compute the vertex normals
- (by averaging the normals of adjascent faces.)
- */
- for (i = 0; i < f->npoints; i++)
- {
- int a = (i == 0 ? f->npoints-1 : i-1);
- int b = i;
- XYZ n1 = f->fnormals[a]; /* normal of [i-1 - i] face */
- XYZ n2 = f->fnormals[b]; /* normal of [i - i+1] face */
- f->pnormals[i].x = (n1.x + n2.x) / 2;
- f->pnormals[i].y = (n1.y + n2.y) / 2;
- f->pnormals[i].z = (n1.z + n2.z) / 2;
- }
-}
-
-
-static void
-gear_teeth_geometry (ModeInfo *mi, gear *g,
- tooth_face *orim, /* outer rim (the teeth) */
- tooth_face *irim) /* inner rim (the hole) */
-{
- int i;
- int ppt = 9; /* max points per tooth */
- GLfloat width = M_PI * 2 / g->nteeth;
- GLfloat rh = g->tooth_h;
- GLfloat tw = width;
-
- /* Approximate shape of an "involute" gear tooth.
-
- (TH)
- th0 th1 th2 th3 th4 th5 th6 th7 th8 th9 th10
- : : : : : : : : : : :
- : : : : : : : : : : :
- r0 ........:..:..:...___________...:..:..:......:......:..
- : : : /: : :\ : : : : :
- : : : / : : : \ : : : : :
- : : :/ : : : \: : : : :
- r1 ........:.....@...:....:....:...@..:..:......:......:..
- : : @: : : : :@ : : : :
- (R) ...........:...@.:...:....:....:...:.@..........:......:......
- : :@ : : : : : @: : : :
- r2 ........:..@..:...:....:....:...:..@:........:......:..
- : /: : : : : : :\ : : :
- :/ : : : : : : : \: : : /
- r3 ......__/..:..:...:....:....:...:..:..\______________/
- : : : : : : : : : : :
- | : : : : : : : | : :
- : : : : : : : : : : :
- | : : : : : : : | : :
- r4 ......__:_____________________________:________________
- */
-
- GLfloat r[20];
- GLfloat th[20];
- GLfloat R = g->r;
-
- r[0] = R + (rh * 0.5);
- r[1] = R + (rh * 0.25);
- r[2] = R - (r[1]-R);
- r[3] = R - (r[0]-R);
- r[4] = g->inner_r;
-
- th[0] = -tw * (g->size == SMALL ? 0.5 : g->size == MEDIUM ? 0.41 : 0.45);
- th[1] = -tw * 0.30;
- th[2] = -tw * (g->nteeth >= 5 ? 0.16 : 0.12);
- th[3] = -tw * (g->size == MEDIUM ? 0.1 : 0.04);
- th[4] = 0;
- th[5] = -th[3];
- th[6] = -th[2];
- th[7] = -th[1];
- th[8] = -th[0];
- th[9] = width / 2;
- th[10]= th[0] + width;
-
- orim->npoints = 0;
- orim->points = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->points));
- orim->fnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->fnormals));
- orim->pnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->pnormals));
-
- irim->npoints = 0;
- irim->points = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->points));
- irim->fnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->fnormals));
- irim->pnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->pnormals));
-
- if (!orim->points || !orim->pnormals || !orim->fnormals ||
- !irim->points || !irim->pnormals || !irim->fnormals)
- {
- fprintf (stderr, "%s: out of memory\n", progname);
- exit (1);
- }
-
- /* First, compute the coordinates of every point used for every tooth.
- */
- for (i = 0; i < g->nteeth; i++)
- {
- GLfloat TH = (i * width) + (width/4);
-
-# undef PUSH
-# define PUSH(OPR,IPR,PTH) \
- orim->points[orim->npoints].x = cos(TH+th[(PTH)]) * r[(OPR)]; \
- orim->points[orim->npoints].y = sin(TH+th[(PTH)]) * r[(OPR)]; \
- orim->npoints++; \
- irim->points[irim->npoints].x = cos(TH+th[(PTH)]) * r[(IPR)]; \
- irim->points[irim->npoints].y = sin(TH+th[(PTH)]) * r[(IPR)]; \
- irim->npoints++
-
- if (g->size == SMALL)
- {
- PUSH(3, 4, 0); /* tooth left 1 */
- PUSH(0, 4, 4); /* tooth top middle */
- }
- else if (g->size == MEDIUM)
- {
- PUSH(3, 4, 0); /* tooth left 1 */
- PUSH(0, 4, 3); /* tooth top left */
- PUSH(0, 4, 5); /* tooth top right */
- PUSH(3, 4, 8); /* tooth right 3 */
- }
- else if (g->size == LARGE)
- {
- PUSH(3, 4, 0); /* tooth left 1 */
- PUSH(2, 4, 1); /* tooth left 2 */
- PUSH(1, 4, 2); /* tooth left 3 */
- PUSH(0, 4, 3); /* tooth top left */
- PUSH(0, 4, 5); /* tooth top right */
- PUSH(1, 4, 6); /* tooth right 1 */
- PUSH(2, 4, 7); /* tooth right 2 */
- PUSH(3, 4, 8); /* tooth right 3 */
- PUSH(3, 4, 9); /* gap top */
- }
- else
- abort();
-# undef PUSH
-
- if (i == 0 && orim->npoints > ppt) abort(); /* go update "ppt"! */
- }
-
- tooth_normals (orim);
- tooth_normals (irim);
-}
-
-
-/* Renders all teeth of a gear.
- */
-static int
-draw_gear_teeth (ModeInfo *mi, gear *g)
-{
- Bool wire_p = MI_IS_WIREFRAME(mi);
- Bool show_normals_p = False;
- int polys = 0;
- int i;
-
- GLfloat z1 = -g->thickness/2;
- GLfloat z2 = g->thickness/2;
-
- tooth_face orim, irim;
- gear_teeth_geometry (mi, g, &orim, &irim);
-
- glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
-
- /* Draw the outer rim (the teeth)
- (In wire mode, this draws just the upright lines.)
- */
- glFrontFace (GL_CW);
- glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
- for (i = 0; i < orim.npoints; i++)
- {
- glNormal3f (orim.pnormals[i].x, orim.pnormals[i].y, orim.pnormals[i].z);
- glVertex3f (orim.points[i].x, orim.points[i].y, z1);
- glVertex3f (orim.points[i].x, orim.points[i].y, z2);
-
- /* Show the face normal vectors */
- if (wire_p && show_normals_p)
- {
- XYZ n = orim.fnormals[i];
- int a = i;
- int b = (i == orim.npoints-1 ? 0 : i+1);
- GLfloat x = (orim.points[a].x + orim.points[b].x) / 2;
- GLfloat y = (orim.points[a].y + orim.points[b].y) / 2;
- GLfloat z = (z1 + z2) / 2;
- glVertex3f (x, y, z);
- glVertex3f (x + n.x, y + n.y, z);
- }
-
- /* Show the vertex normal vectors */
- if (wire_p && show_normals_p)
- {
- XYZ n = orim.pnormals[i];
- GLfloat x = orim.points[i].x;
- GLfloat y = orim.points[i].y;
- GLfloat z = (z1 + z2) / 2;
- glVertex3f (x, y, z);
- glVertex3f (x + n.x, y + n.y, z);
- }
- }
-
- if (!wire_p) /* close the quad loop */
- {
- glNormal3f (orim.pnormals[0].x, orim.pnormals[0].y, orim.pnormals[0].z);
- glVertex3f (orim.points[0].x, orim.points[0].y, z1);
- glVertex3f (orim.points[0].x, orim.points[0].y, z2);
- }
- polys += orim.npoints;
- glEnd();
-
- /* Draw the outer rim circles, in wire mode */
- if (wire_p)
- {
- glBegin (GL_LINE_LOOP);
- for (i = 0; i < orim.npoints; i++)
- glVertex3f (orim.points[i].x, orim.points[i].y, z1);
- glEnd();
- glBegin (GL_LINE_LOOP);
- for (i = 0; i < orim.npoints; i++)
- glVertex3f (orim.points[i].x, orim.points[i].y, z2);
- glEnd();
- }
-
-
- /* Draw the inner rim (the hole)
- (In wire mode, this draws just the upright lines.)
- */
- glFrontFace (GL_CCW);
- glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
- for (i = 0; i < irim.npoints; i++)
- {
- glNormal3f(-irim.pnormals[i].x, -irim.pnormals[i].y,-irim.pnormals[i].z);
- glVertex3f (irim.points[i].x, irim.points[i].y, z1);
- glVertex3f (irim.points[i].x, irim.points[i].y, z2);
-
- /* Show the face normal vectors */
- if (wire_p && show_normals_p)
- {
- XYZ n = irim.fnormals[i];
- int a = i;
- int b = (i == irim.npoints-1 ? 0 : i+1);
- GLfloat x = (irim.points[a].x + irim.points[b].x) / 2;
- GLfloat y = (irim.points[a].y + irim.points[b].y) / 2;
- GLfloat z = (z1 + z2) / 2;
- glVertex3f (x, y, z);
- glVertex3f (x - n.x, y - n.y, z);
- }
-
- /* Show the vertex normal vectors */
- if (wire_p && show_normals_p)
- {
- XYZ n = irim.pnormals[i];
- GLfloat x = irim.points[i].x;
- GLfloat y = irim.points[i].y;
- GLfloat z = (z1 + z2) / 2;
- glVertex3f (x, y, z);
- glVertex3f (x - n.x, y - n.y, z);
- }
- }
-
- if (!wire_p) /* close the quad loop */
- {
- glNormal3f (-irim.pnormals[0].x,-irim.pnormals[0].y,-irim.pnormals[0].z);
- glVertex3f (irim.points[0].x, irim.points[0].y, z1);
- glVertex3f (irim.points[0].x, irim.points[0].y, z2);
- }
- polys += irim.npoints;
- glEnd();
-
- /* Draw the inner rim circles, in wire mode
- */
- if (wire_p)
- {
- glBegin (GL_LINE_LOOP);
- for (i = 0; i < irim.npoints; i++)
- glVertex3f (irim.points[i].x, irim.points[i].y, z1);
- glEnd();
- glBegin (GL_LINE_LOOP);
- for (i = 0; i < irim.npoints; i++)
- glVertex3f (irim.points[i].x, irim.points[i].y, z2);
- glEnd();
- }
-
-
- /* Draw the side (the flat bit)
- */
- if (!wire_p || wire_all_p)
- {
- GLfloat z;
- if (irim.npoints != orim.npoints) abort();
- for (z = z1; z <= z2; z += z2-z1)
- {
- glFrontFace (z == z1 ? GL_CCW : GL_CW);
- glNormal3f (0, 0, z);
- glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
- for (i = 0; i < orim.npoints; i++)
- {
- glVertex3f (orim.points[i].x, orim.points[i].y, z);
- glVertex3f (irim.points[i].x, irim.points[i].y, z);
- }
- if (!wire_p) /* close the quad loop */
- {
- glVertex3f (orim.points[0].x, orim.points[0].y, z);
- glVertex3f (irim.points[0].x, irim.points[0].y, z);
- }
- polys += orim.npoints;
- glEnd();
- }
- }
-
- free (irim.points);
- free (irim.fnormals);
- free (irim.pnormals);
-
- free (orim.points);
- free (orim.fnormals);
- free (orim.pnormals);
-
- return polys;
-}
-
-
-/* Render one gear, unrotated at 0,0.
- */
-static int
-draw_gear_1 (ModeInfo *mi, gear *g)
-{
- Bool wire_p = MI_IS_WIREFRAME(mi);
- int polys = 0;
-
- static GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
- static GLfloat shiny = 128.0;
-
- glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
- glMateriali (GL_FRONT, GL_SHININESS, shiny);
- glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
- glColor3f (g->color[0], g->color[1], g->color[2]);
-
- if (debug_p && wire_p)
- polys += draw_gear_schematic (mi, g);
- else
- {
- glPushMatrix();
- glRotatef (g->wobble, 1, 0, 0);
- polys += draw_gear_teeth (mi, g);
- polys += draw_gear_interior (mi, g);
- polys += draw_gear_nubs (mi, g);
- glPopMatrix();
- }
- return polys;
-}
-
-
/* Render one gear in the proper position, creating the gear's
display list first if necessary.
*/
static void
draw_gear (ModeInfo *mi, int which)
{
+ Bool wire_p = MI_IS_WIREFRAME(mi);
pinion_configuration *pp = &pps[MI_SCREEN(mi)];
gear *g = pp->gears[which];
GLfloat th;
}
glNewList (g->dlist, GL_COMPILE);
- g->polygons = draw_gear_1 (mi, g);
+ g->polygons = draw_involute_gear (g, (wire_p && debug_p ? 2 : wire_p));
glEndList ();
}
glPushName (g->id);
if (! visible_p)
- mi->polygon_count += draw_gear_schematic (mi, g);
+ mi->polygon_count += draw_involute_schematic (g, wire_p);
else
{
glCallList (g->dlist);
/* draw a line connecting gears that are, uh, geared. */
if (debug_p)
{
- static GLfloat color[4] = {1.0, 0.0, 0.0, 1.0};
+ static const GLfloat color[4] = {1.0, 0.0, 0.0, 1.0};
GLfloat off = 0.1;
GLfloat ox=0, oy=0, oz=0;
/* Mouse hit detection
*/
-void
+static void
find_mouse_gear (ModeInfo *mi)
{
pinion_configuration *pp = &pps[MI_SCREEN(mi)];
/* Window management, etc
*/
-void
+ENTRYPOINT void
reshape_pinion (ModeInfo *mi, int width, int height)
{
GLfloat h = (GLfloat) height / (GLfloat) width;
}
-Bool
+ENTRYPOINT Bool
pinion_handle_event (ModeInfo *mi, XEvent *event)
{
pinion_configuration *pp = &pps[MI_SCREEN(mi)];
}
else if (event->xany.type == ButtonPress &&
(event->xbutton.button == Button4 ||
- event->xbutton.button == Button5))
+ event->xbutton.button == Button5 ||
+ event->xbutton.button == Button6 ||
+ event->xbutton.button == Button7))
{
gltrackball_mousewheel (pp->trackball, event->xbutton.button, 5,
!!event->xbutton.state);
}
-void
+ENTRYPOINT void
init_pinion (ModeInfo *mi)
{
pinion_configuration *pp;
fprintf(stderr, "%s: out of memory\n", progname);
exit(1);
}
-
- pp = &pps[MI_SCREEN(mi)];
}
pp = &pps[MI_SCREEN(mi)];
load_fonts (mi);
reshape_pinion (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+ clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
pp->title_list = glGenLists (1);
pp->gears_size = 0;
pp->gears = 0;
- plane_displacement *= gear_size;
+ pp->plane_displacement = gear_size * 0.1;
if (!wire)
{
}
-void
+ENTRYPOINT void
draw_pinion (ModeInfo *mi)
{
pinion_configuration *pp = &pps[MI_SCREEN(mi)];
Display *dpy = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
Bool wire_p = MI_IS_WIREFRAME(mi);
- static int tick = 0;
if (!pp->glx_context)
return;
+ glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(pp->glx_context));
+
if (!pp->button_down_p)
{
if (!debug_one_gear_p || pp->ngears == 0)
if (!wire_p) glEnable(GL_LIGHTING);
}
- if (tick++ > 10) /* only do this every N frames */
+ if (pp->draw_tick++ > 10) /* only do this every N frames */
{
- tick = 0;
+ pp->draw_tick = 0;
find_mouse_gear (mi);
new_label (mi);
}
glXSwapBuffers(dpy, window);
}
+XSCREENSAVER_MODULE ("Pinion", pinion)
+
#endif /* USE_GL */