/* Create and return a new gear sized for placement next to or on top of the given parent gear (if any.) Returns 0 if out of memory. */ 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; /* 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 (loop_count > 1000) /* The only time we loop in here is when making a coaxial gear, and trying to pick a radius that is either significantly larger or smaller than its parent. That shouldn't be hard, so something must be really wrong if we can't do that in only a few tries. */ abort(); /* Pick the size of the teeth. */ if (parent && !coaxial_p) /* adjascent gears need matching teeth */ { g->tooth_w = parent->tooth_w; g->tooth_h = parent->tooth_h; g->thickness = parent->thickness; g->thickness2 = parent->thickness2; g->thickness3 = parent->thickness3; } else /* gears that begin trains get any size they want */ { double scale = (1.0 + BELLRAND(4.0)) * gear_size; g->tooth_w = 0.007 * scale; g->tooth_h = 0.005 * scale; g->thickness = g->tooth_h * (0.1 + BELLRAND(1.5)); g->thickness2 = g->thickness / 4; g->thickness3 = g->thickness; } /* Pick the number of teeth, and thus, the radius. */ { double c; AGAIN: g->nteeth = 3 + (random() % 97); /* from 3 to 100 teeth */ if (g->nteeth < 7 && (random() % 5) != 0) goto AGAIN; /* Let's make very small tooth-counts more rare */ c = g->nteeth * g->tooth_w * 2; /* circumference = teeth + gaps */ g->r = c / (M_PI * 2); /* c = 2 pi r */ } /* Are we done now? */ if (! coaxial_p) break; /* yes */ if (g->nteeth == parent->nteeth) continue; /* ugly */ if (g->r < parent->r * 0.6) break; /* g much smaller than parent */ 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); g->color[1] = 0.5 + frand(0.5); g->color[2] = 0.5 + frand(0.5); g->color[3] = 1.0; g->color2[0] = g->color[0] * 0.85; g->color2[1] = g->color[1] * 0.85; g->color2[2] = g->color[2] * 0.85; g->color2[3] = g->color[3]; /* Decide on shape of gear interior: - just a ring with teeth; - that, plus a thinner in-set "plate" in the middle; - that, plus a thin raised "lip" on the inner plate; - or, a wide lip (really, a thicker third inner plate.) */ if ((random() % 10) == 0) { /* inner_r can go all the way in; there's no inset disc. */ g->inner_r = (g->r * 0.1) + frand((g->r - g->tooth_h/2) * 0.8); g->inner_r2 = 0; g->inner_r3 = 0; } else { /* inner_r doesn't go in very far; inner_r2 is an inset disc. */ g->inner_r = (g->r * 0.5) + frand((g->r - g->tooth_h) * 0.4); g->inner_r2 = (g->r * 0.1) + frand(g->inner_r * 0.5); g->inner_r3 = 0; if (g->inner_r2 > (g->r * 0.2)) { int nn = (random() % 10); if (nn <= 2) g->inner_r3 = (g->r * 0.1) + frand(g->inner_r2 * 0.2); else if (nn <= 7 && g->inner_r2 >= 0.1) g->inner_r3 = g->inner_r2 - 0.01; } } /* Coaxial gears need to have the same innermost hole size (for the axle.) Use whichever of the two is smaller. (Modifies parent.) */ if (coaxial_p) { double hole1 = (g->inner_r3 ? g->inner_r3 : g->inner_r2 ? g->inner_r2 : g->inner_r); double hole2 = (parent->inner_r3 ? parent->inner_r3 : parent->inner_r2 ? parent->inner_r2 : parent->inner_r); double hole = (hole1 < hole2 ? hole1 : hole2); if (hole <= 0) abort(); if (g->inner_r3) g->inner_r3 = hole; else if (g->inner_r2) g->inner_r2 = hole; else g->inner_r = hole; if (parent->inner_r3) parent->inner_r3 = hole; else if (parent->inner_r2) parent->inner_r2 = hole; else parent->inner_r = hole; } /* If we have three discs, sometimes make the middle disc be spokes. */ if (g->inner_r3 && ((random() % 5) == 0)) { g->spokes = 2 + BELLRAND (5); g->spoke_thickness = 1 + frand(7.0); if (g->spokes == 2 && g->spoke_thickness < 2) g->spoke_thickness += 1; } /* Sometimes add little nubbly bits, if there is room. */ if (g->nteeth > 5) { double size = 0; involute_biggest_ring (g, 0, &size, 0); if (size > g->r * 0.2 && (random() % 5) == 0) { g->nubs = 1 + (random() % 16); if (g->nubs > 8) g->nubs = 1; } } if (g->inner_r3 > g->inner_r2) abort(); if (g->inner_r2 > g->inner_r) abort(); if (g->inner_r > g->r) abort(); /* Decide how complex the polygon model should be. */ { double pix = g->tooth_h * MI_HEIGHT(mi); /* approx. tooth size in pixels */ 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; return g; }
/* Create and return a new gear sized for placement next to or on top of the given parent gear (if any.) Returns 0 if out of memory. [Mostly lifted from pinion.c] */ static gear * new_gear (ModeInfo *mi, gear *parent) { gears_configuration *bp = &bps[MI_SCREEN(mi)]; gear *g = (gear *) calloc (1, sizeof (*g)); static unsigned long id = 0; /* only used in debugging output */ if (!g) return 0; g->id = ++id; /* Pick the size of the teeth. */ if (parent) /* adjascent gears need matching teeth */ { g->tooth_w = parent->tooth_w; g->tooth_h = parent->tooth_h; g->tooth_slope = -parent->tooth_slope; } else /* gears that begin trains get any size they want */ { g->tooth_w = 0.007 * (1.0 + BELLRAND(4.0)); g->tooth_h = 0.005 * (1.0 + BELLRAND(8.0)); /* g->tooth_slope = ((random() % 8) ? 0 : 0.5 + BELLRAND(1)); */ } /* Pick the number of teeth, and thus, the radius. */ { double c; if (!parent || bp->ngears > 4) g->nteeth = 5 + BELLRAND (20); else g->nteeth = parent->nteeth * (0.5 + BELLRAND(2)); c = g->nteeth * g->tooth_w * 2; /* circumference = teeth + gaps */ g->r = c / (M_PI * 2); /* c = 2 pi r */ } g->thickness = g->tooth_w + frand (g->r); g->thickness2 = g->thickness * 0.7; g->thickness3 = g->thickness; /* Colorize */ g->color[0] = 0.5 + frand(0.5); g->color[1] = 0.5 + frand(0.5); g->color[2] = 0.5 + frand(0.5); g->color[3] = 1.0; g->color2[0] = g->color[0] * 0.85; g->color2[1] = g->color[1] * 0.85; g->color2[2] = g->color[2] * 0.85; g->color2[3] = g->color[3]; /* Decide on shape of gear interior: - just a ring with teeth; - that, plus a thinner in-set "plate" in the middle; - that, plus a thin raised "lip" on the inner plate; - or, a wide lip (really, a thicker third inner plate.) */ if ((random() % 10) == 0) { /* inner_r can go all the way in; there's no inset disc. */ g->inner_r = (g->r * 0.1) + frand((g->r - g->tooth_h/2) * 0.8); g->inner_r2 = 0; g->inner_r3 = 0; } else { /* inner_r doesn't go in very far; inner_r2 is an inset disc. */ g->inner_r = (g->r * 0.5) + frand((g->r - g->tooth_h) * 0.4); g->inner_r2 = (g->r * 0.1) + frand(g->inner_r * 0.5); g->inner_r3 = 0; if (g->inner_r2 > (g->r * 0.2)) { int nn = (random() % 10); if (nn <= 2) g->inner_r3 = (g->r * 0.1) + frand(g->inner_r2 * 0.2); else if (nn <= 7 && g->inner_r2 >= 0.1) g->inner_r3 = g->inner_r2 - 0.01; } } /* If we have three discs, sometimes make the middle disc be spokes. */ if (g->inner_r3 && ((random() % 5) == 0)) { g->spokes = 2 + BELLRAND (5); g->spoke_thickness = 1 + frand(7.0); if (g->spokes == 2 && g->spoke_thickness < 2) g->spoke_thickness += 1; } /* Sometimes add little nubbly bits, if there is room. */ if (g->nteeth > 5) { double size = 0; involute_biggest_ring (g, 0, &size, 0); if (size > g->r * 0.2 && (random() % 5) == 0) { g->nubs = 1 + (random() % 16); if (g->nubs > 8) g->nubs = 1; } } if (g->inner_r3 > g->inner_r2) abort(); if (g->inner_r2 > g->inner_r) abort(); if (g->inner_r > g->r) abort(); /* Decide how complex the polygon model should be. */ { double pix = g->tooth_h * MI_HEIGHT(mi); /* approx. tooth size in pixels */ 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; return g; }