/*         ______   ___    ___
 *        /\  _  \ /\_ \  /\_ \
 *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
 *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
 *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
 *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
 *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
 *                                           /\____/
 *                                           \_/__/
 *
 *      Some high level routines, provided for user's convinience.
 *
 *
 *      By Pavel Sountsov.
 *
 *
 *      Bezier spline plotter By Seymour Shlien.
 *
 *      Optimised version by Sven Sandberg.
 *
 *      I'm not sure wether or not we still use the Castelau Algorithm
 *      described in the book :o)
 *
 *      Interactive Computer Graphics
 *      by Peter Burger and Duncan Gillies
 *      Addison-Wesley Publishing Co 1989
 *      ISBN 0-201-17439-1
 *
 *      The 4 th order Bezier curve is a cubic curve passing
 *      through the first and fourth point. The curve does
 *      not pass through the middle two points. They are merely
 *      guide points which control the shape of the curve. The
 *      curve is tangent to the lines joining points 1 and 2
 *      and points 3 and 4.
 *
 *      See readme.txt for copyright information.
 */


#define ALLEGRO_INTERNAL_UNSTABLE
#include "allegro5/allegro_primitives.h"
#ifdef ALLEGRO_CFG_OPENGL
#include "allegro5/allegro_opengl.h"
#endif
#include "allegro5/internal/aintern_bitmap.h"
#include "allegro5/debug.h"
#include <math.h>

ALLEGRO_DEBUG_CHANNEL("primitives")

#ifdef ALLEGRO_MSVC
   #define hypotf(x, y) _hypotf((x), (y))
#endif

#define LOCAL_VERTEX_CACHE  ALLEGRO_VERTEX vertex_cache[ALLEGRO_VERTEX_CACHE_SIZE]

/*
 * Make an estimate of the scale of the current transformation. 
 * We do this by computing the determinants of the 2D section of the transformation matrix.
 */
static float get_scale(void)
{
#define DET2D(T) (fabs((T)->m[0][0] * (T)->m[1][1] - (T)->m[0][1] * (T)->m[1][0]))

   const ALLEGRO_TRANSFORM* t = al_get_current_transform();
   float scale_sq = DET2D(t);
   ALLEGRO_BITMAP* b = al_get_target_bitmap();
   if (b) {
      const ALLEGRO_TRANSFORM* p = al_get_current_projection_transform();
      /* Divide by 4.0f as the screen coordinates range from -1 to 1 on both axes. */
      scale_sq *= DET2D(p) * al_get_bitmap_width(b) * al_get_bitmap_height(b) / 4.0f;
   }

   return sqrtf(scale_sq);

#undef DET2D
}

/* Function: al_draw_line
 */
void al_draw_line(float x1, float y1, float x2, float y2,
   ALLEGRO_COLOR color, float thickness)
{
   if (thickness > 0) {
      int ii;
      float tx, ty;
      float len = hypotf(x2 - x1, y2 - y1);
      
      ALLEGRO_VERTEX vtx[4];

      if (len == 0)
         return;

      tx = 0.5f * thickness * (y2 - y1) / len;
      ty = 0.5f * thickness * -(x2 - x1) / len;
            
      vtx[0].x = x1 + tx; vtx[0].y = y1 + ty;
      vtx[1].x = x1 - tx; vtx[1].y =  y1 - ty;
      vtx[2].x = x2 - tx; vtx[2].y = y2 - ty;
      vtx[3].x = x2 + tx; vtx[3].y = y2 + ty;
      
      for (ii = 0; ii < 4; ii++) {
         vtx[ii].color = color;
         vtx[ii].z = 0;
      }
      
      al_draw_prim(vtx, 0, 0, 0, 4, ALLEGRO_PRIM_TRIANGLE_FAN);
      
   } else {
      ALLEGRO_VERTEX vtx[2];
         
      vtx[0].x = x1; vtx[0].y = y1;
      vtx[1].x = x2; vtx[1].y = y2;
      
      vtx[0].color = color;
      vtx[1].color = color;
      vtx[0].z = 0;
      vtx[1].z = 0;
      
      al_draw_prim(vtx, 0, 0, 0, 2, ALLEGRO_PRIM_LINE_LIST);
   }
}

/* Function: al_draw_triangle
 */
