+/* Upcase string, convert Unicrud to ASCII, remove any non-letters.
+ */
+static char *
+codeword_simplify_text (const char *s0)
+{
+ char *s1 = utf8_to_latin1 ((s0 ? s0 : ""), True);
+ int L = strlen(s1);
+ char *s2 = (char *) malloc (L + 10);
+ char *s3 = s2;
+ int i;
+ for (i = 0; i < L; i++)
+ {
+ char c = s1[i];
+ if (c >= 'a' && c <= 'z')
+ c -= 'a'-'A';
+ if (c >= 'A' && c <= 'Z')
+ *s3++ = c;
+ }
+ *s3 = 0;
+ if (! *s2)
+ strcpy (s2, "CODEWORD");
+ return s2;
+}
+
+
+static void
+make_codeword_path (ModeInfo *mi)
+{
+ logo_configuration *dc = &dcs[MI_SCREEN(mi)];
+ int letters = strlen (dc->codeword_text);
+
+ GLfloat rtick = dc->codeword_spread;
+ GLfloat iradius = rtick * dc->codeword_cap_size;
+
+ int dial = 0;
+ int letter;
+ GLfloat last_r;
+
+ GLfloat inner_circum = M_PI * 2 * (iradius + rtick * 2);
+ GLfloat outer_circum = M_PI * 2 * (iradius + rtick * (letters + 1));
+ GLfloat facet_length = inner_circum / (26 * dc->codeword_facets);
+ int outer_facets = ceil (outer_circum / facet_length);
+
+ int *histo = (int *) calloc (letters * 26, sizeof(*histo));
+ XYZ *points = (XYZ *) calloc (letters * outer_facets, sizeof (*points));
+ int npoints = 0;
+
+ for (letter = -1; letter < letters; letter++)
+ {
+ if (letter == -1) /* Inner starting point */
+ {
+ points[npoints].x = iradius;
+ points[npoints].y = 0;
+ last_r = iradius;
+ npoints++;
+ }
+ else /* Add arc for this letter */
+ {
+ int direction = (letter & 1 ? -1 : 1);
+ int v = (dc->codeword_text[letter] - 'A' + 1);
+ int dial1 = dial + v * direction;
+
+ GLfloat th;
+ GLfloat th0 = M_PI * 2 / 26 * dial;
+ GLfloat th1 = M_PI * 2 / 26 * dial1;
+ GLfloat r = iradius + rtick * (letter + 2);
+ GLfloat circum = M_PI * 2 * r;
+ GLfloat arc_length = circum * v / 26;
+ int arc_facets = ceil (fabs (arc_length / facet_length));
+ GLfloat facet_th = (th1 - th0) / arc_facets;
+
+ if (arc_facets > outer_facets) abort();
+
+ /* Let's put some intermediate facets on the crossbars too,
+ so that the animation doesn't speed up on those. */
+ {
+ GLfloat rr;
+ for (rr = last_r + facet_length;
+ rr <= r - facet_length;
+ rr += facet_length)
+ {
+ points[npoints].x = rr * cos (th0);
+ points[npoints].y = rr * sin (th0);
+ npoints++;
+ }
+ last_r = r;
+ }
+
+
+ for (th = th0;
+ (th0 < th1
+ ? th <= th1 + facet_th
+ : th >= th1 + facet_th);
+ th += facet_th)
+ {
+ GLfloat th2 = th;
+ if (th0 < th1 && th > th1)
+ th2 = th1;
+ if (th0 > th1 && th < th1)
+ th2 = th1;
+ points[npoints].x = r * cos (th2);
+ points[npoints].y = r * sin (th2);
+
+ /* Ugh, add point only if it differs from prev.
+ Not sure how this happens. */
+ if (npoints == 0 ||
+ points[npoints-1].x != points[npoints].x ||
+ points[npoints-1].y != points[npoints].y)
+ npoints++;
+ }
+
+ /* Mark up the histo array to find the outer border. */
+ {
+ int i;
+ for (i = dial;
+ (direction > 0
+ ? i <= dial1
+ : i >= dial1);
+ i += direction)
+ {
+ int x = (i + 26) % 26;
+ int y;
+ for (y = 0; y <= letter; y++)
+ histo[y * 26 + x]++;
+ }
+ }
+
+ dc->codeword_text_points[letter] = npoints;
+
+ dial = dial1;
+ }
+ }
+
+ if (npoints >= letters * outer_facets) abort();
+
+# if 0
+ { /* Print histo */
+ int x, y;
+ for (y = 0; y < letters; y++)
+ {
+ fprintf (stderr, "%2d: ", y);
+ for (x = 0; x < 26; x++)
+ fprintf (stderr, "%x", histo[y * 26 + x]);
+ fprintf (stderr, "\n");
+ }
+ fprintf (stderr, "\n");
+ }
+# endif
+
+
+ /* Find a gap in the outer edge, to draw guide dots. */
+ {
+ int x, y;
+ int last_row = letters;
+ int start_dial = -1, end_dial = -1;
+
+ for (y = letters-1; y >= 0; y--)
+ {
+ for (x = 0; x < 26; x++)
+ {
+ if (histo[y * 26 + x] == 0)
+ {
+ if (last_row != y)
+ start_dial = end_dial = -1;
+ last_row = y;
+ if (start_dial == -1)
+ start_dial = x;
+ end_dial = x;
+ }
+ }
+ }
+
+ if (last_row < letters-1 && start_dial >= 0)
+ {
+ GLfloat r = iradius + rtick * (last_row + 2);
+ int i;
+
+ dc->codeword_nguides = 0;
+ dc->codeword_guides = (XYZ *)
+ calloc (end_dial - start_dial + 1, sizeof (*dc->codeword_guides));
+ for (i = start_dial; i <= end_dial; i++)
+ {
+ GLfloat th = i * M_PI * 2 / 26;
+ GLfloat x = r * cos (th);
+ GLfloat y = r * sin (th);
+ dc->codeword_guides[dc->codeword_nguides].x = x;
+ dc->codeword_guides[dc->codeword_nguides].y = y;
+ dc->codeword_nguides++;
+ }
+ }
+ free (histo);
+ histo = 0;
+ }
+
+ dc->codeword_path_npoints = npoints;
+ dc->codeword_path = points;
+}
+
+
+static int
+draw_codeword_cap (ModeInfo *mi)
+{
+ logo_configuration *dc = &dcs[MI_SCREEN(mi)];
+ int wire = MI_IS_WIREFRAME(mi);
+ int polys = 0;
+
+ int segments = dc->codeword_disc_facets;
+ GLfloat size = dc->codeword_spread * dc->codeword_cap_size;
+ GLfloat width = dc->codeword_line_width / 2;
+ GLfloat thick = dc->codeword_thickness / 2;
+ GLfloat r1 = size + width/2;
+ GLfloat r2 = size - width/2;
+ GLfloat facet, th, z;
+
+ if (wire) segments = 12;
+ facet = M_PI * 2 / segments;
+
+ glPushMatrix();
+
+ /* Top and bottom */
+
+ for (z = -thick; z <= thick; z += thick*2)
+ {
+ glNormal3f (0, 0, (z < 0 ? -1 : 1));
+ glFrontFace (z < 0 ? GL_CCW : GL_CW);
+
+ glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
+ for (th = 0; th <= M_PI*2; th += facet)
+ {
+ GLfloat x = cos (th);
+ GLfloat y = sin (th);
+ glVertex3f (r1 * x, r1 * y, z);
+ glVertex3f (r2 * x, r2 * y, z);
+ }
+ glEnd();
+
+ if (wire)
+ {
+ glBegin (GL_LINE_LOOP);
+ for (th = 0; th <= M_PI*2; th += facet)
+ {
+ GLfloat x = cos (th);
+ GLfloat y = sin (th);
+ glVertex3f (r1 * x, r1 * y, z);
+ }
+ glEnd();
+ glBegin (GL_LINE_LOOP);
+ for (th = 0; th <= M_PI*2; th += facet)
+ {
+ GLfloat x = cos (th);
+ GLfloat y = sin (th);
+ glVertex3f (r2 * x, r2 * y, z);
+ }
+ glEnd();
+ }
+ }
+
+ /* Inside and outside */
+
+ for (z = -1; z <= 1; z += 2)
+ {
+ glFrontFace (z < 0 ? GL_CCW : GL_CW);
+
+ glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
+ for (th = 0; th <= M_PI*2; th += facet)
+ {
+ GLfloat th1 = th + facet;
+ GLfloat x0 = cos (th);
+ GLfloat y0 = sin (th);
+ GLfloat x1 = cos (th1);
+ GLfloat y1 = sin (th1);
+ GLfloat r = z < 0 ? r1 : r2;
+
+ if (z < 0)
+ do_normal (r * x0, r * y0, thick,
+ r * x0, r * y0, -thick,
+ r * x1, r * y1, -thick);
+ else
+ do_normal (r * x1, r * y1, thick,
+ r * x1, r * y1, -thick,
+ r * x0, r * y0, -thick);
+
+ glVertex3f (r * x0, r * y0, thick);
+ glVertex3f (r * x0, r * y0, -thick);
+ }
+ glEnd();
+ }
+
+ glPopMatrix();
+
+ return polys;
+}
+
+
+static int
+draw_codeword_guides (ModeInfo *mi, GLfloat anim_ratio)
+{
+ logo_configuration *dc = &dcs[MI_SCREEN(mi)];
+ int wire = MI_IS_WIREFRAME(mi);
+ int polys = 0;
+
+ int segments = dc->codeword_disc_facets;
+ GLfloat s = dc->codeword_line_width / 2;
+ GLfloat each = 1.0 / dc->codeword_nguides;
+ int i;
+
+ if (wire) segments = 6;
+
+ for (i = 0; i < dc->codeword_nguides; i++)
+ {
+ GLfloat ratio;
+ if (anim_ratio < i*each) ratio = 0;
+ else if (anim_ratio >= (i+1)*each) ratio = 1;
+ else ratio = (anim_ratio - i*each) / each;
+
+ if (ratio <= 0) continue;
+ if (ratio == 0) ratio = 0.001;
+
+ glPushMatrix();
+ glTranslatef (dc->codeword_guides[i].x,
+ dc->codeword_guides[i].y,
+ dc->codeword_guides[i].z);
+
+ glScalef (ratio, ratio, ratio);
+
+ /* If the line width and thickness are pretty close to each other,
+ use spheres. Otherwise use tubes.
+ */
+ if (dc->codeword_thickness < dc->codeword_line_width * 1.3 &&
+ dc->codeword_thickness > dc->codeword_line_width / 1.3)
+ {
+ glScalef (s, s, s);
+ glFrontFace (GL_CCW);
+ polys += unit_sphere (segments, segments, wire);
+ }
+ else
+ {
+ polys += tube (0, 0, -dc->codeword_thickness / 2,
+ 0, 0, dc->codeword_thickness / 2,
+ s, 0, segments, True, True, wire);
+ }
+
+ glPopMatrix();
+ }
+
+ return polys;
+}
+
+
+/* Compute the characters at which the cursor is currently pointing,
+ and render it on the logo.
+ */
+static void
+codeword_text_output (ModeInfo *mi, GLfloat anim_ratio)
+{
+ logo_configuration *dc = &dcs[MI_SCREEN(mi)];
+ int i;
+ int L = strlen (dc->codeword_text);
+ int point = dc->codeword_path_npoints * anim_ratio;
+ Bool hit = False;
+
+ if (dc->anim_state == CODEWORD_BLANK)
+ point = 0;
+
+ for (i = 0; i < L; i++)
+ {
+ if (point >= dc->codeword_text_points[i])
+ dc->codeword_text_out[i] = dc->codeword_text[i];
+ else if (hit)
+ dc->codeword_text_out[i] = 0;
+ else
+ {
+ int steps = dc->codeword_text[i] - 'A' + 1;
+ int last = (i > 0 ? dc->codeword_text_points[i-1] : 0);
+ double ratio = ((point - last) /
+ (double) (dc->codeword_text_points[i] - last));
+ char chr = 'A' + (ratio * steps);
+ if (ratio < 0.1) chr = 0;
+ dc->codeword_text_out[i] = chr;
+ hit = True;
+ }
+ }
+ dc->codeword_text_out[i] = 0;
+
+ if (*dc->codeword_text_out &&
+ !strcmp (dc->codeword_text, "CODEWORD"))
+ {
+ int i;
+ int L2 = strlen (dc->codeword_text_out);
+ GLfloat ss = 0.01;
+ int ascent, descent;
+
+ glPushMatrix();
+ glColor4fv (dc->codeword_color);
+ glRotatef (90, 0, 1, 0);
+ glRotatef (-90, 0, 0, 1);
+
+ for (i = 0; i < L2; i++)
+ {
+ XCharStruct e;
+ char buf[2];
+ glPushMatrix();
+ glRotatef ((i + 0.5) * 360 / 26.0, 0, 0, 1);
+
+# if 0
+ {
+ GLfloat th;
+ glDisable(GL_LIGHTING);
+ glBegin(GL_LINES);
+ glVertex3f(0,0,0);
+ glVertex3f(0,-4,0);
+ glEnd();
+ glBegin(GL_LINE_STRIP);
+ for (th = M_PI * 1.45; th < M_PI * 1.55; th += 0.1)
+ {
+ GLfloat r = 3.85;
+ glVertex3f (r * cos(th), r * sin(th), 0);
+ }
+ glEnd();
+ if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
+ }
+# endif
+
+ glTranslatef (0, -dc->codeword_spread * (L - 1), 0);
+ glScalef (ss, ss, ss);
+ buf[0] = dc->codeword_text_out[i] + ('a' - 'A');
+ buf[1] = 0;
+ texture_string_metrics (dc->font, buf, &e, &ascent, &descent);
+
+# ifdef USE_IPHONE
+ /* #### Magic magic magic WTF... */
+ glScalef (0.5, 0.5, 0.5);
+# endif
+
+ glTranslatef (-e.width * 1.0,
+ -(ascent + descent + e.descent * 2.4), /* #### WTF */
+ 0);
+
+ glScalef (2, 2, 2);
+
+# if 0
+ glDisable(GL_LIGHTING);
+ glBegin(GL_LINE_LOOP);
+ glVertex3f(0, 0, 0);
+ glVertex3f(e.width, 0, 0);
+ glVertex3f(e.width, e.ascent + e.descent, 0);
+ glVertex3f(0, e.ascent + e.descent, 0);
+ glEnd();
+ if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
+# endif
+
+ glDisable(GL_CULL_FACE); /* tell texfont.c to draw both sides */
+ print_texture_string (dc->font, buf);
+ glEnable(GL_CULL_FACE);
+
+ glPopMatrix();
+ }
+ glPopMatrix();
+ }
+}
+
+
+/* Convert the precomputed path to a thick line of polygons.
+ We could actually precompute all of these polygons too,
+ but it's fast enough.
+ */
+static int
+draw_codeword_path (ModeInfo *mi)
+{
+ logo_configuration *dc = &dcs[MI_SCREEN(mi)];
+ int wire = MI_IS_WIREFRAME(mi);
+ int polys = 0;
+
+ GLfloat anim_ratio = (dc->anim_state == CODEWORD_IN ? dc->anim_ratio :
+ dc->anim_state == CODEWORD_OUT ? 1 - dc->anim_ratio :
+ dc->anim_state == CODEWORD_BLANK ? 0 :
+ 1);
+ int last_anim_point = 0;
+
+ GLfloat width = dc->codeword_line_width / 2;
+ GLfloat thick = dc->codeword_thickness / 2;
+ int i, k;
+ GLfloat j;
+
+ int quad_size = (dc->codeword_path_npoints + 1) * 2;
+ XYZ *quads = (XYZ *) calloc (quad_size, sizeof(*quads));
+ XYZ *norms = (XYZ *) calloc (quad_size, sizeof(*norms));
+ int nquads = 0;
+
+ for (i = 0; i < dc->codeword_path_npoints; i++)
+ {
+ XYZ p1 = dc->codeword_path[i];
+ XYZ p2 = (i < dc->codeword_path_npoints-1
+ ? dc->codeword_path[i+1]
+ : dc->codeword_path[i-1]);
+ XYZ p1a, p1b;
+
+ XYZ n; /* normal of the first line segment */
+ n.x = -(p2.y - p1.y);
+ n.y = (p2.x - p1.x);
+ n.z = 0;
+ normalize (&n);
+
+ if (i == 0)
+ {
+ p1a.x = p1.x - width / 2 * n.x;
+ p1a.y = p1.y - width / 2 * n.y;
+ p1a.z = 0;
+
+ p1b.x = p1.x + width / 2 * n.x;
+ p1b.y = p1.y + width / 2 * n.y;
+ p1b.z = 0;
+ }
+ else if (i == dc->codeword_path_npoints - 1)
+ {
+ p1b.x = p1.x - width / 2 * n.x;
+ p1b.y = p1.y - width / 2 * n.y;
+ p1b.z = 0;
+
+ p1a.x = p1.x + width / 2 * n.x;
+ p1a.y = p1.y + width / 2 * n.y;
+ p1a.z = 0;
+ }
+ else
+ {
+ XYZ p0 = dc->codeword_path[i-1];
+
+ XYZ t, t0, t1; /* tangent of corner between two line segments */
+ XYZ m; /* miter line: normal of tangent */
+ GLfloat d; /* length of miter */
+
+ t0.x = p2.x - p1.x;
+ t0.y = p2.y - p1.y;
+ t0.z = p2.z - p1.z;
+ normalize (&t0);
+
+ t1.x = p1.x - p0.x;
+ t1.y = p1.y - p0.y;
+ t1.z = p1.z - p0.z;
+ normalize (&t1);
+
+ t.x = t0.x + t1.x;
+ t.y = t0.y + t1.y;
+ t.z = t0.z + t1.z;
+ normalize (&t);
+
+ m.x = -t.y;
+ m.y = t.x;
+ m.z = 0;
+
+ /* find length of miter by projecting it on one of the normals */
+ d = width / 2 / dot (m, n);
+
+ p1a.x = p1.x - d * m.x;
+ p1a.y = p1.y - d * m.y;
+ p1a.z = 0;
+
+ p1b.x = p1.x + d * m.x;
+ p1b.y = p1.y + d * m.y;
+ p1b.z = 0;
+ }
+
+ quads[nquads++] = p1a;
+ quads[nquads++] = p1b;
+
+ if (nquads >= quad_size) abort();
+
+ if (i / (double) dc->codeword_path_npoints > anim_ratio)
+ break;
+
+ last_anim_point = i;
+ }
+
+
+ /* Compute normals for each point along the interior edge */
+ for (k = 0; k <= 1; k++)
+ {
+ for (i = k; i < nquads-2; i += 2)
+ {
+ XYZ p1a = quads[i];
+ XYZ p2a = quads[i+2];
+ XYZ p1b = p1a;
+ XYZ p2b = p2a;
+ p1a.z = thick; /* a: top */
+ p1b.z = -thick; /* b: bottom */
+ p2a.z = thick;
+ p2b.z = -thick;
+
+ norms[i] = (k == 0
+ ? calc_normal (p1a, p1b, p2b)
+ : calc_normal (p2a, p2b, p1a));
+ }
+ }
+
+ glPushMatrix();
+ glColor4fv (dc->codeword_color);
+ glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, dc->codeword_color);
+
+# ifdef USE_IPHONE /* Make the whole thing fit on the phone screen */
+ {
+ GLfloat size = MI_WIDTH(mi) < MI_HEIGHT(mi) ? MI_WIDTH(mi) : MI_HEIGHT(mi);
+ glScalef (0.9, 0.9, 0.9);
+ if (size <= 768) /* iPad retina / iPhone 6 */
+ glScalef (0.7, 0.7, 0.7);
+ }
+# endif
+
+ codeword_text_output (mi, anim_ratio);
+
+ glRotatef (90, 1, 0, 0);
+ glRotatef (90, 0, 1, 0);
+ glRotatef (-90, 0, 0, 1);
+ glScalef (0.8, 0.8, 0.8);
+
+ glNormal3f (0, 0, -1);
+
+ if (anim_ratio <= 0)
+ {
+ polys += draw_codeword_cap (mi);
+ goto DONE;
+ }
+
+# if 0
+ glColor3f (1, 0, 0);
+ glBegin(GL_LINE_STRIP);
+ for (i = 0; i < dc->codeword_path_npoints; i++)
+ {
+ glVertex3f (dc->codeword_path[i].x,
+ dc->codeword_path[i].y,
+ dc->codeword_path[i].z);
+ polys++;
+ }
+ glEnd();
+ glColor4fv (dc->codeword_color);
+# endif
+
+ if (wire)
+ {
+ int k;
+ GLfloat j;
+ for (i = 0; i <= 1; i++)
+ for (j = -thick; j <= thick; j += thick*2)
+ {
+ glBegin (GL_LINE_STRIP);
+ for (k = i; k < nquads; k += 2)
+ {
+ glVertex3f (quads[k].x, quads[k].y, j);
+ polys++;
+ }
+ glEnd();
+ }
+ }
+
+ /* Top and bottom */
+
+ for (j = -thick; j <= thick; j += thick*2)
+ {
+ if (j < 0)
+ {
+ glNormal3f (0, 0, -1);
+ glFrontFace (GL_CW);
+ }
+ else
+ {
+ glNormal3f (0, 0, 1);
+ glFrontFace (GL_CCW);
+ }
+ glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
+ for (i = 0; i < nquads; i += 2)
+ {
+ glVertex3f (quads[i+1].x, quads[i+1].y, j);
+ glVertex3f (quads[i].x, quads[i].y, j);
+ polys++;
+ }
+ glEnd();
+ }
+
+ /* Edges */
+
+ for (k = 0; k <= 1; k++)
+ {
+ if (k > 0)
+ {
+ glNormal3f (0, 0, -1);
+ glFrontFace (GL_CW);
+ }
+ else
+ {
+ glNormal3f (0, 0, 1);
+ glFrontFace (GL_CCW);
+ }
+
+ glBegin (wire ? GL_LINES : GL_QUADS);
+ for (i = k; i < nquads; i += 2)
+ {
+ XYZ p1a = quads[i];
+ XYZ p2a = (i < nquads-2) ? quads[i+2] : p1a;
+ XYZ p1b = p1a;
+ XYZ p2b = p2a;
+
+ XYZ n1 = norms[i];
+ XYZ n2 = (i < nquads-2) ? norms[i+2] : n1;
+
+ /* If the two normals are very similar, smooth the face.
+ If they are different, it's a sharp turn, and use the
+ same normal for both edges (not quite right, but close).
+ */
+ GLfloat angle = vector_angle (n1.x, n1.y, n1.z,
+ n2.x, n2.y, n2.z);
+ GLfloat pointy = 0.8;
+
+ p1a.z = thick;
+ p1b.z = -thick;
+ p2a.z = thick;
+ p2b.z = -thick;
+
+ glNormal3f (n1.x, n1.y, n1.z);
+ glVertex3f (p1a.x, p1a.y, p1a.z);
+ glVertex3f (p1b.x, p1b.y, p1b.z);
+
+ if (angle < pointy)
+ glNormal3f (n2.x, n2.y, n2.z);
+ glVertex3f (p2b.x, p2b.y, p2b.z);
+ glVertex3f (p2a.x, p2a.y, p2a.z);
+ polys++;
+ }
+ glEnd();
+ }
+
+
+ /* Only draw the guides when the path is almost complete;
+ fade them in and out based on completeness. */
+ {
+ GLfloat size = 0.95;
+ GLfloat r = (anim_ratio > size
+ ? (anim_ratio - size) / (1 - size)
+ : 0);
+ polys += draw_codeword_guides (mi, r);
+ }
+
+
+ /* Draw the start and end caps */
+ {
+ int i;
+ GLfloat x, y, z, x2, y2, z2, X, Y, Z, L;
+ GLfloat r = dc->codeword_spread * dc->codeword_cap_size;
+
+ i = 0;
+ x = dc->codeword_path[i].x;
+ y = dc->codeword_path[i].y;
+ z = dc->codeword_path[i].z;
+
+ x -= r;
+
+ glPushMatrix();
+ glTranslatef (x, y, z);
+ polys += draw_codeword_cap (mi);
+ glPopMatrix();
+
+ /* end cap */
+
+ i = last_anim_point + 1;
+ if (i > dc->codeword_path_npoints - 1)
+ i = dc->codeword_path_npoints - 1;
+
+ x = dc->codeword_path[i].x;
+ y = dc->codeword_path[i].y;
+ z = dc->codeword_path[i].z;
+
+ i--;
+ x2 = dc->codeword_path[i].x;
+ y2 = dc->codeword_path[i].y;
+ z2 = dc->codeword_path[i].z;
+
+ X = (x2 - x);
+ Y = (y2 - y);
+ Z = (z2 - z);
+ L = sqrt (X*X + Y*Y + Z*Z);
+
+ glPushMatrix();
+ glTranslatef (x, y, z);
+ glRotatef (-atan2 (X, Y) * (180 / M_PI), 0, 0, 1);
+ glRotatef ( atan2 (Z, sqrt(X*X + Y*Y)) * (180 / M_PI), 1, 0, 0);
+ glTranslatef (0, -r, 0);
+ polys += draw_codeword_cap (mi);
+ glPopMatrix();
+ }
+
+ DONE:
+
+ glPopMatrix();
+
+ free (quads);
+ free (norms);
+
+ return polys;
+}
+