void al_draw_triangle(float x1, float y1, float x2, float y2,
   float x3, float y3, ALLEGRO_COLOR color, float thickness)
{
   if (thickness > 0) {
      int ii = 0;
      float side1, side2, side3;      
      float perimeter, semi_perimeter;
      float outer_frac, inner_frac;
      float incenter_x, incenter_y;
      float incircle_rad;
      int idx = 0;
      ALLEGRO_VERTEX vtx[5];
      float x[3] = {x1, x2, x3};
      float y[3] = {y1, y2, y3};
      ALLEGRO_VERTEX first_inner_vtx;
      ALLEGRO_VERTEX first_outer_vtx;
      ALLEGRO_VERTEX ini_vtx;
      float cross = (x[1] - x[0]) * (y[2] - y[0]) - (x[2] - x[0]) * (y[1] - y[0]);
      
      ini_vtx.x = ini_vtx.y = ini_vtx.z = ini_vtx.u = ini_vtx.v = 0;
      ini_vtx.color = color;
      first_inner_vtx = ini_vtx;
      first_outer_vtx = ini_vtx;
      
      /*
       * If the triangle is very flat, draw it as a line
       */
      if(fabsf(cross) < 0.0001f) {
         float tx, ty, lx, ly;
         float len;
         /*
          * Find the obtuse vertex via two dot products
          */
         float dot = (x[1] - x[0]) * (x[2] - x[0]) + (y[1] - y[0]) * (y[2] - y[0]);
         if(dot < 0) {
            x1 = x[1]; y1 = y[1];
            x2 = x[2]; y2 = y[2];
         } else {
            dot = (x[0] - x[1]) * (x[2] - x[1]) + (y[0] - y[1]) * (y[2] - y[1]);
            if(dot < 0) {
               x1 = x[0]; y1 = y[0];
               x2 = x[2]; y2 = y[2];
            } else {
               x1 = x[0]; y1 = y[0];
               x2 = x[1]; y2 = y[1];
            }
         }
         len = hypotf(x2 - x1, y2 - y1);

         if (len == 0)
            return;

         tx = 0.5f * thickness * (y2 - y1) / len;
         ty = 0.5f * thickness * -(x2 - x1) / len;
         lx = 0.5f * thickness * (x2 - x1) / len;
         ly = 0.5f * thickness * (y2 - y1) / len;
               
         vtx[0].x = x1 + tx - lx; vtx[0].y = y1 + ty - ly;
         vtx[1].x = x1 - tx - lx; vtx[1].y = y1 - ty - ly;
         vtx[2].x = x2 - tx + lx; vtx[2].y = y2 - ty + ly;
         vtx[3].x = x2 + tx + lx; vtx[3].y = y2 + ty + ly;
         
         for (ii = 0; ii < 4; ii++) {
            vtx[ii].color = color;
            vtx[ii].z = 0;
         }
         
         al_draw_prim(vtx, 0, 0, 0, 4, ALLEGRO_PRIM_TRIANGLE_FAN);
         return;
      }
      else if(cross > 0) {
         /*
          * Points need to be wound correctly for the algorithm to work
          */
         float t;
         t = x[1];
         x[1] = x[2];
         x[2] = t;
         
         t = y[1];
         y[1] = y[2];
         y[2] = t;
      }
  
      side1 = hypotf(x[1] - x[0], y[1] - y[0]);
      side2 = hypotf(x[2] - x[0], y[2] - y[0]);
      side3 = hypotf(x[2] - x[1], y[2] - y[1]);

      perimeter = side1 + side2 + side3;
      semi_perimeter = perimeter / 2.0f;
      if (semi_perimeter < 0.00001f)
         return;

      incircle_rad = sqrtf((semi_perimeter - side1) * (semi_perimeter - side2) * (semi_perimeter - side3) / semi_perimeter);

      if (incircle_rad < 0.00001f)
         return;

      outer_frac = (incircle_rad + thickness / 2) / incircle_rad;
      inner_frac = (incircle_rad - thickness / 2) / incircle_rad;
      
      if(inner_frac < 0)
         inner_frac = 0;

      incenter_x = (side1 * x[2] + side2 * x[1] + side3 * x[0]) / perimeter;
      incenter_y = (side1 * y[2] + side2 * y[1] + side3 * y[0]) / perimeter;

      #define DRAW                                                         \
            if(ii != 0) {                                                  \
               vtx[idx++] = outer_vtx;                                     \
               vtx[idx++] = inner_vtx;                                     \
                                                                           \
               al_draw_prim(vtx, 0, 0, 0, idx, ALLEGRO_PRIM_TRIANGLE_FAN); \
                                                                           \
               idx = 0;                                                    \
            }

      /*
       * Iterate across the vertices, and draw each side of the triangle separately
       */
      for(ii = 0; ii < 3; ii++)
      {
         float vert_x = x[ii] - incenter_x;
         float vert_y = y[ii] - incenter_y;
         
         float o_dx = vert_x * outer_frac;
         float o_dy = vert_y * outer_frac;
         
         float i_dx = vert_x * inner_frac;
         float i_dy = vert_y * inner_frac;
         
         float tdx = o_dx - i_dx;
         float tdy = o_dy - i_dy;
         
         ALLEGRO_VERTEX inner_vtx = ini_vtx;
         ALLEGRO_VERTEX outer_vtx = ini_vtx;
         
         if(tdx * tdx + tdy * tdy > 16 * thickness * thickness) {
            float x_pos = x[(ii + 1) % 3];
            float y_pos = y[(ii + 1) % 3];
            
            float x_neg = x[(ii + 2) % 3];
            float y_neg = y[(ii + 2) % 3];
            
            float x1_x2 = x[ii] - x_pos;
            float y1_y2 = y[ii] - y_pos;
            
            float x1_x3 = x[ii] - x_neg;
            float y1_y3 = y[ii] - y_neg;
            
            float mag_1_2 = hypotf(x1_x2, y1_y2);
            float mag_1_3 = hypotf(x1_x3, y1_y3);
            
            ALLEGRO_VERTEX next_vtx = ini_vtx;
            
            x1_x2 *= thickness / 2 / mag_1_2;
            y1_y2 *= thickness / 2 / mag_1_2;
            
            x1_x3 *= thickness / 2 / mag_1_3;
            y1_y3 *= thickness / 2 / mag_1_3;
            
            outer_vtx.x = x[ii] + x1_x3 - y1_y3; outer_vtx.y = y[ii] + y1_y3 + x1_x3;
            inner_vtx.x = incenter_x + i_dx; inner_vtx.y = incenter_y + i_dy;
            next_vtx.x = x[ii] + x1_x2 + y1_y2; next_vtx.y = y[ii] + y1_y2 - x1_x2;
            
            DRAW
            
            vtx[idx++] = inner_vtx;
            vtx[idx++] = outer_vtx;
            vtx[idx++] = next_vtx;
         } else {
            inner_vtx.x = incenter_x + i_dx; inner_vtx.y = incenter_y + i_dy;
            outer_vtx.x = incenter_x + o_dx; outer_vtx.y = incenter_y + o_dy;
            
            DRAW
            
            vtx[idx++] = inner_vtx;
            vtx[idx++] = outer_vtx;
         }
         
         if(ii == 0) {
            first_inner_vtx = inner_vtx;
            first_outer_vtx = outer_vtx;
         }
      }
      
      vtx[idx++] = first_outer_vtx;
      vtx[idx++] = first_inner_vtx;

      al_draw_prim(vtx, 0, 0, 0, idx, ALLEGRO_PRIM_TRIANGLE_FAN);
      
      #undef DRAW
   } else {
      ALLEGRO_VERTEX vtx[3];
         
      vtx[0].x = x1; vtx[0].y = y1;
      vtx[1].x = x2; vtx[1].y = y2;
      vtx[2].x = x3; vtx[2].y = y3;
      
      vtx[0].color = color;
      vtx[1].color = color;
      vtx[2].color = color;
      
      vtx[0].z = 0;
      vtx[1].z = 0;
      vtx[2].z = 0;
      
      al_draw_prim(vtx, 0, 0, 0, 3, ALLEGRO_PRIM_LINE_LOOP);
   }
}

/* Function: al_draw_filled_triangle
 */
void al_draw_filled_triangle(float x1, float y1, float x2, float y2,
   float x3, float y3, ALLEGRO_COLOR color)
{
   ALLEGRO_VERTEX vtx[3];

   vtx[0].x = x1; vtx[0].y = y1;
   vtx[1].x = x2; vtx[1].y = y2;
   vtx[2].x = x3; vtx[2].y = y3;
  
   vtx[0].color = color;
   vtx[1].color = color;
   vtx[2].color = color;
   
   vtx[0].z = 0;
   vtx[1].z = 0;
   vtx[2].z = 0;
   
   al_draw_prim(vtx, 0, 0, 0, 3, ALLEGRO_PRIM_TRIANGLE_LIST);
}

/* Function: al_draw_rectangle
 */
void al_draw_rectangle(float x1, float y1, float x2, float y2,
   ALLEGRO_COLOR color, float thickness)
{
   int ii;

   if (thickness > 0) {
      float t = thickness / 2;
      ALLEGRO_VERTEX vtx[10];
               
      vtx[0].x = x1 - t; vtx[0].y = y1 - t;
      vtx[1].x = x1 + t; vtx[1].y = y1 + t;
      vtx[2].x = x2 + t; vtx[2].y = y1 - t;
      vtx[3].x = x2 - t; vtx[3].y = y1 + t;
      vtx[4].x = x2 + t; vtx[4].y = y2 + t;
      vtx[5].x = x2 - t; vtx[5].y = y2 - t;
      vtx[6].x = x1 - t; vtx[6].y = y2 + t;
      vtx[7].x = x1 + t; vtx[7].y = y2 - t;
      vtx[8].x = x1 - t; vtx[8].y = y1 - t;
      vtx[9].x = x1 + t; vtx[9].y = y1 + t;
      
      for (ii = 0; ii < 10; ii++) {
         vtx[ii].color = color;
         vtx[ii].z = 0;
      }
      
      al_draw_prim(vtx, 0, 0, 0, 10, ALLEGRO_PRIM_TRIANGLE_STRIP);
   } else {
      ALLEGRO_VERTEX vtx[4];
         
      vtx[0].x = x1; vtx[0].y = y1;
      vtx[1].x = x2; vtx[1].y = y1;
      vtx[2].x = x2; vtx[2].y = y2;
      vtx[3].x = x1; vtx[3].y = y2;
      
      for (ii = 0; ii < 4; ii++) {
         vtx[ii].color = color;
         vtx[ii].z = 0;
      }
      
      al_draw_prim(vtx, 0, 0, 0, 4, ALLEGRO_PRIM_LINE_LOOP);
   }
}

/* Function: al_draw_filled_rectangle
 */
void al_draw_filled_rectangle(float x1, float y1, float x2, float y2,
   ALLEGRO_COLOR color)
{
   ALLEGRO_VERTEX vtx[4];
   int ii;

   vtx[0].x = x1; vtx[0].y = y1;
   vtx[1].x = x1; vtx[1].y = y2;
   vtx[2].x = x2; vtx[2].y = y2;
   vtx[3].x = x2; vtx[3].y = y1;
   
   for (ii = 0; ii < 4; ii++) {
      vtx[ii].color = color;
      vtx[ii].z = 0;
   }
   
   al_draw_prim(vtx, 0, 0, 0, 4, ALLEGRO_PRIM_TRIANGLE_FAN);
}

/* Function: al_calculate_arc
 */
void al_calculate_arc(float* dest, int stride, float cx, float cy,
   float rx, float ry, float start_theta, float delta_theta, float thickness,
   int num_points)
{   
   float theta;
   float c;
   float s;
   float x, y, t;
   int ii;
 
   ASSERT(dest);
   ASSERT(num_points > 1);
   ASSERT(rx >= 0);
   ASSERT(ry >= 0);

   if (thickness > 0.0f) {
      theta = delta_theta / ((float)(num_points) - 1);
      c = cosf(theta);
      s = sinf(theta);
      x = cosf(start_theta);
      y = sinf(start_theta);
 
      if (rx == ry) {
         /*
         The circle case is particularly simple
         */
         float r1 = rx - thickness / 2.0f;
         float r2 = rx + thickness / 2.0f;
         for (ii = 0; ii < num_points; ii ++) {
            *dest =       r2 * x + cx;
            *(dest + 1) = r2 * y + cy;
            dest = (float*)(((char*)dest) + stride);
            *dest =        r1 * x + cx;
            *(dest + 1) =  r1 * y + cy;
            dest = (float*)(((char*)dest) + stride);
            
            t = x;
            x = c * x - s * y;
            y = s * t + c * y;
         }
      } else {
         if (rx != 0 && ry != 0) {
            for (ii = 0; ii < num_points; ii++) {
               float denom = hypotf(ry * x, rx * y);
               float nx = thickness / 2 * ry * x / denom;
               float ny = thickness / 2 * rx * y / denom;

               *dest =       rx * x + cx + nx;
               *(dest + 1) = ry * y + cy + ny;
               dest = (float*)(((char*)dest) + stride);
               *dest =       rx * x + cx - nx;
               *(dest + 1) = ry * y + cy - ny;
               dest = (float*)(((char*)dest) + stride);

               t = x;
               x = c * x - s * y;
               y = s * t + c * y;
            }
         }
      }
   } else {
      theta = delta_theta / ((float)num_points - 1);
      c = cosf(theta);
      s = sinf(theta);
      x = cosf(start_theta);
      y = sinf(start_theta);
      
      for (ii = 0; ii < num_points; ii++) {
         *dest =       rx * x + cx;
         *(dest + 1) = ry * y + cy;
         dest = (float*)(((char*)dest) + stride);

         t = x;
         x = c * x - s * y;
         y = s * t + c * y;
      }
   }
}

/* Function: al_draw_pieslice
 */
void al_draw_pieslice(float cx, float cy, float r, float start_theta,
   float delta_theta, ALLEGRO_COLOR color, float thickness)
{
   LOCAL_VERTEX_CACHE;
   float scale = get_scale();
   int num_segments, ii;
   
   ASSERT(r >= 0);
   
   /* Just makes things a bit easier */
   if(delta_theta < 0) {
      delta_theta = -delta_theta;
      start_theta -= delta_theta;
   }
   
   if (thickness <= 0) {
      num_segments = fabs(delta_theta / (2 * ALLEGRO_PI) * ALLEGRO_PRIM_QUALITY * sqrtf(scale * r));

      if (num_segments < 2)
         num_segments = 2;
      
      if (num_segments + 1 >= ALLEGRO_VERTEX_CACHE_SIZE) {
         num_segments = ALLEGRO_VERTEX_CACHE_SIZE - 1 - 1;
      }
         
      al_calculate_arc(&(vertex_cache[1].x), sizeof(ALLEGRO_VERTEX), cx, cy, r, r, start_theta, delta_theta, 0, num_segments);
      vertex_cache[0].x = cx; vertex_cache[0].y = cy;
      
      for (ii = 0; ii < num_segments + 1; ii++) {
         vertex_cache[ii].color = color;
         vertex_cache[ii].z = 0;
      }
      
      al_draw_prim(vertex_cache, 0, 0, 0, num_segments + 1, ALLEGRO_PRIM_LINE_LOOP);
   } else {
      float ht = thickness / 2;
      float inner_side_angle = asinf(ht / (r - ht));
      float outer_side_angle = asinf(ht / (r + ht));
      float central_angle = delta_theta - 2 * inner_side_angle;
      bool inverted_winding = ((int)(delta_theta / ALLEGRO_PI)) % 2 == 1;
      float midangle = start_theta + (fmodf(delta_theta + ALLEGRO_PI, 2 * ALLEGRO_PI) - ALLEGRO_PI) / 2;
      float midpoint_dir_x = cosf(midangle);
      float midpoint_dir_y = sinf(midangle);
      float side_dir_x = cosf(start_theta);
      float side_dir_y = sinf(start_theta);
      float sine_half_delta = fabsf(side_dir_x * midpoint_dir_y - side_dir_y * midpoint_dir_x); /* Cross product */
      float connect_len = ht / sine_half_delta;
      bool blunt_tip = connect_len > 2 * thickness;
      
      /* The angle is big enough for there to be a hole in the middle */
      if (central_angle > 0) {
         float central_start_angle = start_theta + inner_side_angle;
         size_t vtx_id;
         int vtx_delta;
         /* Two inner hole vertices and the apex (2 vertices if the apex is blunt) */
         int extra_vtx = blunt_tip ? 4 : 3;
         
         al_draw_arc(cx, cy, r, central_start_angle, central_angle, color, thickness);
         
         vertex_cache[0].x = cx + (r - thickness / 2) * cosf(central_start_angle);
         vertex_cache[0].y = cy + (r - thickness / 2) * sinf(central_start_angle);
         
         num_segments = (inner_side_angle + outer_side_angle) / (2 * ALLEGRO_PI) * ALLEGRO_PRIM_QUALITY * sqrtf(scale * (r + ht));
         
         if (num_segments < 2)
            num_segments = 2;
         
         if (num_segments + extra_vtx >= ALLEGRO_VERTEX_CACHE_SIZE)
            num_segments = ALLEGRO_VERTEX_CACHE_SIZE - 1 - extra_vtx;
           
         al_calculate_arc(&(vertex_cache[1].x), sizeof(ALLEGRO_VERTEX), cx, cy, r + ht, r + ht, central_start_angle, -(outer_side_angle + inner_side_angle), 0, num_segments);
         
         /* Do the tip */
         vtx_id = num_segments + 1 + (inverted_winding ? (1 + (blunt_tip ? 1 : 0)) : 0);
         vtx_delta = inverted_winding ? -1 : 1;
         if (blunt_tip) {
            float vx = ht * (side_dir_y * (inverted_winding ? -1 : 1) - side_dir_x);
            float vy = ht * (-side_dir_x * (inverted_winding ? -1 : 1) - side_dir_y);
            float dot = vx * midpoint_dir_x + vy * midpoint_dir_y;
            
            vertex_cache[vtx_id].x = cx + vx;
            vertex_cache[vtx_id].y = cy + vy;
            vtx_id += vtx_delta;
            
            vertex_cache[vtx_id].x = cx + dot * midpoint_dir_x;
            vertex_cache[vtx_id].y = cy + dot * midpoint_dir_y;
         } else {
            vertex_cache[vtx_id].x = cx - connect_len * midpoint_dir_x;
            vertex_cache[vtx_id].y = cy - connect_len * midpoint_dir_y;
         }
         vtx_id += vtx_delta;
         
         if(connect_len > r - ht)
            connect_len = r - ht;
         vertex_cache[vtx_id].x = cx + connect_len * midpoint_dir_x;
         vertex_cache[vtx_id].y = cy + connect_len * midpoint_dir_y;
         
         for (ii = 0; ii < num_segments + extra_vtx; ii++) {
            vertex_cache[ii].color = color;
            vertex_cache[ii].z = 0;
         }
         
         al_draw_prim(vertex_cache, 0, 0, 0, num_segments + extra_vtx, ALLEGRO_PRIM_TRIANGLE_FAN);
         
         /* Mirror the vertices and draw them again */
         for (ii = 0; ii < num_segments + extra_vtx; ii++) {
            float dot = (vertex_cache[ii].x - cx) * midpoint_dir_x + (vertex_cache[ii].y - cy) * midpoint_dir_y;
            vertex_cache[ii].x = 2 * cx + 2 * dot * midpoint_dir_x - vertex_cache[ii].x;
            vertex_cache[ii].y = 2 * cy + 2 * dot * midpoint_dir_y - vertex_cache[ii].y;
         }
         
         al_draw_prim(vertex_cache, 0, 0, 0, num_segments + extra_vtx, ALLEGRO_PRIM_TRIANGLE_FAN);
      } else {
         /* Apex: 2 vertices if the apex is blunt) */
         int extra_vtx = blunt_tip ? 2 : 1;
         
         num_segments = (2 * outer_side_angle) / (2 * ALLEGRO_PI) * ALLEGRO_PRIM_QUALITY * sqrtf(scale * (r + ht));
         
         if (num_segments < 2)
            num_segments = 2;
         
         if (num_segments + extra_vtx >= ALLEGRO_VERTEX_CACHE_SIZE)
            num_segments = ALLEGRO_VERTEX_CACHE_SIZE - 1 - extra_vtx;
           
         al_calculate_arc(&(vertex_cache[1].x), sizeof(ALLEGRO_VERTEX), cx, cy, r + ht, r + ht, start_theta - outer_side_angle, 2 * outer_side_angle + delta_theta, 0, num_segments);
         
         if (blunt_tip) {
            float vx = ht * (side_dir_y - side_dir_x);
            float vy = ht * (-side_dir_x - side_dir_y);
            float dot = vx * midpoint_dir_x + vy * midpoint_dir_y;
            
            vertex_cache[0].x = cx + vx;
            vertex_cache[0].y = cy + vy;
            
            vx = 2 * dot * midpoint_dir_x - vx;
            vy = 2 * dot * midpoint_dir_y - vy;
            
            vertex_cache[num_segments + 1].x = cx + vx;
            vertex_cache[num_segments + 1].y = cy + vy;
         } else {
            vertex_cache[0].x = cx - connect_len * midpoint_dir_x;
            vertex_cache[0].y = cy - connect_len * midpoint_dir_y;
         }
         
         for (ii = 0; ii < num_segments + extra_vtx; ii++) {
            vertex_cache[ii].color = color;
            vertex_cache[ii].z = 0;
         }
         
         al_draw_prim(vertex_cache, 0, 0, 0, num_segments + extra_vtx, ALLEGRO_PRIM_TRIANGLE_FAN);
      }
   }
}

/* Function: al_draw_filled_pieslice
 */
void al_draw_filled_pieslice(float cx, float cy, float r, float start_theta,
   float delta_theta, ALLEGRO_COLOR color)
{
   LOCAL_VERTEX_CACHE;
   float scale = get_scale();
   int num_segments, ii;
   
   ASSERT(r >= 0);
   
   num_segments = fabs(delta_theta / (2 * ALLEGRO_PI) * ALLEGRO_PRIM_QUALITY * sqrtf(scale * r));

   if (num_segments < 2)
      num_segments = 2;
   
   if (num_segments + 1 >= ALLEGRO_VERTEX_CACHE_SIZE) {
      num_segments = ALLEGRO_VERTEX_CACHE_SIZE - 1 - 1;
   }
      
   al_calculate_arc(&(vertex_cache[1].x), sizeof(ALLEGRO_VERTEX), cx, cy, r, r, start_theta, delta_theta, 0, num_segments);
   vertex_cache[0].x = cx; vertex_cache[0].y = cy;
   
   for (ii = 0; ii < num_segments + 1; ii++) {
      vertex_cache[ii].color = color;
      vertex_cache[ii].z = 0;
   }
   
   al_draw_prim(vertex_cache, 0, 0, 0, num_segments + 1, ALLEGRO_PRIM_TRIANGLE_FAN);
}

/* Function: al_draw_ellipse
 */
void al_draw_ellipse(float cx, float cy, float rx, float ry,
   ALLEGRO_COLOR color, float thickness)
{
   LOCAL_VERTEX_CACHE;
   float scale = get_scale();

   ASSERT(rx >= 0);
   ASSERT(ry >= 0);

   if (thickness > 0) {
      int num_segments = ALLEGRO_PRIM_QUALITY * sqrtf(scale * (rx + ry) / 2.0f);
      int ii;

      /* In case rx and ry are both 0. */
      if (num_segments < 2)
         return;

      if (2 * num_segments >= ALLEGRO_VERTEX_CACHE_SIZE) {
         num_segments = (ALLEGRO_VERTEX_CACHE_SIZE - 1) / 2;
      }
      
      al_calculate_arc(&(vertex_cache[0].x), sizeof(ALLEGRO_VERTEX), cx, cy, rx, ry, 0, ALLEGRO_PI * 2, thickness, num_segments);
      for (ii = 0; ii < 2 * num_segments; ii++) {
         vertex_cache[ii].color = color;
         vertex_cache[ii].z = 0;
      }
         
      al_draw_prim(vertex_cache, 0, 0, 0, 2 * num_segments, ALLEGRO_PRIM_TRIANGLE_STRIP);
   } else {
      int num_segments = ALLEGRO_PRIM_QUALITY * sqrtf(scale * (rx + ry) / 2.0f);
      int ii;
      
      /* In case rx and ry are both 0. */
      if (num_segments < 2)
         return;

      if (num_segments >= ALLEGRO_VERTEX_CACHE_SIZE) {
         num_segments = ALLEGRO_VERTEX_CACHE_SIZE - 1;
      }

      al_calculate_arc(&(vertex_cache[0].x), sizeof(ALLEGRO_VERTEX), cx, cy, rx, ry, 0, ALLEGRO_PI * 2, 0, num_segments);
      for (ii = 0; ii < num_segments; ii++) {
         vertex_cache[ii].color = color;
         vertex_cache[ii].z = 0;
      }
         
      al_draw_prim(vertex_cache, 0, 0, 0, num_segments - 1, ALLEGRO_PRIM_LINE_LOOP);
   }
}

/* Function: al_draw_filled_ellipse
 */
void al_draw_filled_ellipse(float cx, float cy, float rx, float ry,
   ALLEGRO_COLOR color)
{
   LOCAL_VERTEX_CACHE;
   int num_segments, ii;
   float scale = get_scale();

   ASSERT(rx >= 0);
   ASSERT(ry >= 0);
   
   num_segments = ALLEGRO_PRIM_QUALITY * sqrtf(scale * (rx + ry) / 2.0f);

   /* In case rx and ry are both close to 0. If al_calculate_arc is passed
    * 0 or 1 it will assert.
    */
   if (num_segments < 2)
      return;
   
   if (num_segments >= ALLEGRO_VERTEX_CACHE_SIZE) {
      num_segments = ALLEGRO_VERTEX_CACHE_SIZE - 1;
   }
      
   al_calculate_arc(&(vertex_cache[1].x), sizeof(ALLEGRO_VERTEX), cx, cy, rx, ry, 0, ALLEGRO_PI * 2, 0, num_segments);
   vertex_cache[0].x = cx; vertex_cache[0].y = cy;
   
   for (ii = 0; ii < num_segments + 1; ii++) {
      vertex_cache[ii].color = color;
      vertex_cache[ii].z = 0;
   }
   
   al_draw_prim(vertex_cache, 0, 0, 0, num_segments + 1, ALLEGRO_PRIM_TRIANGLE_FAN);
}

/* Function: al_draw_circle
 */
void al_draw_circle(float cx, float cy, float r, ALLEGRO_COLOR color,
   float thickness)
{
   al_draw_ellipse(cx, cy, r, r, color, thickness);
}

/* Function: al_draw_filled_circle
 */
void al_draw_filled_circle(float cx, float cy, float r, ALLEGRO_COLOR color)
{
   al_draw_filled_ellipse(cx, cy, r, r, color);
}

/* Function: al_draw_elliptical_arc
 */
void al_draw_elliptical_arc(float cx, float cy, float rx, float ry, float start_theta,
   float delta_theta, ALLEGRO_COLOR color, float thickness)
{
   LOCAL_VERTEX_CACHE;
   float scale = get_scale();

   ASSERT(rx >= 0 && ry >= 0);
   if (thickness > 0) {
      int num_segments = fabs(delta_theta / (2 * ALLEGRO_PI) * ALLEGRO_PRIM_QUALITY * sqrtf(scale * (rx + ry) / 2.0f));
      int ii;

      if (num_segments < 2)
         num_segments = 2;

      if (2 * num_segments >= ALLEGRO_VERTEX_CACHE_SIZE) {
         num_segments = (ALLEGRO_VERTEX_CACHE_SIZE - 1) / 2;
      }

      al_calculate_arc(&(vertex_cache[0].x), sizeof(ALLEGRO_VERTEX), cx, cy, rx, ry, start_theta, delta_theta, thickness, num_segments);
      
      for (ii = 0; ii < 2 * num_segments; ii++) {
         vertex_cache[ii].color = color;
         vertex_cache[ii].z = 0;
      }
      
      al_draw_prim(vertex_cache, 0, 0, 0, 2 * num_segments, ALLEGRO_PRIM_TRIANGLE_STRIP);
   } else {
      int num_segments = fabs(delta_theta / (2 * ALLEGRO_PI) * ALLEGRO_PRIM_QUALITY * sqrtf(scale * (rx + ry) / 2.0f));
      int ii;

      if (num_segments < 2)
         num_segments = 2;

      if (num_segments >= ALLEGRO_VERTEX_CACHE_SIZE) {
         num_segments = ALLEGRO_VERTEX_CACHE_SIZE - 1;
      }
      
      al_calculate_arc(&(vertex_cache[0].x), sizeof(ALLEGRO_VERTEX), cx, cy, rx, ry, start_theta, delta_theta, 0, num_segments);
      
      for (ii = 0; ii < num_segments; ii++) {
         vertex_cache[ii].color = color;
         vertex_cache[ii].z = 0;
      }
      
      al_draw_prim(vertex_cache, 0, 0, 0, num_segments, ALLEGRO_PRIM_LINE_STRIP);
   }
}

/* Function: al_draw_arc
 */
void al_draw_arc(float cx, float cy, float r, float start_theta,
   float delta_theta, ALLEGRO_COLOR color, float thickness)
{
   al_draw_elliptical_arc(cx, cy, r, r, start_theta, delta_theta, color, thickness);
}

/* Function: al_draw_rounded_rectangle
 */
void al_draw_rounded_rectangle(float x1, float y1, float x2, float y2,
   float rx, float ry, ALLEGRO_COLOR color, float thickness)
{
   LOCAL_VERTEX_CACHE;
   float scale = get_scale();

   ASSERT(rx >= 0);
   ASSERT(ry >= 0);

   if (thickness > 0) {
      int num_segments = ALLEGRO_PRIM_QUALITY * sqrtf(scale * (rx + ry) / 2.0f) / 4;
      int ii;

      /* In case rx and ry are both 0. */
      if (num_segments < 2) {
         al_draw_rectangle(x1, y1, x2, y2, color, thickness);
         return;
      }

      if (8 * num_segments + 2 >= ALLEGRO_VERTEX_CACHE_SIZE) {
         num_segments = (ALLEGRO_VERTEX_CACHE_SIZE - 3) / 8;
      }
      
      al_calculate_arc(&(vertex_cache[0].x), sizeof(ALLEGRO_VERTEX), 0, 0, rx, ry, 0, ALLEGRO_PI / 2, thickness, num_segments);
      
      for (ii = 0; ii < 2 * num_segments; ii += 2) {
         vertex_cache[ii + 2 * num_segments + 1].x = x1 + rx - vertex_cache[2 * num_segments - 1 - ii].x;
         vertex_cache[ii + 2 * num_segments + 1].y = y1 + ry - vertex_cache[2 * num_segments - 1 - ii].y;
         vertex_cache[ii + 2 * num_segments].x = x1 + rx - vertex_cache[2 * num_segments - 1 - ii - 1].x;
         vertex_cache[ii + 2 * num_segments].y = y1 + ry - vertex_cache[2 * num_segments - 1 - ii - 1].y;

         vertex_cache[ii + 4 * num_segments].x = x1 + rx - vertex_cache[ii].x;
         vertex_cache[ii + 4 * num_segments].y = y2 - ry + vertex_cache[ii].y;
         vertex_cache[ii + 4 * num_segments + 1].x = x1 + rx - vertex_cache[ii + 1].x;
         vertex_cache[ii + 4 * num_segments + 1].y = y2 - ry + vertex_cache[ii + 1].y;

         vertex_cache[ii + 6 * num_segments + 1].x = x2 - rx + vertex_cache[2 * num_segments - 1 - ii].x;
         vertex_cache[ii + 6 * num_segments + 1].y = y2 - ry + vertex_cache[2 * num_segments - 1 - ii].y;
         vertex_cache[ii + 6 * num_segments].x = x2 - rx + vertex_cache[2 * num_segments - 1 - ii - 1].x;
         vertex_cache[ii + 6 * num_segments].y = y2 - ry + vertex_cache[2 * num_segments - 1 - ii - 1].y;
      }
      for (ii = 0; ii < 2 * num_segments; ii += 2) {
         vertex_cache[ii].x = x2 - rx + vertex_cache[ii].x;
         vertex_cache[ii].y = y1 + ry - vertex_cache[ii].y;
         vertex_cache[ii + 1].x = x2 - rx + vertex_cache[ii + 1].x;
         vertex_cache[ii + 1].y = y1 + ry - vertex_cache[ii + 1].y;
      }
      vertex_cache[8 * num_segments] = vertex_cache[0];
      vertex_cache[8 * num_segments + 1] = vertex_cache[1];

      for (ii = 0; ii < 8 * num_segments + 2; ii++) {
         vertex_cache[ii].color = color;
         vertex_cache[ii].z = 0;
      }
         
      al_draw_prim(vertex_cache, 0, 0, 0, 8 * num_segments + 2, ALLEGRO_PRIM_TRIANGLE_STRIP);
   } else {
      int num_segments = ALLEGRO_PRIM_QUALITY * sqrtf(scale * (rx + ry) / 2.0f) / 4;
      int ii;
      
      /* In case rx and ry are both 0. */
      if (num_segments < 2) {
         al_draw_rectangle(x1, y1, x2, y2, color, thickness);
         return;
      }

      if (num_segments * 4 >= ALLEGRO_VERTEX_CACHE_SIZE) {
         num_segments = (ALLEGRO_VERTEX_CACHE_SIZE - 1) / 4;
      }
      
      al_calculate_arc(&(vertex_cache[0].x), sizeof(ALLEGRO_VERTEX), 0, 0, rx, ry, 0, ALLEGRO_PI / 2, 0, num_segments + 1);

      for (ii = 0; ii < num_segments; ii++) {
         vertex_cache[ii + 1 * num_segments].x = x1 + rx - vertex_cache[num_segments - 1 - ii].x;
         vertex_cache[ii + 1 * num_segments].y = y1 + ry - vertex_cache[num_segments - 1 - ii].y;

         vertex_cache[ii + 2 * num_segments].x = x1 + rx - vertex_cache[ii].x;
         vertex_cache[ii + 2 * num_segments].y = y2 - ry + vertex_cache[ii].y;

         vertex_cache[ii + 3 * num_segments].x = x2 - rx + vertex_cache[num_segments - 1 - ii].x;
         vertex_cache[ii + 3 * num_segments].y = y2 - ry + vertex_cache[num_segments - 1 - ii].y;
      }
      for (ii = 0; ii < num_segments; ii++) {
         vertex_cache[ii].x = x2 - rx + vertex_cache[ii].x;
         vertex_cache[ii].y = y1 + ry - vertex_cache[ii].y;
      }

      for (ii = 0; ii < 4 * num_segments; ii++) {
         vertex_cache[ii].color = color;
         vertex_cache[ii].z = 0;
      }
         
      al_draw_prim(vertex_cache, 0, 0, 0, 4 * num_segments, ALLEGRO_PRIM_LINE_LOOP);
   }
}

/* Function: al_draw_filled_rounded_rectangle
 */
void al_draw_filled_rounded_rectangle(float x1, float y1, float x2, float y2,
   float rx, float ry, ALLEGRO_COLOR color)
{
   LOCAL_VERTEX_CACHE;
   int ii;
   float scale = get_scale();
   int num_segments = ALLEGRO_PRIM_QUALITY * sqrtf(scale * (rx + ry) / 2.0f) / 4;

   ASSERT(rx >= 0);
   ASSERT(ry >= 0);
   
   /* In case rx and ry are both 0. */
   if (num_segments < 2) {
      al_draw_filled_rectangle(x1, y1, x2, y2, color);
      return;
   }

   if (num_segments * 4 >= ALLEGRO_VERTEX_CACHE_SIZE) {
      num_segments = (ALLEGRO_VERTEX_CACHE_SIZE - 1) / 4;
   }
   
   al_calculate_arc(&(vertex_cache[0].x), sizeof(ALLEGRO_VERTEX), 0, 0, rx, ry, 0, ALLEGRO_PI / 2, 0, num_segments + 1);

   for (ii = 0; ii < num_segments; ii++) {
      vertex_cache[ii + 1 * num_segments].x = x1 + rx - vertex_cache[num_segments - 1 - ii].x;
      vertex_cache[ii + 1 * num_segments].y = y1 + ry - vertex_cache[num_segments - 1 - ii].y;

      vertex_cache[ii + 2 * num_segments].x = x1 + rx - vertex_cache[ii].x;
      vertex_cache[ii + 2 * num_segments].y = y2 - ry + vertex_cache[ii].y;

      vertex_cache[ii + 3 * num_segments].x = x2 - rx + vertex_cache[num_segments - 1 - ii].x;
      vertex_cache[ii + 3 * num_segments].y = y2 - ry + vertex_cache[num_segments - 1 - ii].y;
   }
   for (ii = 0; ii < num_segments; ii++) {
      vertex_cache[ii].x = x2 - rx + vertex_cache[ii].x;
      vertex_cache[ii].y = y1 + ry - vertex_cache[ii].y;
   }

   for (ii = 0; ii < 4 * num_segments; ii++) {
      vertex_cache[ii].color = color;
      vertex_cache[ii].z = 0;
   }

   /*
   TODO: Doing this as a triangle fan just doesn't sound all that great, perhaps shuffle the vertices somehow to at least make it a strip
   */
   al_draw_prim(vertex_cache, 0, 0, 0, 4 * num_segments, ALLEGRO_PRIM_TRIANGLE_FAN);
}

/* Function: al_calculate_spline
 */
void al_calculate_spline(float* dest, int stride, const float points[8],
   float thickness, int num_segments)
{
   /* Derivatives of x(t) and y(t). */
   float x, dx, ddx, dddx;
   float y, dy, ddy, dddy;
   int ii = 0;
   
   /* Temp variables used in the setup. */
   float dt, dt2, dt3;
   float xdt2_term, xdt3_term;
   float ydt2_term, ydt3_term;

   /* This is enough to avoid malloc in ex_prim, which I take as a reasonable
    * guess to what a common number of segments might be.  To be honest, it
    * probably makes no difference.
    */
   float cache_point_buffer_storage[150];
   float* cache_point_buffer = cache_point_buffer_storage;

   ASSERT(num_segments > 1);
   ASSERT(points);

   if (num_segments > (int)(sizeof(cache_point_buffer_storage) / sizeof(float) / 2)) {
      cache_point_buffer = al_malloc(2 * sizeof(float) * num_segments);
   }

   dt = 1.0 / (num_segments - 1);
   dt2 = (dt * dt);
   dt3 = (dt2 * dt);
   
   /* x coordinates. */
   xdt2_term = 3 * (points[4] - 2 * points[2] + points[0]);
   xdt3_term = points[6] + 3 * (-points[4] + points[2]) - points[0];
   
   xdt2_term = dt2 * xdt2_term;
   xdt3_term = dt3 * xdt3_term;
   
   dddx = 6 * xdt3_term;
   ddx = -6 * xdt3_term + 2 * xdt2_term;
   dx = xdt3_term - xdt2_term + 3 * dt * (points[2] - points[0]);
   x = points[0];
   
   /* y coordinates. */
   ydt2_term = 3 * (points[5] - 2 * points[3] + points[1]);
   ydt3_term = points[7] + 3 * (-points[5] + points[3]) - points[1];
   
   ydt2_term = dt2 * ydt2_term;
   ydt3_term = dt3 * ydt3_term;
   
   dddy = 6 * ydt3_term;
   ddy = -6 * ydt3_term + 2 * ydt2_term;
   dy = ydt3_term - ydt2_term + dt * 3 * (points[3] - points[1]);
   y = points[1];
   
   cache_point_buffer[2 * ii] = x;
   cache_point_buffer[2 * ii + 1] = y;
   
   for (ii = 1; ii < num_segments; ii++) {
      ddx += dddx;
      dx += ddx;
      x += dx;
      
      ddy += dddy;
      dy += ddy;
      y += dy;
      
      cache_point_buffer[2 * ii] = x;
      cache_point_buffer[2 * ii + 1] = y;
   }
   al_calculate_ribbon(dest, stride, cache_point_buffer, 2 * sizeof(float), thickness, num_segments);

   if (cache_point_buffer != cache_point_buffer_storage) {
      al_free(cache_point_buffer);
   }
}

/* Function: al_draw_spline
 */
void al_draw_spline(const float points[8], ALLEGRO_COLOR color, float thickness)
{
   int ii;
   float scale = get_scale();
   int num_segments = (int)(sqrtf(hypotf(points[2] - points[0], points[3] - points[1]) +
                                  hypotf(points[4] - points[2], points[5] - points[3]) +
                                  hypotf(points[6] - points[4], points[7] - points[5])) *
                            1.2 * ALLEGRO_PRIM_QUALITY * scale / 10);
   LOCAL_VERTEX_CACHE;
   
   if(num_segments < 2)
      num_segments = 2;

   if (thickness > 0) {
      if (2 * num_segments >= ALLEGRO_VERTEX_CACHE_SIZE) {
         num_segments = (ALLEGRO_VERTEX_CACHE_SIZE - 1) / 2;
      }
         
      al_calculate_spline(&(vertex_cache[0].x), sizeof(ALLEGRO_VERTEX), points, thickness, num_segments);
      
      for (ii = 0; ii < 2 * num_segments; ii++) {
         vertex_cache[ii].color = color;
         vertex_cache[ii].z = 0;
      }
      
      al_draw_prim(vertex_cache, 0, 0, 0, 2 * num_segments, ALLEGRO_PRIM_TRIANGLE_STRIP);
   } else {
      if (num_segments >= ALLEGRO_VERTEX_CACHE_SIZE) {
         num_segments = ALLEGRO_VERTEX_CACHE_SIZE - 1;
      }
         
      al_calculate_spline(&(vertex_cache[0].x), sizeof(ALLEGRO_VERTEX), points, thickness, num_segments);
      
      for (ii = 0; ii < num_segments; ii++) {
         vertex_cache[ii].color = color;
         vertex_cache[ii].z = 0;
      }
      
      al_draw_prim(vertex_cache, 0, 0, 0, num_segments, ALLEGRO_PRIM_LINE_STRIP);
   }
}

/* Function: al_calculate_ribbon
 */
void al_calculate_ribbon(float* dest, int dest_stride, const float *points,
   int points_stride, float thickness, int num_segments)
{
   ASSERT(points);
   ASSERT(num_segments >= 2);

   if (thickness > 0) {
      int ii = 0;
      float x, y;
      
      float cur_dir_x;
      float cur_dir_y;
      float prev_dir_x = 0;
      float prev_dir_y = 0;
      float t = thickness / 2;
      float tx, ty;
      float nx, ny;
      float sign = 1;
      
      for (ii = 0; ii < 2 * num_segments - 2; ii += 2) {
         float dir_len;
         x = *points;
         y = *(points + 1);

         points = (float*)(((char*)points) + points_stride);
         
         cur_dir_x = *(points)     - x;
         cur_dir_y = *(points + 1) - y;
         
         dir_len = hypotf(cur_dir_x, cur_dir_y);
         
         if(dir_len > 0.000001f) {
            cur_dir_x /= dir_len;
            cur_dir_y /= dir_len;
         } else if (ii == 0) {
            cur_dir_x = 1;
            cur_dir_y = 0;
         } else {
            cur_dir_x = prev_dir_x;
            cur_dir_y = prev_dir_y;
         }
         
         if (ii == 0) {
            tx = -t * cur_dir_y;
            ty = t * cur_dir_x;
            nx = 0;
            ny = 0;
         } else {
            float dot = cur_dir_x * prev_dir_x + cur_dir_y * prev_dir_y;
            float norm_len, cosine;
            if(dot < 0) {
               /*
                * This is by no means exact, but seems to produce acceptable results
                */
               float tx_;
               tx = cur_dir_x - prev_dir_x;
               ty = cur_dir_y - prev_dir_y;
               norm_len = hypotf(tx, ty);
               
               tx /= norm_len;
               ty /= norm_len;
               
               cosine = tx * cur_dir_x + ty * cur_dir_y;
               
               nx = -t * tx / cosine;
               ny = -t * ty / cosine;
               tx_ = tx;
               tx =  -t * ty * cosine;
               ty =  t * tx_ * cosine;
               sign = -sign;
            } else {
               float new_norm_len;
               tx = cur_dir_y + prev_dir_y;
               ty = -(cur_dir_x + prev_dir_x);
               norm_len = hypotf(tx, ty);
  
               tx /= norm_len;
               ty /= norm_len;
               cosine = tx * (-cur_dir_y) + ty * (cur_dir_x);
               new_norm_len = t / cosine;
               
               tx *= new_norm_len;
               ty *= new_norm_len;
               nx = 0;
               ny = 0;
            }
         }
         
         *dest =       x - sign * tx + nx;
         *(dest + 1) = y - sign * ty + ny;
         dest = (float*)(((char*)dest) + dest_stride);
         *dest =       x + sign * tx + nx;
         *(dest + 1) = y + sign * ty + ny;
         dest = (float*)(((char*)dest) + dest_stride);
         
         prev_dir_x = cur_dir_x;
         prev_dir_y = cur_dir_y;
      }
      tx = -t * prev_dir_y;
      ty = t * prev_dir_x;
      
      x = *points;
      y = *(points + 1);
      
      *dest =       x - sign * tx;
      *(dest + 1) = y - sign * ty;
      dest = (float*)(((char*)dest) + dest_stride);
      *dest =       x + sign * tx;
      *(dest + 1) = y + sign * ty;
   } else {
      int ii;
      for (ii = 0; ii < num_segments; ii++) {
         *dest =       *points;
         *(dest + 1) = *(points + 1);
         dest = (float*)(((char*)dest) + dest_stride);
         points = (float*)(((char*)points) + points_stride);
      }
   }
}

/* Function: al_draw_ribbon
 */
void al_draw_ribbon(const float *points, int points_stride, ALLEGRO_COLOR color,
   float thickness, int num_segments)
{
   LOCAL_VERTEX_CACHE;
   int ii;

   if (num_segments * (thickness > 0 ? 2 : 1) > ALLEGRO_VERTEX_CACHE_SIZE) {
      ALLEGRO_ERROR("Ribbon has too many segments.\n");
      return;
   }

   al_calculate_ribbon(&(vertex_cache[0].x), sizeof(ALLEGRO_VERTEX), points, points_stride, thickness, num_segments);
   
   if (thickness > 0) {
      for (ii = 0; ii < 2 * num_segments; ii++) {
         vertex_cache[ii].color = color;
         vertex_cache[ii].z = 0;
      }
      
      al_draw_prim(vertex_cache, 0, 0, 0, 2 * num_segments, ALLEGRO_PRIM_TRIANGLE_STRIP);
   } else {
      for (ii = 0; ii < num_segments; ii++) {
         vertex_cache[ii].color = color;
         vertex_cache[ii].z = 0;
      }
      
      al_draw_prim(vertex_cache, 0, 0, 0, num_segments, ALLEGRO_PRIM_LINE_STRIP);
   }
}

/* vim: set sts=3 sw=3 et: */