/* $Id: splinestroke.c 4532 2015-12-22 13:18:53Z mskala $ */
/* Copyright (C) 2000-2012  George Williams
 * Copyright (C) 2015  Matthew Skala
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * The name of the author may not be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "fontanvil.h"
#include "splinefont.h"
#include <math.h>

typedef struct strokepoint {
   Spline *sp;
   double t;
   BasePoint me;		/* This lies on the original curve */
   BasePoint slope;		/* Slope at that point */
   BasePoint left;
   BasePoint right;
   /* double radius_of_curvature; */
   unsigned int butt_bevel:1;	/* the butt line cap and bevel line join always produce an edge which will be hidden by the pen. We don't want that to happen so must special case them. */
   unsigned int left_hidden:1;
   unsigned int right_hidden:1;
   unsigned int hide_left_if_on_edge:1;
   unsigned int hide_right_if_on_edge:1;
   unsigned int extemum:1;
   unsigned int circle:1;	/* On a cap or a join, point lies on a circular arc centered at me. So slope is the normal to (left-me) */
   unsigned int line:1;		/* On a cap, join or place where slope paralel to poly edge, point lies on a line can find slope by vector (pos,pos-1) or (pos+1,pos) */
   unsigned int needs_point_left:1;	/* Contour changes direction abruptly on the left side */
   unsigned int needs_point_right:1;
   uint8_t lt;
   uint8_t rt;
} StrokePoint;

/* Square line caps and miter line joins cover more area than the circular pen*/
/*  So we need a list of the polygons which represent these guys so we can do */
/*  an additional hit test on them. */
/* I'm not sure that these will always be presented to us in a clockwise fashion*/
/*  We can test that by taking a point which is the average of the vertices */
/*  and seeing if the hit-test says it is inside (It will be inside because */
/*  these polies are convex). If the hit test fails then the poly must be */
/*  drawn counter clockwise */
struct extrapoly {
   BasePoint poly[4];
   int ptcnt;			/* 3 (for miters) or 4 (for square caps) */
};

enum pentype { pt_circle, pt_square, pt_poly };

typedef struct strokecontext {
   enum pentype pentype;
   int cur, max;
   StrokePoint *all;
   struct extrapoly *ep;
   int ecur, emax;
   TPoint *tpt;
   int tmax;
   double resolution;		/* take samples roughly this many em-units */ /* radius/16.0? */
   double radius;
   double radius2;		/* Squared */
   enum linejoin join;		/* Only for circles */
   enum linecap cap;		/* Only for circles */
   double miterlimit;		/* Only for circles if join==lj_miter */
   /* PostScript uses 1/sin(theta/2 ) as their miterlimit */
   /*  I use -cos(theta). (where theta is the angle between the slopes */
   /*  same idea, different implementation */
   int n;			/* For polygon pens */
   BasePoint *corners;		/* Expressed as an offset from the center of the poly */ /* (Where center could mean center of bounding box, or average or all verteces) */
   BasePoint *slopes;		/* slope[0] is unitvector corners[1]-corners[0] */
   double largest_distance2;	/* The greatest distance from center of poly to a corner */ /* Used to speed up hit tests */
   double longest_edge;
   unsigned int open:1;		/* Original is an open contour */
   unsigned int remove_inner:1;
   unsigned int remove_outer:1;
   /* unsigned int rotate_relative_to_direction: 1; */ /* Rotate the polygon pen so it maintains the same orientation with respect to the contour's slope */
   /* Um, the above is essentially equivalent to a circular pen. No point in duplicating it */
   unsigned int leave_users_center:1;	/* Don't move the pen so its center is at the origin */
   unsigned int scaled_or_rotated:1;
   unsigned int transform_needed:1;
   double transform[6];
   double inverse[6];
} StrokeContext;

static char *glyphname=NULL;

/* Basically the idea is we find the spline, and then at each point, project */
/*  out normal to the current slope and find a point that is radius units away*/
/*  Now that's all well and good but we've also got to handle linecaps and joins */
/*  So we also generate point data for them */
/* Then we check and see if any points are closer to any other baseline points*/
/*  than radius. Any that are get ignored (they will be internal to the       */
/*  resultant shape. (linecaps can also get hidden -- not sure about joints)  */
/* Then (I hope) the points surrounding any hidden points should be very close*/
/*  together. Put break points where there were spline breaks in the original */
/*  and where there is a significant hidden area, and at certain places within*/
/*  caps and joins */

/* Butt line caps and bevel line joins cause problems because they are going */
/*  be inside (hidden) by a circle centered on the line end or joint. So we */
/*  can't do the hit test on these edges using any point on the contour which */
/*  is within radius of the joint. */
/* Hmmm. That works for the edge itself, but if some other part of the contour*/
/*  approaches closely (within radius) to the non-existant area it will be */
/*  incorrectly hidden. BUG!!!!! */
/* Square line caps and miter line joins have the opposite problem: They should*/
/*  hide more points than a circular pen will cover. So we can build up a list*/
/*  of the polygons which represent these extrusions (rectangle for the cap, */
/*  and triangle for the miter) and do a polygon hit test with those. */

/* OK, that's a circular pen. Now by scaling we can turn a circle into an */
/*  elipse, and by rotating we can orient the main axis how we like */
/*  (we actually scale the splines by a transform that turns the ellipse into */
/*   a circle, do the stroking with a circle, and then do the inverse trans-  */
/*   form (on the splines) that turns the circle into the ellipse) */

/* We could also get away with the radius field, and scale the splines by     */
/*  1/radius, and then inverse scale by radius. That way we'd always stroke   */
/*  a unit circle. I think that is not a good idea as there are routines where*/
/*  we assume a 1em error is acceptable -- but not if scaled up to radius     */
/*  later */

/* Fine. But we also want to deal with the caligraphic pen, which is a rectangle */
/*  tilted at 45degrees (or something like that). Well we do the same rotation*/
/*  scaling process we can turn the rectangle into a square oriented with the */
/*  coordinate axes. So how do we stroke a square pen? */

/* Similar, but not quite the same. We don't generate points that are normal  */
/*  to the spline, instead the stroked edge will be the drawn by one of the   */
/*  corners of the square (actually two corners, one on the left, one on the  */
/*  right of the spline), and which corner will depend on the slope of the    */
/*  spline. We will change the corner every time the spline becomes parallel  */
/*  to the square's edges -- that is at the extrema of the spline. When that  */
/*  happens we will have to draw a set of points that represent the side of   */
/*  the square */
/* Same rigamarole for caps and joins (except that these are determined by a  */
/*  square, not a circle, so the shapes will be bumpy not rounded). */
/* Then we have to do the hit test thing to find which points get hidden. Well*/
/*  hit test on a square is pretty easy. If the square is centered at (cx,cy) */
/*  and the square's edges are 2*r, then a point (x,y) is within the square if*/
/*  (x>=cx-r && x<cx+r && y>=cy-r && y<=cy+r) */

/* Well that was easy enough. Can we generalize? A regular polygon: the       */
/*  stroked edges will be dragged out one of the polygon's corners. Which one */
/*  will be determined by the spline's slope. When the spline moves parallel  */
/*  to one of the edges then we will switch corners and have to throw in a set*/
/*  of points as long as that edge. */
/* Note for a regulare n-gon where n even then the left and right sides will  */
/*  change corners at the same time (because their edges are parallel when    */
/*  mirrored across the spline). This is not true of n-gons where n is odd.   */
/*  (So in a regular triangle with one edge flat at the bottom, then we will  */
/*   only switch corners when the spline achieves a minimum in y, not a max)  */
/* Caps and Joins, now we have a polygon so it's even bumpier */
/* Hit test, more work but doable. There are two circles of interest to us,   */
/*  one which is the biggest circle that can be drawn within the polygon, and */
/*  the other, the smallest circle that can be drawn outside the poly. If a   */
/*  point is within the inscribed circle then it is in the poly. If a point is*/
/*  outside the exscribed circle then it is out of the poly. Between those two*/
/*  we have to do some math */

/* Actually, I think any convex polygon could be used with the above algorithm*/
/* The difficulty would be figuring out a good way of inputting the shape of  */
/* the pen (and testing that it wasn't concave). */
/* For a concave poly, the concavities are only relevant at the joins and caps*/
/* (otherwise the concavity will be filled as the pen moves) so we could      */
/* simply use the smallest convex poly that contains the concave one, and just */
/* worry about the concavity at the caps and joins (and maybe in doing the hit*/
/* test) But I don't think there is much point in dealing with that, and there*/
/* are the same issues on describing the shape as before */

static int AdjustedSplineLength(Spline *s) {
   /* I find the spline length in hopes of subdividing the spline into chunks */
   /*  roughly 1em unit apart. But if the spline bends a lot, then when      */
   /*  expanded out by a pen the distance between chunks will be amplified   */
   /*  So do something quick and dirty to adjust for that */
   /* What I really want to do is to split the spline into segments, find */
   /*  the radius of curvature, find the length, multiply length by       */
   /*  (radius-curvature+c->radius)/radius-curvature                      */
   /*  For each segment and them sum that. But that's too hard */
   double len=SplineLength(s);
   double xdiff=s->to->me.x-s->from->me.x, ydiff =
      s->to->me.y-s->from->me.y;
   double distance=sqrt(xdiff * xdiff+ydiff * ydiff);

   if (len <= distance)
      return (len);		/* It's a straight line */
   len += 1.5*(len-distance);
   return (len);
}

enum hittest { ht_Outside, ht_Inside, ht_OnEdge };

static enum hittest PolygonHitTest(BasePoint *poly,BasePoint *polyslopes,
				   int n, BasePoint * test,
				   double * distance) {
   /* If the poly is drawn clockwise, then a point is inside the poly if it */
   /*  is on the right side of each edge */
   /* A point lies on an edge if the dot product of:             */
   /*  1. the vector normal to the edge                          */
   /*  2. the vector from either vertex to the point in question */
   /* is 0 (and it is otherwise inside)                          */
   /* Now, if we choose the normal vector which points right, then */
   /*  the dot product must be positive.                           */
   /* Note we could modify this code to work with counter-clockwise */
   /*  polies. Either all the dots must be positive, or all must be */
   /*  negative */
   /* Sigh. Rounding errors. dot may be off by a very small amount */
   /*  (the polyslopes need to be unit vectors for this test to work) */
   int i, zero_cnt=0, outside=false;
   double dx, dy, dot, bestd=0;

   for (i=0; i<n; ++i) {

      dx=test->x-poly[i].x;
      dy=test->y-poly[i].y;
      dot=dx * polyslopes[i].y-dy * polyslopes[i].x;
      if (dot >= -0.001 && dot <= .001)
	 ++zero_cnt;
      else if (dot<0) {	/* It's on the left, so it can't be inside */
	 if (distance==NULL)
	    return (ht_Outside);
	 outside=true;
	 if (bestd<-dot)
	    bestd=-dot;
      }
   }

   if (outside) {
      *distance=bestd;
      return (ht_Outside);
   }

   if (distance != NULL)
      *distance=0;
   /* zero_cnt==1 => on edge, zero_cnt==2 => on a vertex (on edge), zero_cnt>2 is impossible on a nice poly */
   if (zero_cnt>0) {
      return (ht_OnEdge);
   }

   return (ht_Inside);
}

/* Something is a valid polygonal pen if: */
/*  1. It contains something (not a line, or a single point, something with area) */
/*  2. It contains ONE contour */
/*	(Multiple contours can be dealt with as long as each contour follows */
/*	 requirements 3-n. If we have multiple contours: Find the offset from */
/*	 the center of each contour to the center of all the contours. Trace  */
/*	 each contour individually and then translate by this offset) */
/*  3. All edges must be lines (no control points) */
/*  4. No more than 255 corners (could extend this number, but why?) */
/*  5. It must be drawn clockwise (not really an error, if found just invert, but important for later checks). */
/*  6. It must be convex */
/*  7. No extraneous points on the edges */

/******************************************************************************/
/* ******************************* Circle Pen ******************************* */
/******************************************************************************/

static void LineCap(StrokeContext *c,int isend) {
   int cnt, i, start, end, incr;
   BasePoint base, base2, slope, slope2, halfleft, halfright;
   StrokePoint done;
   StrokePoint *p;
   double factor, si, co;

   cnt=ceil(c->radius/c->resolution);
   if (cnt<2)
      cnt=2;
   if (c->cur+2*cnt+10 >= c->max) {
      int extras=2*cnt+200;

      c->all=realloc(c->all, (c->max+extras)*sizeof(StrokePoint));
      memset(c->all+c->max, 0, extras * sizeof(StrokePoint));
      c->max += extras;
   }
   done=c->all[c->cur-1];
   if (!isend)
      --c->cur;			/* If not at the end then we want to insert */
   /* the line cap data before the last thing */
   /* on the array. So save it (in "done") and */
   /* remove from array. We'll put it back at the */
   /* end */

   base=done.me;
   slope=done.slope;

   cnt=ceil(c->radius/c->resolution);
   if (cnt<3)
      cnt=3;
   if (c->cap==lc_butt) {
      /* Flat end at the point */
      if (isend) {
	 start=cnt;
	 end=0;
	 incr=-1;
      } else {
	 start=0;
	 end=cnt;
	 incr=1;
      }
      for (i=start;; i += incr) {
	 p=&c->all[c->cur++];
	 p->sp=done.sp;
	 p->t=isend;
	 p->line=true;
	 p->butt_bevel=true;
	 p->me=base;
	 p->slope=slope;
	 p->needs_point_left=p->needs_point_right=i==0 || i==cnt;
	 factor=c->radius * i/(cnt);
	 p->left.x=base.x-factor * p->slope.y;
	 p->left.y=base.y+factor * p->slope.x;
	 p->right.x=base.x+factor * p->slope.y;
	 p->right.y=base.y-factor * p->slope.x;
	 if (i==end)
	    break;
      }
   } else if (c->cap==lc_square) {
      /* Continue in the direction of motion for 1 radius, then a flat end */
      if (cnt<4)
	 cnt=4;
      if (cnt&1)
	 ++cnt;
      if (isend) {
	 start=2*cnt;
	 end=0;
	 incr=-1;
      } else {
	 start=0;
	 end=2*cnt;
	 incr=1;
	 slope.x=-slope.x;
	 slope.y=-slope.y;
      }
      base2.x=base.x+(c->radius)*slope.x;
      base2.y=base.y+(c->radius)*slope.y;
      halfleft.x=base2.x-c->radius * done.slope.y;
      halfleft.y=base2.y+c->radius * done.slope.x;
      halfright.x=base2.x+c->radius * done.slope.y;
      halfright.y=base2.y-c->radius * done.slope.x;
      {
	 struct extrapoly *ep;

	 if (c->ecur >= c->emax)
	    c->ep =
	       realloc(c->ep, (c->emax += 40)*sizeof(struct extrapoly));
	 ep=&c->ep[c->ecur++];
	 ep->poly[0]=done.left;
	 ep->poly[1].x=done.left.x+c->radius * slope.x;
	 ep->poly[1].y=done.left.y+c->radius * slope.y;
	 ep->poly[2].x=done.right.x+c->radius * slope.x;
	 ep->poly[2].y=done.right.y+c->radius * slope.y;
	 ep->poly[3]=done.right;
	 if (!isend) {
	    BasePoint temp;

	    ep->poly[0]=done.right;
	    temp=ep->poly[1];
	    ep->poly[1]=ep->poly[2];
	    ep->poly[2]=temp;
	    ep->poly[3]=done.left;
	 }
	 ep->ptcnt=4;
      }
      for (i=start;; i += incr) {
	 p=&c->all[c->cur++];
	 p->sp=done.sp;
	 p->t=isend;
	 p->line=true;
	 p->me=base;
	 p->slope=slope;
	 p->needs_point_left=p->needs_point_right=(i==0 || i==cnt
						       || i==cnt+cnt);
	 if (i <= cnt) {
	    factor=c->radius * i/cnt;
	    p->left.x=base2.x-factor * done.slope.y;
	    p->left.y=base2.y+factor * done.slope.x;
	    p->right.x=base2.x+factor * done.slope.y;
	    p->right.y=base2.y-factor * done.slope.x;
	 } else {
	    factor=c->radius * (i-cnt)/cnt;
	    p->left.x=halfleft.x-factor * slope.x;
	    p->left.y=halfleft.y-factor * slope.y;
	    p->right.x=halfright.x-factor * slope.x;
	    p->right.y=halfright.y-factor * slope.y;
	 }
	 if (i==end)
	    break;
      }
   } else {
      /* Semicircle */
      if (cnt<8)
	 cnt=8;
      if (isend) {
	 start=cnt;
	 end=0;
	 incr=-1;
      } else {
	 start=0;
	 end=cnt;
	 incr=1;
      }
      for (i=start;; i += incr) {
	 p=&c->all[c->cur++];
	 *p=done;
	 p->circle=true;
	 p->needs_point_left=p->needs_point_right=i==0 || i==cnt;
	 si=sin((M_PI/2.0)*i/(double) cnt);
	 co=sqrt(1-si * si);
	 if (isend)
	    co=-co;
	 slope2.x=slope.x * co+slope.y * si;
	 slope2.y=-slope.x * si+slope.y * co;
	 p->left.x=base.x-c->radius * slope2.x;
	 p->left.y=base.y-c->radius * slope2.y;
	 slope2.x=slope.x * co-slope.y * si;
	 slope2.y=slope.x * si+slope.y * co;
	 p->right.x=base.x-c->radius * slope2.x;
	 p->right.y=base.y-c->radius * slope2.y;
	 if (i==end)
	    break;
      }
   }
   if (!isend)
      c->all[c->cur++]=done;
}

static void LineJoin(StrokeContext *c,int atbreak) {
   BasePoint nslope, pslope, curslope, nextslope, base, final, inter,
      center, vector, rot, temp, diff_angle, incr_angle;
   double len, dot, normal_dot, factor;
   int bends_left;
   StrokePoint done;
   StrokePoint *p, *pp;
   int pindex;
   int cnt, cnt1, i, was_neg;
   int force_bevel;

   /* atbreak means that we are dealing with a closed contour, and we have */
   /*  reached the "end" of the contour (which is only the end because we */
   /*  had to start somewhere), so the next point on the contour is the   */
   /*  start (index 0), as opposed to (index+1) */

   if (atbreak) {
      pindex=c->cur-1;
      done=c->all[0];
   } else {
      pindex=c->cur-2;
      done=c->all[c->cur-1];
      /* We decrement c->cur a little later, after we figure out if we need */
      /*  to add a line join */
   }
   if (pindex<0)
      ErrorMsg(2,"LineJoin: pindex<0\n");
   pslope=c->all[pindex].slope;
   nslope=done.slope;
   len=sqrt(nslope.x * nslope.x+nslope.y * nslope.y);
   nslope.x /= len;
   nslope.y /= len;

   len=sqrt(pslope.x * pslope.x+pslope.y * pslope.y);
   pslope.x /= len;
   pslope.y /= len;

   dot=(nslope.x * pslope.x+nslope.y * pslope.y);
   if (dot >= .999)
      return;			/* Essentially colinear */ /* Won't be perfect because control points lie on integers */
   /* miterlimit of 6, 18 degrees */
   force_bevel=(c->join==lj_miter && dot<c->miterlimit);

   cnt=ceil(c->radius/c->resolution);
   if (cnt<6)
      cnt=6;
   if (c->cur+2*cnt+10 >= c->max) {
      int extras=2*cnt+200;

      c->all=realloc(c->all, (c->max+extras)*sizeof(StrokePoint));
      memset(c->all+c->max, 0, extras * sizeof(StrokePoint));
      c->max += extras;
   }
   if (!atbreak)
      --c->cur;			/* If not at the break then we want to insert */
   /* the line join data before the last thing */
   /* on the array. So save it (in "done") and */
   /* remove from array. We'll put it back at the */
   /* end */

   normal_dot=-nslope.x * pslope.y+nslope.y * pslope.x;
   if (normal_dot>0)
      bends_left=true;
   else
      bends_left=false;	/* So it bends right */

   if (c->join==lj_bevel || force_bevel) {
      /* This is easy, a line between the two end points */
      if (bends_left) {
	 /* If it bends left, then the joint is on the right */
	 final=done.right;
	 base=c->all[pindex].right;
      } else {
	 final=done.left;
	 base=c->all[pindex].left;
      }
      curslope.x=final.x-base.x;
      curslope.y=final.y-base.y;
      len=sqrt(curslope.x * curslope.x+curslope.y * curslope.y);
      cnt=ceil(len/c->resolution);
      if (cnt<3)
	 cnt=3;
      if (c->cur+cnt+10 >= c->max) {
	 int extras=cnt+200;

	 c->all=realloc(c->all, (c->max+extras)*sizeof(StrokePoint));
	 memset(c->all+c->max, 0, extras * sizeof(StrokePoint));
	 c->max += extras;
      }
      for (i=0; i <= cnt; ++i) {
	 p=&c->all[c->cur++];
	 *p=c->all[pindex];
	 p->line=true;
	 p->butt_bevel=true;
	 p->needs_point_left=p->needs_point_right=i==0 || i==cnt;
	 p->left_hidden=bends_left;
	 p->right_hidden=!bends_left;
	 factor=((double) i)/cnt;
	 if (i==cnt)
	    p->left=final;
	 else {
	    p->left.x=base.x+factor * curslope.x;
	    p->left.y=base.y+factor * curslope.y;
	 }
	 if (bends_left)
	    p->right=p->left;
      }
   } else if (c->join==lj_miter) {
      /* Almost as easy. Extend from those same two points in the slope */
      /*  at the each until they intersect */
      if (bends_left) {
	 base=c->all[pindex].right;
	 final=done.right;
      } else {
	 base=c->all[pindex].left;
	 final=done.left;
      }
      if (!IntersectLinesSlopes(&inter, &base, &c->all[pindex].slope,
				&final, &done.slope))
	 inter=base;
      {
	 struct extrapoly *ep;

	 if (c->ecur >= c->emax)
	    c->ep =
	       realloc(c->ep, (c->emax += 40)*sizeof(struct extrapoly));
	 ep=&c->ep[c->ecur++];
	 ep->poly[0]=base;
	 ep->poly[1]=inter;
	 ep->poly[2]=final;
	 if (bends_left) {
	    ep->poly[0]=final;
	    ep->poly[2]=base;
	 }
	 ep->ptcnt=3;
      }
      curslope.x=inter.x-base.x;
      curslope.y=inter.y-base.y;
      len=sqrt(curslope.x * curslope.x+curslope.y * curslope.y);
      cnt=ceil(len/c->resolution);
      if (cnt<3)
	 cnt=3;
      nextslope.x=final.x-inter.x;
      nextslope.y=final.y-inter.y;
      len=sqrt(nextslope.x * nextslope.x+nextslope.y * nextslope.y);
      cnt1=ceil(len/c->resolution);
      if (cnt1<3)
	 cnt1=3;
      if (c->cur+cnt+cnt1+2 >= c->max) {
	 int extras=cnt+cnt1+200;

	 c->all=realloc(c->all, (c->max+extras)*sizeof(StrokePoint));
	 memset(c->all+c->max, 0, extras * sizeof(StrokePoint));
	 c->max += extras;
      }
      for (i=0; i <= cnt; ++i) {
	 p=&c->all[c->cur++];
	 *p=c->all[pindex];
	 p->line=true;
	 p->needs_point_left=p->needs_point_right=i==0 || i==cnt;
	 p->left_hidden=bends_left;
	 p->right_hidden=!bends_left;
	 factor=((double) i)/cnt;
	 if (i==cnt)
	    p->left=inter;
	 else {
	    p->left.x=base.x+factor * curslope.x;
	    p->left.y=base.y+factor * curslope.y;
	 }
	 if (bends_left)
	    p->right=p->left;
      }
      for (i=1; i <= cnt1; ++i) {
	 p=&c->all[c->cur++];
	 *p=c->all[pindex];
	 p->line=true;
	 p->needs_point_left=p->needs_point_right=i==0 || i==cnt;
	 p->left_hidden=bends_left;
	 p->right_hidden=!bends_left;
	 factor=((double) i)/cnt1;
	 if (i==cnt1)
	    p->left=final;
	 else {
	    p->left.x=inter.x+factor * nextslope.x;
	    p->left.y=inter.y+factor * nextslope.y;
	 }
	 if (bends_left)
	    p->right=p->left;
      }
   } else {
      /* Now the unit vectors can also be viewed as (sin,cos) pairs */
      /* so to get the separation between them we rotate nslope by -pslope */
      /* or multiple nslope by the rotation matrix: */
      /*  (pslope.x    pslope.y)  */
      /*  (-pslope.y   pslope.x)  */
      diff_angle.x=nslope.x * pslope.x+nslope.y * pslope.y;
      diff_angle.y=-nslope.x * pslope.y+nslope.y * pslope.x;
      /* But what we really want is a small fraction of that. Say 1/20th of */
      /*  the angle. Well that's hard to compute exactly, but don't need exact */
      if (diff_angle.x <= 0) {	/* more than 90 (or less than -90) */
	 incr_angle.y=.078459;	/* sin(PI/40) */
	 if (diff_angle.y<0)
	    incr_angle.y=-incr_angle.y;
      } else
	 incr_angle.y=diff_angle.y/20;
      incr_angle.x=sqrt(1-incr_angle.y * incr_angle.y);

      pp=&c->all[pindex];
      center=pp->me;
      if (bends_left) {
	 vector.x=pp->right.x-center.x;
	 vector.y=pp->right.y-center.y;
      } else {
	 vector.x=pp->left.x-center.x;
	 vector.y=pp->left.y-center.y;
      }
      rot=incr_angle;
      was_neg=false;
      while (1) {
	 if (c->cur >= c->max) {
	    int extras=400;
	    int off=pp-c->all;

	    c->all=realloc(c->all, (c->max+extras)*sizeof(StrokePoint));
	    memset(c->all+c->max, 0, extras * sizeof(StrokePoint));
	    c->max += extras;
	    pp=&c->all[off];
	 }
	 p=&c->all[c->cur++];
	 *p=*pp;
	 p->circle=true;
	 p->needs_point_left=p->needs_point_right=false;
	 p->left_hidden=bends_left;
	 p->right_hidden=!bends_left;
	 if (rot.x <= fabs(diff_angle.x)) {
	    p->right=done.right;
	    p->left=done.left;
	    p->needs_point_left=p->needs_point_right=true;
	    break;
	 } else {
	    temp.x=center.x+vector.x * rot.x-vector.y * rot.y;
	    temp.y=center.y+vector.x * rot.y+vector.y * rot.x;
	    if (bends_left)
	       p->right=temp;
	    else
	       p->left=temp;
	    if (rot.x <= 0 && !was_neg) {
	       was_neg=c->cur-1;
	       p->needs_point_left=p->needs_point_right=true;
	       if (diff_angle.y>.1) {
		  incr_angle.y=diff_angle.y/20;
		  incr_angle.x=sqrt(1-incr_angle.y * incr_angle.y);
	       }
	    }
	    temp.x=rot.x * incr_angle.x-rot.y * incr_angle.y;
	    temp.y=rot.x * incr_angle.y+rot.y * incr_angle.x;
	    if (temp.y>1) {	/* Rounding errors */
	       temp.y=1;
	       temp.x=0;
	    } else if (temp.y<-1) {
	       temp.y=-1;
	       temp.x=0;
	    }
	    rot=temp;
	 }
      }
      if (was_neg != 0 && c->cur-was_neg<5)
	 c->all[was_neg].needs_point_left =
	    c->all[was_neg].needs_point_right=false;
   }
   if (!atbreak)
      c->all[c->cur++]=done;
}

static void FindSlope(StrokeContext *c,Spline *s,double t,double tdiff) {
   StrokePoint *p=&c->all[c->cur-1];
   double len;

   p->sp=s;
   p->t=t;
   p->me.x =
      ((s->splines[0].a * t+s->splines[0].b)*t+s->splines[0].c)*t +
      s->splines[0].d;
   p->me.y =
      ((s->splines[1].a * t+s->splines[1].b)*t+s->splines[1].c)*t +
      s->splines[1].d;
   p->slope.x =
      (3*s->splines[0].a * t+2*s->splines[0].b)*t+s->splines[0].c;
   p->slope.y =
      (3*s->splines[1].a * t+2*s->splines[1].b)*t+s->splines[1].c;
   p->needs_point_left=p->needs_point_right=(t==0.0 || t==1.0);

   if (p->slope.x==0 && p->slope.y==0) {
      /* If the control point is at the endpoint then at the endpoints we */
      /*  have an undefined slope. Can't have that. */
      /* I suppose it could happen elsewhere */
      if (t>0)
	 p->slope=p[-1].slope;
      else {
	 double nextt=t+tdiff;

	 p->slope.x =
	    (3*s->splines[0].a * nextt+2*s->splines[0].b)*nextt +
	    s->splines[0].c;
	 p->slope.y =
	    (3*s->splines[1].a * nextt+2*s->splines[1].b)*nextt +
	    s->splines[1].c;
	 if (p->slope.x==0 && p->slope.y==0) {
	    BasePoint next;

	    next.x =
	       ((s->splines[0].a * nextt+s->splines[0].b)*nextt +
		s->splines[0].c)*nextt+s->splines[0].d;
	    next.y =
	       ((s->splines[1].a * nextt+s->splines[1].b)*nextt +
		s->splines[1].c)*nextt+s->splines[1].d;
	    p->slope.x=next.x-p->me.x;
	    p->slope.y=next.y-p->me.y;
	 }
      }
      if (p->slope.x==0 && p->slope.y==0) {
	 p->slope.x=s->to->me.x=s->from->me.x;
	 p->slope.y=s->to->me.y=s->from->me.y;
      }
      if (p->slope.x==0 && p->slope.y==0)
	 p->slope.x=1;
   }
   len=p->slope.x * p->slope.x+p->slope.y * p->slope.y;
   if (len != 0) {
      len=sqrt(len);
      p->slope.x /= len;
      p->slope.y /= len;
   }
}

static void HideStrokePointsCircle(StrokeContext *c) {
   int i, j;
   double xdiff, ydiff, dist2sq, dist1sq;
   double res2=c->resolution * c->resolution, res_bound=100*res2;

   for (i=c->cur-1; i >= 0; --i) {
      StrokePoint *p=&c->all[i];
      Spline *myline=p->sp->knownlinear?p->sp:NULL;

      if (p->line || p->circle)
	 myline=NULL;
      for (j=c->cur-1; j >= 0; --j)
	 if (j != i) {
	    StrokePoint *op=&c->all[j];

	    /* Something based at the same location as us cannot cover us */
	    /*  but rounding errors might make it look as though it did */
	    if (op->me.x==p->me.x && op->me.y==p->me.y)
	       continue;
	    /* Something on the same line as us cannot cover us */
	    if (op->sp==myline)
	       continue;
	    if (p->butt_bevel) {
	       /* Butt caps and bevel line joins cause problems because the */
	       /*  point on which they are centered will, in fact, cover them */
	       /*  so we wait until the base point has moved at least radius */
	       /*  from our base */
	       xdiff=(p->me.x-op->me.x);
	       ydiff=(p->me.y-op->me.y);
	       dist2sq=xdiff * xdiff+ydiff * ydiff;
	       if (dist2sq<c->radius2)
		  continue;
	    }
	    if (!p->left_hidden) {
	       xdiff=(p->left.x-op->me.x);
	       ydiff=(p->left.y-op->me.y);
	       dist1sq=xdiff * xdiff+ydiff * ydiff;
	       if (dist1sq<c->radius2) {
		  p->left_hidden=true;
		  if (p->right_hidden)
		     break;
	       }
	    } else
	       dist1sq=1e20;
	    if (!p->right_hidden) {
	       xdiff=(p->right.x-op->me.x);
	       ydiff=(p->right.y-op->me.y);
	       dist2sq=xdiff * xdiff+ydiff * ydiff;
	       if (dist2sq<c->radius2) {
		  p->right_hidden=true;
		  if (p->left_hidden)
		     break;
	       }
	    } else
	       dist2sq=1e20;
	    if (dist1sq>dist2sq)
	       dist1sq=dist2sq;
	    dist1sq -= c->radius2;
	    if (dist1sq>res_bound) {
	       /* If we are far away from the desired point then we can */
	       /*  skip ahead more quickly. Since things should be spaced */
	       /*  about resolution units apart we know how far we can    */
	       /*  skip. Since that measurement isn't perfect we leave some */
	       /*  slop */
	       dist1sq /= res2;
	       if (dist1sq<400)
		  j -= (6-1);
	       else
		  j -= .66667*sqrt(dist1sq)-1;
	       /* Minus 1 because we are going to add 1 anyway */
	    }
	 }
   }

   /* now see if any miter join polygons (or square cap polys) cover anything */
   for (j=0; j<c->ecur; ++j) {
      BasePoint slopes[4];
      double len;
      struct extrapoly *ep=&c->ep[j];

      for (i=0; i<ep->ptcnt; ++i) {
	 int ni=i+1;

	 if (ni==ep->ptcnt)
	    ni=0;
	 slopes[i].x=ep->poly[ni].x-ep->poly[i].x;
	 slopes[i].y=ep->poly[ni].y-ep->poly[i].y;
	 len=sqrt(slopes[i].x * slopes[i].x+slopes[i].y * slopes[i].y);
	 if (len==0)
	    return;
	 slopes[i].x /= len;
	 slopes[i].y /= len;
      }
      for (i=c->cur-1; i >= 0; --i) {
	 StrokePoint *p=&c->all[i];

	 if (!p->left_hidden)
	    if (PolygonHitTest(ep->poly, slopes, ep->ptcnt, &p->left, NULL) ==
		ht_Inside)
	       p->left_hidden=true;
	 if (!p->right_hidden)
	    if (PolygonHitTest(ep->poly, slopes, ep->ptcnt, &p->right, NULL)
		== ht_Inside)
	       p->right_hidden=true;
      }
   }
}

static void FindStrokePointsCircle(SplineSet *ss,StrokeContext *c) {
   Spline *s, *first;
   double length;
   int i, len;
   double diff, t;
   int open=ss->first->prev==NULL;
   int gothere=false;

   first=NULL;
   for (s=ss->first->next; s != NULL && s != first; s=s->to->next) {
      if (first==NULL)
	 first=s;
      length=AdjustedSplineLength(s);
      if (length==0)		/* This can happen when we have a spline with the same first and last point and no control point */
	 continue;		/* We can safely ignore it because it is of zero length */
      /* We need to ignore it because it gives us 0/0 in places */

      len=ceil(length/c->resolution);
      if (len<2)
	 len=2;
      /* There will be len+1 sample points taken. Two of those points will be */
      /*  the end points, and there will be at least one internal point */
      diff=1.0/len;
      for (i=0, t=0; i <= len; ++i, t += diff) {
	 StrokePoint *p;

	 if (c->cur >= c->max) {
	    int extras=len+200;

	    c->all=realloc(c->all, (c->max+extras)*sizeof(StrokePoint));
	    memset(c->all+c->max, 0, extras * sizeof(StrokePoint));
	    c->max += extras;
	 }
	 p=&c->all[c->cur++];
	 if (i==len)
	    t=1.0;		/* In case there were rounding errors */
	 FindSlope(c, s, t, diff);
	 p->left.x=p->me.x-c->radius * p->slope.y;
	 p->left.y=p->me.y+c->radius * p->slope.x;
	 p->right.x=p->me.x+c->radius * p->slope.y;
	 p->right.y=p->me.y-c->radius * p->slope.x;
	 if (i==0) {
	    /* OK, the join or cap should happen before this point */
	    /*  But we will use the values we calculate here. So we'll */
	    /*  move the stuff we just calculated until after the joint */
	    if (open && !gothere)
	       LineCap(c, 0);
	    else if (gothere)	/* Do nothing if it's the join at the start */
	       LineJoin(c, false);
	    gothere=true;
	 }
      }
      if (s->to->next==NULL)
	 LineCap(c, 1);
   }
   if (!open && c->cur>0)
      LineJoin(c, true);
   HideStrokePointsCircle(c);

   /* just in case... add an on curve point every time the sample's slopes */
   /*  move through 90 degrees. (We only do this for circles because for  */
   /*  squares and polygons this will happen automatically whenever we change */
   /*  polygon/square vertices, which is close enough to the same idea as no */
   /*  matter) */
   {
      int j, k, skip=10/c->resolution;

      for (i=c->cur-1; i >= 0;) {
	 while (i >= 0
		&& (c->all[i].left_hidden || c->all[i].needs_point_left))
	    --i;
	 if (i<c->cur-1 && !c->all[i+1].left_hidden)
	    ++i;
	 while (i>0 && c->all[i].left.x==c->all[i-1].left.x
		&& c->all[i].left.y==c->all[i-1].left.y)
	    --i;
	 for (j=i-1;
	      j >= 0 && !c->all[j].left_hidden && !c->all[j].needs_point_left;
	      --j) {
	    if (c->all[i].slope.x * c->all[j].slope.x +
		c->all[i].slope.y * c->all[j].slope.y <= 0) {
	       for (k=j-1;
		    k >= 0 && k>j-skip && !c->all[k].left_hidden
		    && !c->all[k].needs_point_left; --k);
	       if (k <= j-skip) {
		  c->all[j].needs_point_left=true;
		  i=j;
		  j=k;
	       }
	    }
	 }
	 i=j;
      }
      for (i=c->cur-1; i >= 0;) {
	 while (i >= 0
		&& (c->all[i].right_hidden || c->all[i].needs_point_right))
	    --i;
	 if (i<c->cur-1 && !c->all[i+1].right_hidden)
	    ++i;
	 while (i>0 && c->all[i].right.x==c->all[i-1].right.x
		&& c->all[i].right.y==c->all[i-1].right.y)
	    --i;
	 for (j=i-1;
	      j >= 0 && !c->all[j].right_hidden
	      && !c->all[j].needs_point_right; --j) {
	    if (c->all[i].slope.x * c->all[j].slope.x +
		c->all[i].slope.y * c->all[j].slope.y <= 0) {
	       for (k=j-1;
		    k >= 0 && k>j-skip && !c->all[k].right_hidden
		    && !c->all[k].needs_point_right; --k);
	       if (k <= j-skip) {
		  c->all[j].needs_point_right=true;
		  i=j;
		  j=k;
	       }
	    }
	 }
	 i=j;
      }
   }

}

/******************************************************************************/
/* ******************************* Square Pen ******************************* */
/******************************************************************************/

static BasePoint SquareCorners[]={
   {-1, 1},
   {1, 1},
   {1, -1},
   {-1, -1},
};

static int WhichSquareCorner(BasePoint *slope,double t,int *right_trace) {
   /* upper left==0, upper right==1, lower right==2, lower left==3 */
   /* Now if we've got a straight line that is horizontal or vertical then */
   /* We return the corner used by the left tracer. The right tracer will */
   /*  use that corner+2%4 */
   int left_trace, rt;

   if (slope->x==0) {
      if (slope->y>0)
	 left_trace=3;
      else
	 left_trace=1;
   } else if (slope->y==0) {
      if (slope->x>0)
	 left_trace=0;
      else
	 left_trace=2;
   } else if (slope->x>0 && slope->y>0)
      left_trace=0;
   else if (slope->x>0)
      left_trace=1;
   else if (slope->y>0)
      left_trace=3;
   else
      left_trace=2;

   rt=left_trace+2;
   if (rt >= 4)
      rt -= 4;
   *right_trace=rt;
   return (left_trace);
}

static void SquareCap(StrokeContext *c,int isend) {
   int cnt, i, start, end, incr;
   int start_corner, end_corner, cc, nc;
   BasePoint slope, slope1, slope2;
   StrokePoint done;
   StrokePoint *p;
   double t;

   /* PostScript has all sorts of funky line endings for circular points */
   /* I shan't bother with them, except for the one which is "draw the */
   /* final half pen". In PostScript that means draw a semi-circle. Here */
   /* we draw half a square (which is easier to draw than a circle) */

   done=c->all[c->cur-1];
   if (!isend) {
      end_corner=done.lt;
      start_corner=done.rt;
   } else {
      end_corner=done.rt;
      start_corner=done.lt;
   }
   /* we draw the square edges in a clockwise direction. So we start at */
   /*  start_corner and add 1 until we get to end corner */
   /* start-end%4==2 */
   cc=end_corner-start_corner;
   if (cc<0)
      cc += 4;
   if (cc==0 || cc==3)
      ErrorMsg(2,"Unexpected value in SquareCap\n");
   /* We want the seam to be half-way. That means if cnt==2 it will be ON the */
   /*  corner point we add, and if cnt==1 it will be halfway along the line */
   /*  between the two new corners. */
   cnt=ceil(c->radius/c->resolution);
   if (c->cur+2*cnt+10 >= c->max) {
      int extras=2*cnt+200;

      c->all=realloc(c->all, (c->max+extras)*sizeof(StrokePoint));
      memset(c->all+c->max, 0, extras * sizeof(StrokePoint));
      c->max += extras;
   }
   if (!isend)
      --c->cur;			/* If not at the end then we want to insert */
   /* the line cap data before the last thing */
   /* on the array. So save it (in "done") and */
   /* remove from array. We'll put it back at the */
   /* end */


   if (cc==2) {
      nc=start_corner+1;
      if (nc==4)
	 nc=0;
      slope1.x=(SquareCorners[nc].x-SquareCorners[done.lt].x)*c->radius;
      slope1.y=(SquareCorners[nc].y-SquareCorners[done.lt].y)*c->radius;
      slope2.x=(SquareCorners[nc].x-SquareCorners[done.rt].x)*c->radius;
      slope2.y=(SquareCorners[nc].y-SquareCorners[done.rt].y)*c->radius;
      if (!isend) {
	 start=cnt;
	 end=1;
	 incr=-1;
      } else {
	 start=1;
	 end=cnt;
	 incr=1;
      }
      for (i=start;; i += incr) {
	 t=((double) i)/cnt;
	 p=&c->all[c->cur++];
	 *p=done;
	 p->line=true;
	 p->needs_point_left=p->needs_point_right=i==cnt;
	 p->left.x=done.left.x+t * slope1.x;
	 p->left.y=done.left.y+t * slope1.y;
	 p->right.x=done.right.x+t * slope2.x;
	 p->right.y=done.right.y+t * slope2.y;
	 if (i==end)
	    break;
      }
   } else {
      slope1.x=done.left.x-done.right.x;
      slope1.y=done.left.y-done.right.y;
      if (isend) {
	 start=cnt;
	 end=1;
	 incr=-1;
      } else {
	 start=1;
	 end=cnt;
	 incr=1;
      }
      for (i=start;; i += incr) {
	 t=((double) i)/(2*cnt);
	 p=&c->all[c->cur++];
	 *p=done;
	 p->line=true;
	 p->needs_point_left=p->needs_point_right=i==cnt;
	 p->left.x=done.left.x-t * slope1.x;
	 p->left.y=done.left.y-t * slope2.y;
	 p->right.x=done.right.x+t * slope.x;
	 p->right.y=done.right.y+t * slope.y;
	 if (i==end)
	    break;
      }
   }
   if (!isend)
      c->all[c->cur++]=done;
}

static void SquareJoin(StrokeContext *c,int atbreak) {
   BasePoint nslope, pslope, base, slope, me;
   double dot;
   int bends_left, start, end, cnt, lastc, nc;
   StrokePoint done;
   StrokePoint *p;
   int pindex, nindex, i;

   if (atbreak) {
      pindex=c->cur-1;
      nindex=0;
   } else {
      pindex=c->cur-2;
      nindex=c->cur-1;
   }
   if (pindex<0)
      ErrorMsg(2,"LineJoin: pindex<0\n");
   done=c->all[nindex];
   pslope=c->all[pindex].slope;
   nslope=done.slope;

   dot=nslope.y * pslope.x-nslope.x * pslope.y;
   if (dot==0) {
      if (nslope.x * pslope.x+nslope.y * pslope.y>0)
	 return;		/* Colinear */
      /* Otherwise we go in the reverse direction */
      /* We need to know whether we are bending left or right, and a dot of 0 */
      /*  won't tell us ... so half the time we get this wrong */
   }
   if (done.rt==c->all[pindex].rt)
      return;			/* Slope changes, but not enough for us to flip to a new corner */
   if (dot>0) {
      bends_left=true;
      start=c->all[pindex].rt;
      end=done.rt;
      /* Either the left point at nindex or at pindex will fall exactly on */
      /*  the other edge. It should be hidden, but because it is exactly on */
      /*  edge it will be retained. Mark as hidden explicitly */
      done.left_hidden=true;
      if (atbreak)
	 c->all[0].left_hidden=true;
   } else {
      bends_left=false;
      start=c->all[pindex].lt;
      end=done.lt;
      c->all[pindex].right_hidden=true;
   }

   cnt=ceil(c->radius/c->resolution);
   if (cnt<2)
      cnt=2;
   if (c->cur+3*cnt+10 >= c->max) {
      int extras=3*cnt+200;

      c->all=realloc(c->all, (c->max+extras)*sizeof(StrokePoint));
      memset(c->all+c->max, 0, extras * sizeof(StrokePoint));
      c->max += extras;
   }
   if (!atbreak)
      --c->cur;			/* If not at the break then we want to insert */
   /* the line join data before the last thing */
   /* on the array. So save it (in "done") and */
   /* remove from array. We'll put it back at the */
   /* end */

   lastc=start;
   for (nc=bends_left?start-1:start+1;; bends_left?--nc:++nc) {
      if (nc==4)
	 nc=0;
      else if (nc<0)
	 nc=3;
      base.x=done.me.x+SquareCorners[lastc].x * c->radius;
      base.y=done.me.y+SquareCorners[lastc].y * c->radius;
      slope.x=SquareCorners[nc].x-SquareCorners[lastc].x;
      slope.y=SquareCorners[nc].y-SquareCorners[lastc].y;
      for (i=1; i <= cnt; ++i) {
	 p=&c->all[c->cur++];
	 *p=c->all[pindex];
	 p->line=true;
	 p->needs_point_left=p->needs_point_right=i==cnt;
	 p->left_hidden=bends_left;
	 p->right_hidden=!bends_left;
	 me.x=base.x+c->radius * i * slope.x/cnt;
	 me.y=base.y+c->radius * i * slope.y/cnt;
	 if (bends_left)
	    p->right=me;
	 else
	    p->left=me;
      }
      if (nc==end)
	 break;
      lastc=nc;
   }
   if (!atbreak)
      c->all[c->cur++]=done;
}

static void HideStrokePointsSquare(StrokeContext *c) {
   int i, j;
   double xdiff, ydiff, dist1, dist2;
   double res_bound=10*c->resolution;

   /* Similar to the case for a circular pen, except the hit test for a square */
   /*  is slightly different from the hit test for a circle */

   for (i=c->cur-1; i >= 0; --i) {
      StrokePoint *p=&c->all[i];
      Spline *myline=p->sp->knownlinear?p->sp:NULL;

      if (p->line || p->circle)
	 myline=NULL;
      for (j=c->cur-1; j >= 0; --j)
	 if (j != i) {
	    StrokePoint *op=&c->all[j];

	    /* Something based at the same location as us cannot cover us */
	    /*  but rounding errors might make it look as though it did */
	    if (op->me.x==p->me.x && op->me.y==p->me.y)
	       continue;
	    /* Something on the same line as us cannot cover us */
	    if (op->sp==myline)
	       continue;
	    dist1=dist2=1e20;
	    if (!p->left_hidden) {
	       if ((xdiff=(p->left.x-op->me.x))<0)
		  xdiff=-xdiff;
	       if ((ydiff=(p->left.y-op->me.y))<0)
		  ydiff=-ydiff;
	       if (xdiff<c->radius && ydiff<c->radius) {
		  p->left_hidden=true;
		  if (p->right_hidden)
		     break;
	       } else
		  dist1=xdiff<ydiff?xdiff:ydiff;
	    }
	    if (!p->right_hidden) {
	       if ((xdiff=(p->right.x-op->me.x))<0)
		  xdiff=-xdiff;
	       if ((ydiff=(p->right.y-op->me.y))<0)
		  ydiff=-ydiff;
	       if (xdiff<c->radius && ydiff<c->radius) {
		  p->right_hidden=true;
		  if (p->left_hidden)
		     break;
	       } else
		  dist2=xdiff<ydiff?xdiff:ydiff;
	    }
	    if (dist1>dist2)
	       dist1=dist2;
	    dist1 -= c->radius;
	    if (dist1>res_bound) {
	       /* If we are far away from the desired point then we can */
	       /*  skip ahead more quickly. Since things should be spaced */
	       /*  about resolution units apart we know how far we can    */
	       /*  skip. Since that measurement isn't perfect we leave some */
	       /*  slop */
	       j -= .66667*dist1/c->resolution-1;
	       /* Minus 1 because we are going to add 1 anyway */
	    }
	 }
   }
}

static int SquareWhichExtreme(StrokePoint *before,StrokePoint *after) {
   /* Just on the off chance that we hit exactly on the extremum */

   if (RealWithin(before->slope.x * before->slope.y, 0, .0001))
      return (-1);		/* the extremum will have a 0 value for either x/y */
   if (RealWithin(after->slope.x * after->slope.y, 0, .0001))
      return (1);

   if (before->slope.y * after->slope.y<0) {
      /* The extremum is in y */
      if (before->slope.y<0)	/* Heading for a minimum */
	 return (before->me.y<after->me.y?-1:1);
      else			/* A maximum */
	 return (before->me.y>after->me.y?-1:1);
   } else {
      /* The extremum is in x */
      if (before->slope.x<0)
	 return (before->me.x<after->me.x?-1:1);
      else
	 return (before->me.x>after->me.x?-1:1);
   }
}

static void FindStrokePointsSquare(SplineSet *ss,StrokeContext *c) {
   Spline *s, *first;
   double length;
   int i, len, cnt;
   double diff, t;
   int open=ss->first->prev==NULL;
   int gothere=false;
   int lt, rt;
   double factor;

   cnt=ceil(2*c->radius/c->resolution);

   first=NULL;
   for (s=ss->first->next; s != NULL && s != first; s=s->to->next) {
      if (first==NULL)
	 first=s;
      length=AdjustedSplineLength(s);
      if (length==0)		/* This can happen when we have a spline with the same first and last point and no control point */
	 continue;		/* We can safely ignore it because it is of zero length */
      /* We need to ignore it because it gives us 0/0 in places */

      len=ceil(length/c->resolution);
      if (len<3)
	 len=3;
      /* There will be len+1 sample points take. Two of those points will be */
      /*  the end points, and there will be at least two internal points */
      if (c->cur+len+1+8*(cnt+2) >= c->max) {
	 int extras=len+8*cnt+200;

	 c->all=realloc(c->all, (c->max+extras)*sizeof(StrokePoint));
	 memset(c->all+c->max, 0, extras * sizeof(StrokePoint));
	 c->max += extras;
      }
      diff=1.0/len;
      for (i=0, t=0; i <= len; ++i, t += diff) {
	 StrokePoint *p;

	 if (c->cur >= c->max) {
	    int extras=len+8*cnt+200;

	    c->all=realloc(c->all, (c->max+extras)*sizeof(StrokePoint));
	    memset(c->all+c->max, 0, extras * sizeof(StrokePoint));
	    c->max += extras;
	 }
	 p=&c->all[c->cur++];
	 if (i==len)
	    t=1.0;		/* In case there were rounding errors */
	 FindSlope(c, s, t, diff);
	 lt=WhichSquareCorner(&p->slope, t, &rt);
	 if (i==0 || p[-1].lt==lt) {
	    p->left.x=p->me.x+c->radius * SquareCorners[lt].x;
	    p->left.y=p->me.y+c->radius * SquareCorners[lt].y;
	    p->right.x=p->me.x+c->radius * SquareCorners[rt].x;
	    p->right.y=p->me.y+c->radius * SquareCorners[rt].y;
	    p->lt=lt;
	    p->rt=rt;
	 } else {
	    StrokePoint final, base, afters;
	    BasePoint slopel, sloper;
	    int k, needsafter=false;

	    p->left.x=p->me.x+c->radius * SquareCorners[p[-1].lt].x;
	    p->left.y=p->me.y+c->radius * SquareCorners[p[-1].lt].y;
	    p->right.x=p->me.x+c->radius * SquareCorners[p[-1].rt].x;
	    p->right.y=p->me.y+c->radius * SquareCorners[p[-1].rt].y;
	    p->lt=p[-1].lt;
	    p->rt=p[-1].rt;

	    /* The side change should happen at the extremum */
	    /* but we aren't there exactly we've got a point before it */
	    /* and a point after it. Either might be closer (and it matters, */
	    /* if we get it wrong we can hide the inserted side by mistake) */
	    /* so figure out which is appropriate */
	    if (SquareWhichExtreme(p-1, p)==-1) {
	       final=base=p[-1];
	       final.left.x=final.me.x+c->radius * SquareCorners[lt].x;
	       final.left.y=final.me.y+c->radius * SquareCorners[lt].y;
	       final.right.x=final.me.x+c->radius * SquareCorners[rt].x;
	       final.right.y=final.me.y+c->radius * SquareCorners[rt].y;
	       final.lt=lt;
	       final.rt=rt;
	       final.needs_point_left=final.needs_point_right=true;
	       p[-1].needs_point_left=p[-1].needs_point_right=true;

	       afters=*p;
	       afters.left.x=afters.me.x+c->radius * SquareCorners[lt].x;
	       afters.left.y=afters.me.y+c->radius * SquareCorners[lt].y;
	       afters.right.x=afters.me.x+c->radius * SquareCorners[rt].x;
	       afters.right.y=afters.me.y+c->radius * SquareCorners[rt].y;
	       --c->cur;
	       needsafter=true;
	    } else {
	       final=base=*p;
	       final.left.x=final.me.x+c->radius * SquareCorners[lt].x;
	       final.left.y=final.me.y+c->radius * SquareCorners[lt].y;
	       final.right.x=final.me.x+c->radius * SquareCorners[rt].x;
	       final.right.y=final.me.y+c->radius * SquareCorners[rt].y;
	       final.lt=lt;
	       final.rt=rt;
	       final.needs_point_left=final.needs_point_right=true;
	       p->needs_point_left=p->needs_point_right=true;
	    }

	    slopel.x=final.left.x-base.left.x;
	    slopel.y=final.left.y-base.left.y;
	    sloper.x=final.right.x-base.right.x;
	    sloper.y=final.right.y-base.right.y;
	    for (k=1; k<cnt; ++k) {
	       p=&c->all[c->cur++];
	       *p=base;
	       p->needs_point_left=p->needs_point_right=false;
	       p->line=true;
	       factor=((double) k)/cnt;
	       p->left.x=base.left.x+factor * slopel.x;
	       p->left.y=base.left.y+factor * slopel.y;
	       p->right.x=base.right.x+factor * sloper.x;
	       p->right.y=base.right.y+factor * sloper.y;
	    }
	    p=&c->all[c->cur++];
	    *p=final;
	    if (needsafter) {
	       p=&c->all[c->cur++];
	       *p=afters;
	    }
	 }
	 if (i==0) {
	    /* OK, the join or cap should happen before this point */
	    /*  But we will use the values we calculate here. So we'll */
	    /*  move the stuff we just calculated until after the joint */
	    if (open && !gothere)
	       SquareCap(c, 0);
	    else if (gothere)
	       SquareJoin(c, false);
	    gothere=true;
	 }
      }
      if (s->to->next==NULL)
	 SquareCap(c, 1);
   }
   if (!c->open && c->cur>0)
      SquareJoin(c, true);
   HideStrokePointsSquare(c);
}

/******************************************************************************/
/* ****************************** Polygon Pen ******************************* */
/******************************************************************************/

static int WhichPolyCorner(StrokeContext *c,BasePoint *slope,
			   int *right_trace) {
   /* The corner we are interested in is the corner whose normal distance */
   /*  from the slope is the greatest. */
   /* Normally there will be one such corner. If the slope is parallel to */
   /*  an edge there will be two. There will never be three because this  */
   /*  is a convex poly and there are no unnecessary points */
   int bestl, bestl2, bestr, bestr2;
   double bestl_off, off, bestr_off;
   int i;

   bestl_off=bestr_off=-1;
   bestl=bestl2=bestr=bestr2=-1;
   for (i=0; i<c->n; ++i) {
      off=-(c->corners[i].x * slope->y)+(c->corners[i].y * slope->x);
      if (off>0 && off >= bestl_off) {
	 if (off>bestl_off) {
	    bestl_off=off;
	    bestl=i;
	    bestl2=-1;
	 } else {
	    bestl2=i;
	 }
      }

      off=(c->corners[i].x * slope->y)-(c->corners[i].y * slope->x);
      if (off>0 && off >= bestr_off) {
	 if (off>bestr_off) {
	    bestr_off=off;
	    bestr=i;
	    bestr2=-1;
	 } else {
	    bestr2=i;
	 }
      }
   }
   if (bestl==-1 || bestr==-1)
      ErrorMsg(2,"Failed to find corner in WhichPolyCorner\n");
   if (bestl2 != -1) {
      if (bestl+1 != bestl2 && !(bestl2==c->n-1 && bestl==0))
	 ErrorMsg(2,"Unexpected multiple left corners in WhichPolyCorner\n");
      if (bestl==0 && bestl2==c->n-1)
	 bestl=c->n-1;
   }
   if (bestr2 != -1) {
      if (bestr+1 != bestr2 && !(bestr2==c->n-1 && bestr==0))
	 ErrorMsg(2,"Unexpected multiple right corners in WhichPolyCorner\n");
      if (bestr==0 && bestr2==c->n-1)
	 bestr=c->n-1;
   }
   *right_trace=bestr;
   return (bestl);
}

static void PolyCap(StrokeContext *c,int isend) {
   int cnt, i, start, end, incr;
   int start_corner, end_corner, cc, nc, bc;
   BasePoint slope1, slope2, base1, base2;
   StrokePoint done;
   StrokePoint *p;
   double t;

   /* Again, we don't worry about funny line endings, we just draw the bit of */
   /*  polygon that sticks out */

   done=c->all[c->cur-1];
   if (!isend) {
      end_corner=done.lt;
      start_corner=done.rt;
   } else {
      end_corner=done.rt;
      start_corner=done.lt;
   }
   cc=end_corner-start_corner;
   if (cc<0)
      cc += c->n;
   if (!isend) {
      int temp=start_corner+cc/2;

      start_corner=end_corner-cc/2;
      end_corner=temp;
      if (end_corner >= c->n)
	 end_corner -= c->n;
      if (start_corner<0)
	 start_corner += c->n;
   }

   cnt=ceil(c->radius/c->resolution);
   if (c->cur+cc * cnt+10 >= c->max) {
      int extras=cc * cnt+200;

      c->all=realloc(c->all, (c->max+extras)*sizeof(StrokePoint));
      memset(c->all+c->max, 0, extras * sizeof(StrokePoint));
      c->max += extras;
   }
   if (!isend)
      --c->cur;			/* If not at the end then we want to insert */
   /* the line cap data before the last thing */
   /* on the array. So save it (in "done") and */
   /* remove from array. We'll put it back at the */
   /* end */
   /* we draw the poly edges in a clockwise direction. So if isend, we start */
   /*  at start_corner and add 1 until we get to middle */
   /* But if !isend we start in the middle and work out */
   /* We do things slightly differently if the number of edges we need to */
   /*  draw is even or odd. */
   for (; cc>0;) {
      base1.x=done.me.x+c->corners[start_corner].x;
      base1.y=done.me.y+c->corners[start_corner].y;
      base2.x=done.me.x+c->corners[end_corner].x;
      base2.y=done.me.y+c->corners[end_corner].y;
      if ((!isend && cc&1) || (isend && cc==1)) {
	 slope1.x=c->corners[end_corner].x-c->corners[start_corner].x;
	 slope1.y=c->corners[end_corner].y-c->corners[start_corner].y;
	 if (!isend) {
	    start=cnt;
	    end=1;
	    incr=-1;
	 } else {
	    start=1;
	    end=cnt;
	    incr=1;
	 }
	 for (i=start;; i += incr) {
	    t=((double) i)/(2*cnt);
	    p=&c->all[c->cur++];
	    *p=done;
	    p->line=true;
	    p->left_hidden=p->right_hidden=false;
	    p->needs_point_left=p->needs_point_right=i==cnt;
	    p->left.x=base1.x+t * slope1.x;
	    p->left.y=base1.y+t * slope1.y;
	    p->right.x=base2.x-t * slope1.x;
	    p->right.y=base2.y-t * slope1.y;
	    if (i==end)
	       break;
	 }
	 cc -= 1;
      } else {
	 nc=start_corner+1;
	 if (nc==c->n)
	    nc=0;
	 bc=end_corner-1;
	 if (bc==-1)
	    bc=c->n-1;
	 slope1.x=(c->corners[nc].x-c->corners[start_corner].x);
	 slope1.y=(c->corners[nc].y-c->corners[start_corner].y);
	 slope2.x=(c->corners[bc].x-c->corners[end_corner].x);
	 slope2.y=(c->corners[bc].y-c->corners[end_corner].y);
	 for (i=0; i <= cnt; ++i) {
	    t=((double) i)/cnt;
	    p=&c->all[c->cur++];
	    *p=done;
	    p->left_hidden=p->right_hidden=false;
	    p->line=true;
	    p->needs_point_left=p->needs_point_right=i==cnt || i==0;
	    p->left.x=base1.x+t * slope1.x;
	    p->left.y=base1.y+t * slope1.y;
	    p->right.x=base2.x+t * slope2.x;
	    p->right.y=base2.y+t * slope2.y;
	 }
	 if (++start_corner >= c->n)
	    start_corner=0;
	 if (--end_corner<0)
	    end_corner=c->n-1;

	 cc -= 2;
      }
   }
   /* OK if there were an even number of edges to add, then we are done */
   /* Otherwise we need add one more edge, and meet in the middle */

   if (cc&1) {
   }
   if (!isend)
      c->all[c->cur++]=done;
}

static void PolyJoin(StrokeContext *c,int atbreak) {
   BasePoint nslope, pslope, base, slope, me;
   double dot;
   int bends_left, dir;
   StrokePoint done;
   StrokePoint *p;
   int pindex, nindex, start, end, cnt, lastc, nc, i;

   /* atbreak means that we are dealing with a close contour, and we have */
   /*  reached the "end" of the contour (which is only the end because we */
   /*  had to start somewhere), so the next point on the contour is the   */
   /*  start (index 0), as opposed to (index+1) */

   if (atbreak) {
      pindex=c->cur-1;
      nindex=0;
   } else {
      pindex=c->cur-2;
      nindex=c->cur-1;
   }
   if (pindex<0)
      ErrorMsg(2,"LineJoin: pindex<0\n");
   done=c->all[nindex];
   pslope=c->all[pindex].slope;
   nslope=done.slope;

   dot=nslope.y * pslope.x-nslope.x * pslope.y;
   if (dot==0) {
      if (nslope.x * pslope.x+nslope.y * pslope.y>0)
	 return;		/* Colinear */
      /* Otherwise we go in the reverse direction */
      /* We need to know whether we are bending left or right, and a dot of 0 */
      /*  won't tell us ... so half the time we get this wrong */
   }
   if (dot>0) {
      /* Slope changes, but not enough for us to flip to a new corner */
      if (done.rt==c->all[pindex].rt)
	 return;
      bends_left=true;
      dir=-1;
      start=c->all[pindex].rt;
      end=done.rt;
      done.hide_left_if_on_edge=true;
      if (atbreak)
	 c->all[0].hide_left_if_on_edge=true;
      c->all[pindex].hide_left_if_on_edge=true;
   } else {
      if (done.lt==c->all[pindex].lt)
	 return;
      bends_left=false;
      dir=1;
      start=c->all[pindex].lt;
      end=done.lt;
      done.hide_right_if_on_edge=true;
      if (atbreak)
	 c->all[0].hide_right_if_on_edge=true;
      c->all[pindex].hide_right_if_on_edge=true;
   }

   cnt=ceil(c->radius/c->resolution);
   if (cnt<2)
      cnt=2;

   if (!atbreak)
      --c->cur;			/* If not at the break then we want to insert */
   /* the line join data before the last thing */
   /* on the array. So save it (in "done") and */
   /* remove from array. We'll put it back at the */
   /* end */

   lastc=start;
   for (nc=start+dir;; nc += dir) {
      if (c->cur+cnt+10 >= c->max) {
	 int extras=3*cnt+200;

	 c->all=realloc(c->all, (c->max+extras)*sizeof(StrokePoint));
	 memset(c->all+c->max, 0, extras * sizeof(StrokePoint));
	 c->max += extras;
      }
      if (nc==c->n)
	 nc=0;
      else if (nc<0)
	 nc=c->n-1;
      base.x=done.me.x+c->corners[lastc].x;
      base.y=done.me.y+c->corners[lastc].y;
      /* Can't use c->slopes because they are unit vectors */
      slope.x=c->corners[nc].x-c->corners[lastc].x;
      slope.y=c->corners[nc].y-c->corners[lastc].y;
      for (i=1; i <= cnt; ++i) {
	 p=&c->all[c->cur++];
	 *p=c->all[pindex];
	 p->line=true;
	 p->needs_point_left=p->needs_point_right=i==cnt;
	 p->left_hidden=bends_left;
	 p->right_hidden=!bends_left;
	 me.x=base.x+i * slope.x/cnt;
	 me.y=base.y+i * slope.y/cnt;
	 if (bends_left)
	    p->right=me;
	 else
	    p->left=me;
      }
      if (nc==end)
	 break;
      lastc=nc;
   }
   if (!atbreak)
      c->all[c->cur++]=done;
}

static void HideStrokePointsPoly(StrokeContext *c) {
   int i, j;
   double xdiff, ydiff, dist1sq, dist2sq;
   BasePoint rel;
   double res2=c->resolution * c->resolution, res_bound=100*res2;
   enum hittest hit;

   /* Similar to the case for a circular pen, except the hit test for a poly */
   /*  is slightly different from the hit test for a circle */


   for (i=c->cur-1; i >= 0; --i) {
      StrokePoint *p=&c->all[i];
      Spline *myline=p->sp->knownlinear?p->sp:NULL;

      if (p->line || p->circle)
	 myline=NULL;
      for (j=c->cur-1; j >= 0; --j)
	 if (j != i) {
	    StrokePoint *op=&c->all[j];

	    /* Something based at the same location as us cannot cover us */
	    /*  but rounding errors might make it look as though it did */
	    if (op->me.x==p->me.x && op->me.y==p->me.y)
	       continue;
	    /* Something on the same line as us cannot cover us */
	    if (op->sp==myline)
	       continue;
	    dist1sq=dist2sq=1e20;
	    if (!p->left_hidden) {
	       xdiff=(p->left.x-op->me.x);
	       ydiff=(p->left.y-op->me.y);
	       dist1sq=xdiff * xdiff+ydiff * ydiff;
	       if (dist1sq <= c->largest_distance2) {
		  rel.x=xdiff;
		  rel.y=ydiff;
		  if ((hit =
		       PolygonHitTest(c->corners, c->slopes, c->n, &rel,
				      NULL))==ht_Inside || (hit==ht_OnEdge
							      && p->
							      hide_left_if_on_edge))
		  {
		     p->left_hidden=true;
		     if (p->right_hidden)
			break;
		  }
	       }
	    }
	    if (!p->right_hidden) {
	       xdiff=(p->right.x-op->me.x);
	       ydiff=(p->right.y-op->me.y);
	       dist2sq=xdiff * xdiff+ydiff * ydiff;
	       if (dist2sq <= c->largest_distance2) {
		  rel.x=xdiff;
		  rel.y=ydiff;
		  if ((hit =
		       PolygonHitTest(c->corners, c->slopes, c->n, &rel,
				      NULL))==ht_Inside || (hit==ht_OnEdge
							      && p->
							      hide_right_if_on_edge))
		  {
		     p->right_hidden=true;
		     if (p->left_hidden)
			break;
		  }
	       }
	    }
	    if (dist1sq>dist2sq)
	       dist1sq=dist2sq;
	    dist1sq -= c->largest_distance2;
	    if (dist1sq>res_bound) {
	       /* If we are far away from the desired point then we can */
	       /*  skip ahead more quickly. Since things should be spaced */
	       /*  about resolution units apart we know how far we can    */
	       /*  skip. Since that measurement isn't perfect we leave some */
	       /*  slop */
	       dist1sq /= res2;
	       if (dist1sq<400)
		  j -= (6-1);
	       else
		  j -= .7*sqrt(dist1sq)-1;
	       /* Minus 1 because we are going to add 1 anyway */
	    }
	 }
   }
}

static int PolyWhichExtreme(StrokeContext *c,int corner,int bends_left,
			    StrokePoint * before, StrokePoint * after) {
   double by, ay, sy;
   int ret;

   if (bends_left) {
      if (--corner<0)
	 corner=c->n-1;
   }

   /* Rotate before/after by the slope of the old side of the polygon */
   /*  and find which is further from the origin */
   /* The rotation means that our min/max will be in y */
   /* (so we only need do the calculations for y */
   sy =
      -before->slope.y * c->slopes[corner].x +
      before->slope.x * c->slopes[corner].y;
   if (RealWithin(sy, 0, .0001))	/* Extremum is at the point, not between */
      return (-1);
   if (RealWithin
       (-after->slope.y * c->slopes[corner].x +
	after->slope.x * c->slopes[corner].y, 0, .0001))
      return (1);

   by =
      -before->me.y * c->slopes[corner].x +
      before->me.x * c->slopes[corner].y;
   ay =
      -after->me.y * c->slopes[corner].x+after->me.x * c->slopes[corner].y;

   if (sy<0) {
      /* Heading for a minimum */
      ret=(by<ay?-1:1);
   } else {
      /* A maximum */
      ret=(by>ay?-1:1);
   }
   return (ret);
}

static void FindStrokePointsPoly(SplineSet *ss,StrokeContext *c) {
   Spline *s, *first;
   double length;
   int i, len, bends_left, k;
   double diff, t, factor;
   int open=ss->first->prev==NULL;
   int gothere=false, stuff_happened;
   int lt, rt, oldlt, oldrt, nlt, nrt;
   BasePoint basel, baser, slopel, sloper;
   int cnt, ldiff, rdiff, dir;
   StrokePoint final, base, saveend;

   cnt=c->longest_edge/c->resolution;
   if (cnt<3)
      cnt=3;

   first=NULL;
   for (s=ss->first->next; s != NULL && s != first; s=s->to->next) {
      if (first==NULL)
	 first=s;
      length=AdjustedSplineLength(s);
      if (length==0)		/* This can happen when we have a spline with the same first and last point and no control point */
	 continue;		/* We can safely ignore it because it is of zero length */
      /* We need to ignore it because it gives us 0/0 in places */

      len=ceil(length/c->resolution);
      if (len<2)
	 len=2;
      /* There will be len+1 sample points take. Two of those points will be */
      /*  the end points, and there will be at least one internal point */
      /* there may be as many as c->n corner changes. Actually there can be */
      /*  c->n on each side, and sides can change independently so 2*c->n */
      if (c->cur+len+1+(cnt+2)*2*c->n >= c->max) {
	 int extras=len+(cnt+2)*2*c->n+200;

	 c->all=realloc(c->all, (c->max+extras)*sizeof(StrokePoint));
	 memset(c->all+c->max, 0, extras * sizeof(StrokePoint));
	 c->max += extras;
      }
      diff=1.0/len;
      oldlt=oldrt=-1;
      memset(&basel, 0, sizeof(basel));
      memset(&baser, 0, sizeof(baser));
      memset(&slopel, 0, sizeof(slopel));
      memset(&sloper, 0, sizeof(sloper));
      for (i=0, t=0; i <= len; ++i, t += diff) {
	 StrokePoint *p;

	 if (c->cur+2 >= c->max) {
	    int extras=200+len;

	    c->all=realloc(c->all, (c->max+extras)*sizeof(StrokePoint));
	    memset(c->all+c->max, 0, extras * sizeof(StrokePoint));
	    c->max += extras;
	    p=&c->all[c->cur-1];
	 }
	 p=&c->all[c->cur++];
	 if (i==len)
	    t=1.0;		/* In case there were rounding errors */
	 FindSlope(c, s, t, diff);
	 lt=WhichPolyCorner(c, &p->slope, &rt);
	 stuff_happened=false;
	 if (p != c->all && (lt != oldlt || rt != oldrt)) {
	    double dot =
	       p->slope.y * p[-1].slope.x-p->slope.x * p[-1].slope.y;
	    bends_left=dot>0;
	    dir=bends_left?-1:1;
	    saveend=*p;
	    --c->cur;
	    stuff_happened=true;
	 }
	 while (oldlt != -1 && (lt != oldlt || rt != oldrt)) {
	    ldiff=0;
	    nlt=oldlt;
	    if (lt != oldlt) {
	       ldiff=PolyWhichExtreme(c, oldlt, bends_left, p-1, p);
	       if ((nlt=oldlt+dir)<0)
		  nlt += c->n;
	       else if (nlt >= c->n)
		  nlt=0;
	       p[-1].needs_point_left=true;
	    }
	    rdiff=0;
	    nrt=oldrt;
	    if (rt != oldrt) {
	       rdiff=PolyWhichExtreme(c, oldrt, bends_left, p-1, p);
	       if ((nrt=oldrt+dir)<0)
		  nrt += c->n;
	       else if (nrt >= c->n)
		  nrt=0;
	       p[-1].needs_point_right=true;
	    }
	    if (ldiff<0 || rdiff<0) {
	       int local_nlt=ldiff<0?nlt:oldlt;
	       int local_nrt=rdiff<0?nrt:oldrt;

	       final=base=p[-1];
	       final.left.x=final.me.x+c->corners[local_nlt].x;
	       final.left.y=final.me.y+c->corners[local_nlt].y;
	       final.right.x=final.me.x+c->corners[local_nrt].x;
	       final.right.y=final.me.y+c->corners[local_nrt].y;
	       final.lt=local_nlt;
	       final.rt=local_nrt;

	       slopel.x=final.left.x-base.left.x;
	       slopel.y=final.left.y-base.left.y;
	       sloper.x=final.right.x-base.right.x;
	       sloper.y=final.right.y-base.right.y;
	       for (k=1; k <= cnt; ++k) {
		  p=&c->all[c->cur++];
		  *p=base;
		  p->needs_point_left=p->needs_point_right=false;
		  p->line=true;
		  factor=((double) k)/cnt;
		  p->left.x=base.left.x+factor * slopel.x;
		  p->left.y=base.left.y+factor * slopel.y;
		  p->right.x=base.right.x+factor * sloper.x;
		  p->right.y=base.right.y+factor * sloper.y;
	       }
	       p->needs_point_left=oldlt != local_nlt;
	       p->needs_point_right=oldrt != local_nrt;
	       oldlt=local_nlt;
	       oldrt=local_nrt;
	    }
	    if (ldiff>0 || rdiff>0) {
	       int local_nlt=ldiff>0?nlt:oldlt;
	       int local_nrt=rdiff>0?nrt:oldrt;

	       final=base=saveend;
	       base.left.x=base.me.x+c->corners[p[-1].lt].x;
	       base.left.y=base.me.y+c->corners[p[-1].lt].y;
	       base.right.x=base.me.x+c->corners[p[-1].rt].x;
	       base.right.y=base.me.y+c->corners[p[-1].rt].y;
	       final.left.x=final.me.x+c->corners[local_nlt].x;
	       final.left.y=final.me.y+c->corners[local_nlt].y;
	       final.right.x=final.me.x+c->corners[local_nrt].x;
	       final.right.y=final.me.y+c->corners[local_nrt].y;
	       final.lt=lt;
	       final.rt=rt;

	       slopel.x=final.left.x-base.left.x;
	       slopel.y=final.left.y-base.left.y;
	       sloper.x=final.right.x-base.right.x;
	       sloper.y=final.right.y-base.right.y;
	       for (k=0; k <= cnt; ++k) {
		  p=&c->all[c->cur++];
		  *p=base;
		  p->needs_point_left=p->needs_point_right=false;
		  if (k==0 || k==cnt) {
		     p->needs_point_left=oldlt != local_nlt;
		     p->needs_point_right=oldrt != local_nrt;
		  }
		  p->line=true;
		  factor=((double) k)/cnt;
		  p->left.x=base.left.x+factor * slopel.x;
		  p->left.y=base.left.y+factor * slopel.y;
		  p->right.x=base.right.x+factor * sloper.x;
		  p->right.y=base.right.y+factor * sloper.y;
	       }
	       oldlt=local_nlt;
	       oldrt=local_nrt;
	    }
	 }
	 if (stuff_happened) {
	    p=&c->all[c->cur++];
	    *p=saveend;
	 }
	 p->left.x=p->me.x+c->corners[lt].x;
	 p->left.y=p->me.y+c->corners[lt].y;
	 p->right.x=p->me.x+c->corners[rt].x;
	 p->right.y=p->me.y+c->corners[rt].y;
	 p->lt=lt;
	 p->rt=rt;
	 oldlt=lt;
	 oldrt=rt;

	 if (i==0) {
	    /* OK, the join or cap should happen before this point */
	    /*  But we will use the values we calculate here. So we'll */
	    /*  move the stuff we just calculated until after the joint */
	    if (open && !gothere)
	       PolyCap(c, 0);
	    else if (gothere)
	       PolyJoin(c, false);
	    gothere=true;
	 }
	 if (c->cur>c->max)
	    ErrorMsg(2,"Memory corrupted\n");
      }
      if (s->to->next==NULL)
	 PolyCap(c, 1);
   }
   if (!c->open && c->cur>0)
      PolyJoin(c, true);
   HideStrokePointsPoly(c);
}

/******************************************************************************/
/* ******************************* To Splines ******************************* */
/******************************************************************************/

static void SSRemoveColinearPoints(SplineSet *ss) {
   SplinePoint *sp, *nsp, *nnsp;
   BasePoint dir, ndir;
   double len;
   int removed;

   sp=ss->first;
   if (sp->next==NULL)
      return;
   nsp=sp->next->to;
   if (nsp==sp)
      return;
   dir.x=nsp->me.x-sp->me.x;
   dir.y=nsp->me.y-sp->me.y;
   len=dir.x * dir.x+dir.y * dir.y;
   if (len != 0) {
      len=sqrt(len);
      dir.x /= len;
      dir.y /= len;
      if (sp->next->knownlinear && (!sp->nonextcp || !nsp->noprevcp)) {
	 sp->nonextcp=true;
	 nsp->noprevcp=true;
	 SplineRefigure(sp->next);
      }
   }
   if (nsp->next==NULL)
      return;
   nnsp=nsp->next->to;
   memset(&ndir, 0, sizeof(ndir));
   removed=false;
   while (1) {
      if (sp==nsp)
	 break;
      if (nsp->next->knownlinear) {
	 ndir.x=nnsp->me.x-nsp->me.x;
	 ndir.y=nnsp->me.y-nsp->me.y;
	 len=ndir.x * ndir.x+ndir.y * ndir.y;
	 if (len != 0) {
	    len=sqrt(len);
	    ndir.x /= len;
	    ndir.y /= len;
	    if (nsp->next->knownlinear && (!nsp->nonextcp || !nnsp->noprevcp)) {
	       nsp->nonextcp=true;
	       nnsp->noprevcp=true;
	       SplineRefigure(nsp->next);
	    }
	 }
      }
      if (sp->next->knownlinear && nsp->next->knownlinear) {
	 double dot=dir.x * ndir.y-dir.y * ndir.x;

	 if (dot<.001 && dot>-.001) {
	    sp->next->to=nnsp;
	    nnsp->prev=sp->next;
	    SplineRefigure(sp->next);
	    SplineFree(nsp->next);
	    SplinePointFree(nsp);
	    if (ss->first==nsp)
	       ss->first=sp;
	    if (ss->last==nsp)
	       ss->last=sp;
	    removed=true;
	 } else
	    sp=nsp;
      } else
	 sp=nsp;
      dir=ndir;
      nsp=nnsp;
      if (nsp->next==NULL)
	 break;
      nnsp=nsp->next->to;
      if (sp==ss->first) {
	 if (!removed)
	    break;
	 removed=false;
      }
   }
}

static struct shapedescrip {
   BasePoint me, prevcp, nextcp;
} unitcircle[]={
   { {
   -1, 0}, {
   -1, -0.552}, {
   -1, 0.552}}, { {
   0, 1}, {
   -0.552, 1}, {
   0.552, 1}}, { {
   1, 0}, {
   1, 0.552}, {
   1, -0.552}}, { {
   0, -1}, {
   0.552, -1}, {
   -0.552, -1}}, { {
   0, 0}, {
   0, 0}, {
   0, 0}}
};

static SplinePoint *SpOnCircle(int i,double radius,BasePoint *center) {
   SplinePoint *sp =
      SplinePointCreate(unitcircle[i].me.x * radius+center->x,
			unitcircle[i].me.y * radius+center->y);

   sp->pointtype=pt_curve;
   sp->prevcp.x=unitcircle[i].prevcp.x * radius+center->x;
   sp->prevcp.y=unitcircle[i].prevcp.y * radius+center->y;
   sp->nextcp.x=unitcircle[i].nextcp.x * radius+center->x;
   sp->nextcp.y=unitcircle[i].nextcp.y * radius+center->y;
   sp->nonextcp=sp->noprevcp=false;
   return (sp);
}

static SplinePointList *SinglePointStroke(SplinePoint *sp,
					  struct strokecontext *c) {
   SplineSet *ret;
   SplinePoint *sp1, *sp2;
   int i;

   ret=chunkalloc(sizeof(SplineSet));

   if (c->pentype==pt_circle && c->cap==lc_butt) {
      /* Leave as a single point */
      ret->first=ret->last=SplinePointCreate(sp->me.x, sp->me.y);
      ret->first->pointtype=pt_corner;
   } else if (c->pentype==pt_circle && c->cap==lc_round) {
      /* Turn into a circle */
      ret->first=sp1=SpOnCircle(0, c->radius, &sp->me);
      for (i=1; i<4; ++i) {
	 sp2=SpOnCircle(i, c->radius, &sp->me);
	 SplineMake3(sp1, sp2);
	 sp1=sp2;
      }
      SplineMake3(sp1, ret->first);
      ret->last=ret->first;
   } else if (c->pentype==pt_circle || c->pentype==pt_square) {
      ret->first=sp1 =
	 SplinePointCreate(sp->me.x+c->radius * SquareCorners[0].x,
			   sp->me.y+c->radius * SquareCorners[0].y);
      sp1->pointtype=pt_corner;
      for (i=1; i<4; ++i) {
	 sp2=SplinePointCreate(sp->me.x+c->radius * SquareCorners[i].x,
				 sp->me.y+c->radius * SquareCorners[i].y);
	 sp2->pointtype=pt_corner;
	 SplineMake3(sp1, sp2);
	 sp1=sp2;
      }
      SplineMake3(sp1, ret->first);
      ret->last=ret->first;
   } else {
      ret->first=sp1=SplinePointCreate(sp->me.x+c->corners[0].x,
					   sp->me.y+c->corners[0].y);
      sp1->pointtype=pt_corner;
      for (i=1; i<c->n; ++i) {
	 sp2=SplinePointCreate(sp->me.x+c->corners[i].x,
				 sp->me.y+c->corners[i].y);
	 sp2->pointtype=pt_corner;
	 SplineMake3(sp1, sp2);
	 sp1=sp2;
      }
      SplineMake3(sp1, ret->first);
      ret->last=ret->first;
   }
   return (ret);
}

static int AllHiddenLeft(struct strokecontext *c) {
   int i;

   for (i=c->cur-1; i >= 0 && c->all[i].left_hidden; --i);
   return (i<0);
}

static int AllHiddenRight(struct strokecontext *c) {
   int i;

   for (i=c->cur-1; i >= 0 && c->all[i].right_hidden; --i);
   return (i<0);
}

static void LeftSlopeAtPos(struct strokecontext *c,int pos,int prev,
			   BasePoint * slope) {
   /* Normally the slope is just the slope stored in the StrokePoint, but */
   /*  that's not the case for line caps and joins (will be for internal  */
   /*  poly edges */
   double len;
   int i;

   if ((prev && pos==0) || (!prev && pos==c->cur-1)) {
      slope->x=slope->y=0;	/* Won't know the slope till we join it to something else */
      return;			/* Can't normalize */
   }
   if ((prev && c->all[pos-1].circle) || (!prev && c->all[pos+1].circle)) {
      /* circle cap/join. Slope is normal to the line from left to me */
      slope->x=(c->all[pos].left.y-c->all[pos].me.y);
      slope->y=-(c->all[pos].left.x-c->all[pos].me.x);
   } else if ((prev && c->all[pos-1].line) ||
	      (!prev && c->all[pos+1].line)) {
      slope->x=slope->y=0;
      /* Sometimes two adjacent points will be coincident (hence the loop) */
      for (i=1;
	   slope->x==0 && slope->y==0 && ((prev && pos >= i)
					      || (!prev && pos+i<c->cur));
	   ++i) {
	 if (prev) {
	    slope->x=c->all[pos].left.x-c->all[pos-i].left.x;
	    slope->y=c->all[pos].left.y-c->all[pos-i].left.y;
	 } else {
	    slope->x=c->all[pos+i].left.x-c->all[pos].left.x;
	    slope->y=c->all[pos+i].left.y-c->all[pos].left.y;
	 }
      }
   } else {
      *slope=c->all[pos].slope;
      return;			/* Already a unit vector */
   }
   len=slope->x * slope->x+slope->y * slope->y;
   if (len != 0) {
      len=sqrt(len);
      slope->x /= len;
      slope->y /= len;
   }
}

static SplinePoint *LeftPointFromContext(struct strokecontext *c,int *_pos,
					 int *newcontour) {
   /* pos should be pointing to an area where we should create a new splinepoint */
   /* this can happen for several reasons: */
   /*  1) There was one in the original contour */
   /*     (actually there will be two: t==1 on one spline, t==0 on the other */
   /*  2) The line cap/join requires one here */
   /*  3) A square or poly pen switched corners */
   /*  4) a bunch of StrokePoints were hidden. In which case we must create */
   /*     a point by taking the first and last unhidden points (around pos) */
   /*     and their slopes and finding where those intersect. If we've done */
   /*     everything right, the intersection will be near the two unhidden  */
   /*     points. This can eat up arbetrary amounts of strokepoints */
   /* When pos==0, life is more complex. In a closed contour we may have to */
   /*  walk backward on the contour which means starting at end and going   */
   /*  back from there. On an open contour, we have to go forwards along the */
   /*  right edge. If there's an original splinepoint here, we just have to */
   /*  walk back one to find the slope, but if things are hidden we can walk */
   /*  arbetrarily far */
   int pos=*_pos, start_pos=pos, orig_pos=pos;
   BasePoint posslope, startslope, inter;
   SplinePoint *ret;
   double normal_dot, len1, len2;
   double res_bound=16*c->resolution * c->resolution;

   *newcontour=false;
   if (pos==0 && !c->open) {
      if (c->all[0].left_hidden ||
	  (c->all[0].needs_point_left && c->all[c->cur-1].needs_point_left))
	 for (start_pos=c->cur-1;
	      start_pos >= 0 && c->all[start_pos].left_hidden; --start_pos);
      /* Might be some hidden points if the other side has a joint !!!!! */
   } else if (c->all[start_pos].left_hidden && start_pos != 0)
      --start_pos;
   if (pos+1<c->cur && c->all[pos].t==1 && c->all[pos+1].t==0)
      ++pos;
   while (pos<c->cur && c->all[pos].left_hidden)
      ++pos;
   while (pos+1<c->cur &&
	  c->all[pos].left.x==c->all[pos+1].left.x &&
	  c->all[pos].left.y==c->all[pos+1].left.y)
      ++pos;
   if (pos >= c->cur) {
      if (c->open)
	 pos=c->cur-1;
      else {
	 pos=0;
	 while (pos<c->cur && c->all[pos].left_hidden)
	    ++pos;
	 if (pos >= c->cur)
	    pos=c->cur-1;	/* Should never happen */
      }
   }
   LeftSlopeAtPos(c, pos, false, &posslope);
   LeftSlopeAtPos(c, start_pos, true, &startslope);
   normal_dot=startslope.x * posslope.y-startslope.y * posslope.x;
   if (normal_dot<0)
      normal_dot=-normal_dot;
   len2 =
      (c->all[start_pos].left.x -
       c->all[pos].left.x)*(c->all[start_pos].left.x-c->all[pos].left.x) +
      (c->all[start_pos].left.y -
       c->all[pos].left.y)*(c->all[start_pos].left.y-c->all[pos].left.y);
   if (len2 >= res_bound && orig_pos==0) {
      /* If we're going to start a new contour at the beginnin, don't bother */
      len2=0;
      start_pos=pos;
      startslope=posslope;
      normal_dot=0;
   }
/* I used to think that if there were hidden points between start and pos then*/
/* those two would be near each other. But there are cases where the hidden */
/* section is at the ends of a long rectangle. In that case we must draw a  */
/* line between the two (actually we should start a new contour, but we aren't*/
   if (normal_dot<.01 && len2<.001) {
      ret=SplinePointCreate(c->all[pos].left.x, c->all[pos].left.y);
      ret->pointtype=pt_curve;
   } else if (len2<16*c->resolution * c->resolution) {
      if (!IntersectLinesSlopes(&inter,
				&c->all[start_pos].left, &startslope,
				&c->all[pos].left, &posslope))
	 inter=c->all[pos].left;
      len1 =
	 (inter.x-c->all[pos].left.x)*(inter.x-c->all[pos].left.x) +
	 (inter.y-c->all[pos].left.y)*(inter.y-c->all[pos].left.y);
      if ((len1>4 && len1>10*len2) || len1>100) {
	 /* Intersection is too far (slopes are very accute? */
	 inter.x=(c->all[pos].left.x+c->all[start_pos].left.x)/2;
	 inter.y=(c->all[pos].left.y+c->all[start_pos].left.y)/2;
      }
      ret=SplinePointCreate(inter.x, inter.y);
      ret->pointtype=pt_corner;
   } else {
      ret =
	 SplinePointCreate(c->all[start_pos].left.x,
			   c->all[start_pos].left.y);
      *newcontour=true;	/* indicates a break in the contour */
   }
   /* CP distance is irrelevent here, all we care about is direction */
   ret->prevcp.x -= startslope.x;
   ret->prevcp.y -= startslope.y;
   if (startslope.x != 0 || startslope.y != 0)
      ret->noprevcp=false;
   if (!*newcontour) {
      ret->nextcp.x += posslope.x;
      ret->nextcp.y += posslope.y;
      if (posslope.x != 0 || posslope.y != 0)
	 ret->nonextcp=false;
      /* the same point will often appear at the end of one spline and the */
      /*  start of the next */
      /* Or a joint may add a lot of points to the other side which show up */
      /*  hidden on this side */
      while (pos+1<c->cur &&
	     ((ret->me.x==c->all[pos+1].left.x
	       && ret->me.y==c->all[pos+1].left.y)
	      || c->all[pos+1].left_hidden))
	 ++pos;
      len2 =
	 (ret->me.x-c->all[pos].left.x)*(ret->me.x-c->all[pos].left.x) +
	 (ret->me.y-c->all[pos].left.y)*(ret->me.y-c->all[pos].left.y);
      if (len2 >= 16*c->resolution * c->resolution)
	 *newcontour=true;
   }
   *_pos=pos;
   return (ret);
}

static void RightSlopeAtPos(struct strokecontext *c,int pos,int prev,
			    BasePoint * slope) {
   /* Normally the slope is just the slope stored in the StrokePoint, but */
   /*  that's not the case for line caps and joins (will be for internal  */
   /*  poly edges */
   double len;
   int i;

   if ((prev && pos==0) || (!prev && pos==c->cur-1)) {
      slope->x=slope->y=0;	/* Won't know the slope till we join it to something else */
      return;			/* Can't normalize */
   }
   if ((prev && c->all[pos-1].circle) || (!prev && c->all[pos+1].circle)) {
      /* circle cap/join. Slope is normal to the line from right to me */
      slope->x=-(c->all[pos].right.y-c->all[pos].me.y);
      slope->y=(c->all[pos].right.x-c->all[pos].me.x);
   } else if ((prev && c->all[pos-1].line) ||
	      (!prev && c->all[pos+1].line)) {
      slope->x=slope->y=0;
      for (i=1;
	   slope->x==0 && slope->y==0 && ((prev && pos >= i)
					      || (!prev && pos+i<c->cur));
	   ++i) {
	 if (prev) {
	    slope->x=c->all[pos].right.x-c->all[pos-i].right.x;
	    slope->y=c->all[pos].right.y-c->all[pos-i].right.y;
	 } else {
	    slope->x=c->all[pos+i].right.x-c->all[pos].right.x;
	    slope->y=c->all[pos+i].right.y-c->all[pos].right.y;
	 }
      }
   } else {
      *slope=c->all[pos].slope;
      return;			/* Already a unit vector */
   }
   len=slope->x * slope->x+slope->y * slope->y;
   if (len != 0) {
      len=sqrt(len);
      slope->x /= len;
      slope->y /= len;
   }
}

static SplinePoint *RightPointFromContext(struct strokecontext *c,int *_pos,
					  int *newcontour) {
   int pos=*_pos, start_pos=pos, orig_pos=pos;
   BasePoint posslope, startslope, inter;
   SplinePoint *ret;
   double normal_dot, len1, len2;
   double res_bound=16*c->resolution * c->resolution;

   *newcontour=false;
   if (pos==0 && !c->open) {
      if (c->all[0].right_hidden ||
	  (c->all[0].needs_point_right
	   && c->all[c->cur-1].needs_point_right))
	 for (start_pos=c->cur-1;
	      start_pos >= 0 && c->all[start_pos].right_hidden; --start_pos);
      /* Might be some hidden points if the other side has a joint !!!!! */
   } else if (c->all[start_pos].right_hidden && start_pos != 0)
      --start_pos;
   if (pos+1<c->cur && c->all[pos].t==1 && c->all[pos+1].t==0)
      ++pos;
   while (pos<c->cur && c->all[pos].right_hidden)
      ++pos;
   while (pos+1<c->cur &&
	  c->all[pos].right.x==c->all[pos+1].right.x &&
	  c->all[pos].right.y==c->all[pos+1].right.y)
      ++pos;
   if (pos >= c->cur) {
      if (c->open)
	 pos=c->cur-1;
      else {
	 pos=0;
	 while (pos<c->cur && c->all[pos].right_hidden)
	    ++pos;
	 if (pos >= c->cur)
	    pos=c->cur-1;	/* Should never happen */
      }
   }
   RightSlopeAtPos(c, pos, false, &posslope);
   RightSlopeAtPos(c, start_pos, true, &startslope);
   normal_dot=startslope.x * posslope.y-startslope.y * posslope.x;
   if (normal_dot<0)
      normal_dot=-normal_dot;
   len2 =
      (c->all[start_pos].right.x -
       c->all[pos].right.x)*(c->all[start_pos].right.x -
			       c->all[pos].right.x) +
      (c->all[start_pos].right.y -
       c->all[pos].right.y)*(c->all[start_pos].right.y -
			       c->all[pos].right.y);
   if (len2 >= res_bound && orig_pos==0) {
      /* If we're going to start a new contour at the beginnin, don't bother */
      len2=0;
      start_pos=pos;
      startslope=posslope;
      normal_dot=0;
   }
/* I used to think that if there were hidden points between start and pos then*/
/* those two would be near each other. But there are cases where the hidden */
/* section is at the ends of a long rectangle. In that case we should start a */
/* new contour */
   if (normal_dot<.01 && len2<.01) {
      ret=SplinePointCreate(c->all[pos].right.x, c->all[pos].right.y);
      ret->pointtype=pt_curve;
   } else if (len2<res_bound) {
      if (!IntersectLinesSlopes(&inter,
				&c->all[start_pos].right, &startslope,
				&c->all[pos].right, &posslope))
	 inter=c->all[pos].right;
      len1 =
	 (inter.x-c->all[pos].right.x)*(inter.x-c->all[pos].right.x) +
	 (inter.y-c->all[pos].right.y)*(inter.y-c->all[pos].right.y);
      if (len1>4 && len1>10*len2) {
	 /* Intersection is too far (slopes are very accute? */
	 inter.x=(c->all[pos].right.x+c->all[start_pos].right.x)/2;
	 inter.y=(c->all[pos].right.y+c->all[start_pos].right.y)/2;
      }
      ret=SplinePointCreate(inter.x, inter.y);
      ret->pointtype=pt_corner;
   } else {
      ret =
	 SplinePointCreate(c->all[start_pos].right.x,
			   c->all[start_pos].right.y);
      *newcontour=true;
   }
   /* CP distance is irrelevent here, all we care about is direction */
   ret->prevcp.x -= startslope.x;
   ret->prevcp.y -= startslope.y;
   if (startslope.x != 0 || startslope.y != 0)
      ret->noprevcp=false;
   if (!*newcontour) {
      /* the same point will often appear at the end of one spline and the */
      /*  start of the next */
      /* Or a joint may add a lot of points to the other side which show up */
      /*  hidden on this side */
      int opos=pos;

      while (pos+1<c->cur &&
	     ((ret->me.x==c->all[pos+1].right.x
	       && ret->me.y==c->all[pos+1].right.y)
	      || c->all[pos+1].right_hidden))
	 ++pos;
      if (opos != pos) {
	 RightSlopeAtPos(c, pos, false, &posslope);
	 ret->pointtype=pt_corner;
      }
      ret->nextcp.x += posslope.x;
      ret->nextcp.y += posslope.y;
      if (posslope.x != 0 || posslope.y != 0)
	 ret->nonextcp=false;
      len2 =
	 (ret->me.x-c->all[pos].right.x)*(ret->me.x -
					      c->all[pos].right.x) +
	 (ret->me.y-c->all[pos].right.y)*(ret->me.y -
					      c->all[pos].right.y);
      if (len2 >= res_bound)
	 *newcontour=true;
   }
   *_pos=pos;
   return (ret);
}

static void HideSomeMorePoints(StrokeContext *c) {
   /* Fix up a couple of problems that hit tests miss */
   /* 1. At the extreme end of a spline when it comes to a joint, then  */
   /*    the last point generated (t==1/t==0) will lie on the >edge< of */
   /*    the covering polygon. So the hit test won't find it (not interior) */
   /*    But it should none the less be removed */
   /* 2. We don't have a continuous view of the generated contour, we have */
   /*    a set of samples. Similarly, we don't use a continuous view of the */
   /*    original contour, we use the same set of samples for our hit tests */
   /*    it can happen that the region of the original which will hide a   */
   /*    given generated sample, is not in our sample set. */
   /* So let's say that any singleton unhidden sample between two hidden   */
   /*  samples should be hidden */
   /* But that isn't good enough. I had two adjacent unhidden points once  */

   /* Similarly there are cases (if the poly edges aren't horizontal/vert) */
   /*  where, due to rounding errors, something which SHOULD be on the edge */
   /*  will appear to be inside */
   int i, last_h, next_h;

   if (c->cur==0)
      return;

   last_h=c->open?false:c->all[c->cur-1].left_hidden;
   for (i=0; i<c->cur; ++i) {
      StrokePoint *p=&c->all[i];

      next_h =
	 (i <
	  c->cur -
	  1)?p[1].left_hidden:c->open?false:c->all[0].left_hidden;
      if (last_h && !p->left_hidden) {
	 if (next_h)
	    p->left_hidden=true;
	 else if (i<c->cur-2 && !next_h && p[2].left_hidden &&
		  (p->t==0 || p->t==1 || p[1].t==0 || p[1].t==1)) {
	    p->left_hidden=true;
	    p[1].left_hidden=true;
	 }
      } else if (!last_h && p->left_hidden && !next_h)
	 p->left_hidden=false;
      if (!p->left_hidden) {
	 /* If two points are at the same location and one is hidden */
	 /*  and the other isn't, then hide both. Note, must be careful */
	 /*  because location data isn't always meaningful for hidden pts */
	 /*  hence the check on "hide_?_if_on_edge" */
	 if (i>0 && p[-1].left_hidden && p[-1].hide_left_if_on_edge &&
	     p[-1].left.x==p->left.x && p[-1].left.y==p->left.y)
	    p->left_hidden=true;
	 else if (i<c->cur-1 && p[1].left_hidden
		  && p[1].hide_left_if_on_edge && p[1].left.x==p->left.x
		  && p[1].left.y==p->left.y)
	    p->left_hidden=true;
      }

      last_h=p->left_hidden;
   }

   last_h=c->open?false:c->all[c->cur-1].right_hidden;
   for (i=0; i<c->cur; ++i) {
      StrokePoint *p=&c->all[i];

      next_h =
	 (i <
	  c->cur -
	  1)?p[1].right_hidden:c->open?false:c->all[0].right_hidden;
      if (last_h && !p->right_hidden) {
	 if (next_h)
	    p->right_hidden=true;
	 else if (i<c->cur-2 && !next_h && p[2].right_hidden &&
		  (p->t==0 || p->t==1 || p[1].t==0 || p[1].t==1)) {
	    p->right_hidden=true;
	    p[1].right_hidden=true;
	 }
      } else if (!last_h && p->right_hidden && !next_h)
	 p->right_hidden=false;
      last_h=p->right_hidden;
   }
}

static void AddUnhiddenPoints(StrokeContext *c) {
   /* Now when we have some hidden points on one side, (and it isn't at a */
   /*  known joint) then it's probably a good idea to put a point on the  */
   /*  other side (if one isn't there already) to stablize it, because our */
   /*  approximation routines can get local maxima -- at the right place  */
   /*  but too much or too little */
   int start, end, mid;
   int i, any;
   double xdiff, ydiff;

   if (c==NULL || c->all==NULL)
      return;
   start=0;
   if (c->all[0].left_hidden)
      for (start=0; start<c->cur && c->all[start].left_hidden; ++start);
   /* Not interested in a hidden section around 0 because there will be an on- */
   /*  curve point there already */
   while (start<c->cur) {
      while (start<c->cur && !c->all[start].left_hidden)
	 ++start;
      if (start >= c->cur)
	 break;
      for (end=start; end<c->cur && c->all[end].left_hidden; ++end);
      if (end >= c->cur)
	 break;			/* again, this would wrap around to 0 so not interesting */
      --start;
      mid=(start+end)/2;
      /* See if already at a joint */
      any=false;
      for (i=1;; ++i) {
	 int back, fore;

	 if (mid-i>0) {
	    if (c->all[mid-i].line || c->all[mid-i].circle
		|| c->all[mid-i].needs_point_right) {
	       any=true;
	       break;
	    }
	    xdiff=c->all[mid-i].me.x-c->all[mid].me.x;
	    ydiff=c->all[mid-i].me.y-c->all[mid].me.y;
	    back=(xdiff * xdiff+ydiff * ydiff>100);
	 } else
	    back=true;
	 if (mid+i<c->cur) {
	    if (c->all[mid+i].line || c->all[mid+i].circle
		|| c->all[mid+i].needs_point_right) {
	       any=true;
	       break;
	    }
	    xdiff=c->all[mid+i].me.x-c->all[mid].me.x;
	    ydiff=c->all[mid+i].me.y-c->all[mid].me.y;
	    fore=(xdiff * xdiff+ydiff * ydiff>100);
	 } else
	    fore=true;
	 if (back && fore)
	    break;
      }
      if (!any)
	 c->all[mid].needs_point_right=true;
      start=end;
   }

   /* Same thing for the right side */
   start=0;
   if (c->all[0].right_hidden)
      for (start=0; start<c->cur && c->all[start].right_hidden; ++start);
   while (start<c->cur) {
      while (start<c->cur && !c->all[start].right_hidden)
	 ++start;
      if (start >= c->cur)
	 break;
      for (end=start; end<c->cur && c->all[end].right_hidden; ++end);
      if (end >= c->cur)
	 break;			/* again, this would wrap around to 0 so not interesting */
      --start;
      mid=(start+end)/2;
      /* See if already at a joint */
      any=false;
      for (i=1;; ++i) {
	 int back, fore;

	 if (mid-i>0) {
	    if (c->all[mid-i].line || c->all[mid-i].circle
		|| c->all[mid-i].needs_point_left) {
	       any=true;
	       break;
	    }
	    xdiff=c->all[mid-i].me.x-c->all[mid].me.x;
	    ydiff=c->all[mid-i].me.y-c->all[mid].me.y;
	    back=(xdiff * xdiff+ydiff * ydiff>100);
	 } else
	    back=true;
	 if (mid+i<c->cur) {
	    if (c->all[mid+i].line || c->all[mid+i].circle
		|| c->all[mid+i].needs_point_left) {
	       any=true;
	       break;
	    }
	    xdiff=c->all[mid+i].me.x-c->all[mid].me.x;
	    ydiff=c->all[mid+i].me.y-c->all[mid].me.y;
	    fore=(xdiff * xdiff+ydiff * ydiff>100);
	 } else
	    fore=true;
	 if (back && fore)
	    break;
      }
      if (!any)
	 c->all[mid].needs_point_left=true;
      start=end;
   }
}

static void PointJoint(SplinePoint *base,SplinePoint *other,
		       double resolution) {
   BasePoint inter, off;
   SplinePoint *hasnext, *hasprev;
   double xdiff, ydiff, len, len2;
   int bad=false;

   if (other->next==NULL && other->prev==NULL) {
      /* Fragments consisting of a single point do happen */ /* Unfortunately */
      SplinePointFree(other);
      return;
   }

   if (base->next==NULL) {
      hasnext=other;
      hasprev=base;
      base->next=other->next;
      base->next->from=base;
      base->nextcp=other->nextcp;
      base->nonextcp=other->nonextcp;
   } else {
      hasnext=base;
      hasprev=other;
      base->prev=other->prev;
      base->prevcp=other->prevcp;
      base->noprevcp=other->noprevcp;
      base->prev->to=base;
   }
   /* We probably won't have exactly the same location on each side to be joined */
   /* No matter. Extend in the same direction and find the point of intersection */
   /*  of the two slopes */
   if (!IntersectLines(&inter,
		       &hasnext->me,
		       hasnext->nonextcp?&hasnext->next->to->me:&hasnext->
		       nextcp, &hasprev->me,
		       hasprev->noprevcp?&hasprev->prev->from->
		       me:&hasprev->prevcp)) {
      bad=true;
   } else {
      xdiff=inter.x-base->me.x;
      ydiff=inter.y-base->me.y;
      len=xdiff * xdiff+ydiff * ydiff;
      if (len>9*resolution * resolution)
	 bad=true;
   }
   /* Well, if the intersection is extremely far away, then just average the */
   /*  two points */
   if (bad) {
      inter.x=(hasnext->me.x+hasprev->me.x)/2;
      inter.y=(hasnext->me.y+hasprev->me.y)/2;
   }

   /* Adjust control points for the new position */
   off.x=hasnext->nextcp.x-hasnext->me.x;
   off.y=hasnext->nextcp.y-hasnext->me.y;
   xdiff=hasnext->next->to->me.x-hasnext->me.x;
   ydiff=hasnext->next->to->me.y-hasnext->me.y;
   len=xdiff * xdiff+ydiff * ydiff;
   xdiff=hasnext->next->to->me.x-inter.x;
   ydiff=hasnext->next->to->me.y-inter.y;
   len2=xdiff * xdiff+ydiff * ydiff;
   if (len != 0 && len2>len) {
      len=sqrt(len2/len);
      off.x *= len;
      off.y *= len;
   }
   base->nextcp.x=inter.x+off.x;
   base->nextcp.y=inter.y+off.y;

   /* And the prevcp */
   off.x=hasprev->prevcp.x-hasprev->me.x;
   off.y=hasprev->prevcp.y-hasprev->me.y;
   xdiff=hasprev->prev->from->me.x-hasprev->me.x;
   ydiff=hasprev->prev->from->me.y-hasprev->me.y;
   len=xdiff * xdiff+ydiff * ydiff;
   xdiff=hasprev->prev->from->me.x-inter.x;
   ydiff=hasprev->prev->from->me.y-inter.y;
   len2=xdiff * xdiff+ydiff * ydiff;
   if (len != 0 && len2>len) {
      len=sqrt(len2/len);
      off.x *= len;
      off.y *= len;
   }
   base->prevcp.x=inter.x+off.x;
   base->prevcp.y=inter.y+off.y;

   base->me=inter;
   SplineRefigure(base->next);
   SplineRefigure(base->prev);
   SplinePointCategorize(base);

   SplinePointFree(other);
}

static SplineSet *JoinFragments(SplineSet *fragments,SplineSet ** contours,
				double resolution) {
   double res2=resolution * resolution;
   SplineSet *prev, *cur, *test, *test2, *next, *prev2;
   double xdiff, ydiff;

   /* Remove any single point fragments */
   prev=NULL;
   for (cur=fragments; cur != NULL; cur=next) {
      next=cur->next;
      if (cur->first==cur->last && cur->first->prev==NULL) {
	 SplinePointListFree(cur);
	 if (prev==NULL)
	    fragments=next;
	 else
	    prev->next=next;
      } else
	 prev=cur;
   }
   prev=NULL;
   for (cur=fragments; cur != NULL; cur=next) {
      next=cur->next;
      prev2=prev;
      test2=NULL;
      for (test=cur; test != NULL; prev2=test, test=test->next) {
	 xdiff=cur->last->me.x-test->first->me.x;
	 ydiff=cur->last->me.y-test->first->me.y;
	 if (xdiff * xdiff+ydiff * ydiff <= res2)
	    break;
      }
      if (test==cur &&
	  (test->first->next==NULL
	   || test->first->next->to==test->last)) {
	 /* The fragment is smaller than the resolution, so just get rid of it */
	 if (prev==NULL)
	    fragments=next;
	 else
	    prev->next=next;
	 cur->next=NULL;
	 SplinePointListFree(cur);
	 continue;
      }
      if (test==NULL) {
	 prev2=prev;
	 for (test2=cur; test2 != NULL; prev2=test2, test2=test2->next) {
	    xdiff=cur->first->me.x-test2->last->me.x;
	    ydiff=cur->first->me.y-test2->last->me.y;
	    if (xdiff * xdiff+ydiff * ydiff <= res2)
	       break;
	 }
      }
      if (test != NULL || test2 != NULL) {
	 if (test != NULL) {
	    PointJoint(cur->last, test->first, resolution);
	    if (cur==test)
	       cur->first=cur->last;
	    else
	       cur->last=test->last;
	 } else {
	    PointJoint(cur->first, test2->last, resolution);
	    if (cur==test2)
	       cur->last=cur->first;
	    else
	       cur->first=test2->first;
	    test=test2;
	 }
	 if (cur != test) {
	    test->first=test->last=NULL;
	    prev2->next=test->next;
	    if (next==test)
	       next=test->next;
	    SplinePointListFree(test);

	    xdiff=cur->last->me.x-cur->first->me.x;
	    ydiff=cur->last->me.y-cur->first->me.y;
	    if (xdiff * xdiff+ydiff * ydiff <= res2) {
	       PointJoint(cur->first, cur->last, resolution);
	       cur->last=cur->first;
	    }
	 }
	 if (cur->last==cur->first) {
	    if (prev==NULL)
	       fragments=next;
	    else
	       prev->next=next;
	    cur->next=*contours;
	    *contours=cur;
	 } else
	    prev=cur;
      } else
	 prev=cur;
   }
   return (fragments);
}

static int Linelike(SplineSet *ss,double resolution) {
   BasePoint slope;
   double len, dot;
   Spline *s;

   if (ss->first->prev != NULL)
      return (false);

   if (ss->first->next==ss->last->prev)
      return (true);

   slope.x=ss->last->me.x-ss->first->me.x;
   slope.y=ss->last->me.y-ss->first->me.y;
   len=slope.x * slope.x+slope.y * slope.y;
   if (len==0)
      return (false);
   len=sqrt(len);
   slope.x /= len;
   slope.y /= len;
   for (s=ss->first->next; s != NULL; s=s->to->next) {
      dot=slope.y * (s->from->nextcp.x-ss->first->me.x) -
	 slope.x * (s->from->nextcp.y-ss->first->me.y);
      if (dot>resolution || dot<-resolution)
	 return (false);
      dot=slope.y * (s->to->prevcp.x-ss->first->me.x) -
	 slope.x * (s->to->prevcp.y-ss->first->me.y);
      if (dot>resolution || dot<-resolution)
	 return (false);
      dot=slope.y * (s->to->me.x-ss->first->me.x) -
	 slope.x * (s->to->me.y-ss->first->me.y);
      if (dot>resolution || dot<-resolution)
	 return (false);
   }
   return (true);
}

/* This routine is a set of hacks to handle things I can't figure out how to */
/*  fix the right way */
static SplineSet *EdgeEffects(SplineSet *fragments,StrokeContext *c) {
   SplineSet *cur, *test, *prev, *next;
   double r2;
   Spline *s;

/* If we have a very thin horizontal stem, so thin that the inside stroke is */
/*  completely hidden, and if at the end of the stroke (say the main vertical*/
/*  stem of E) the two edges end at the same point, then we are left with two*/
/*  disconnected lines which overlap. The points don't get hidden because they*/
/*  are on the edge of the square pen, rather than inside it */

/* This has problems when resolution<1 */
   for (cur=fragments; cur != NULL; cur=cur->next) {
      SSRemoveColinearPoints(cur);
      if (!cur->last->prev->knownlinear)
	 continue;
      for (test=fragments; test != NULL; test=test->next) {
	 if (!test->first->next->knownlinear)
	    continue;
	 if (BpColinear
	     (&cur->last->me, &test->first->me, &cur->last->prev->from->me)
	     && BpColinear(&test->first->me, &cur->last->me,
			   &test->first->next->to->me)) {
	    test->first->me=cur->last->me;
	    test->first->nextcp=cur->last->me;
	    test->first->nonextcp=true;
	    test->first->next->to->prevcp=test->first->next->to->me;
	    test->first->next->to->noprevcp=true;
	    SplineRefigure(test->first->next);
	    break;
	 }
      }
   }

   /* We can also get single small segments where the other side gets hidden */
   /* If we get to this point we know there isn't a good match, so just toss 'em */
   prev=NULL;
   for (cur=fragments; cur != NULL; cur=next) {
      next=cur->next;
      if (!Linelike(cur, 1.0))
	 prev=cur;
      else {
	 if (prev==NULL)
	    fragments=next;
	 else
	    prev->next=next;
	 cur->next=NULL;
	 SplinePointListFree(cur);
      }
   }

   /* And sometimes we are just missing a chunk */
   r2=(c->pentype==pt_poly)?c->longest_edge:2*c->radius;
   r2 += 2*c->resolution;	/* Add a little fudge */
   r2 *= r2;
   for (cur=fragments; cur != NULL; cur=cur->next) {
      double xdiff=cur->last->me.x-cur->first->me.x;
      double ydiff=cur->last->me.y-cur->first->me.y;
      double len=xdiff * xdiff+ydiff * ydiff;

      if (len != 0 && len <= r2) {
	 SplinePoint *sp =
	    SplinePointCreate(cur->first->me.x, cur->first->me.y);
	 SplineMake3(cur->last, sp);
	 cur->last=sp;
      } else if (len != 0) {
	 double t=-1;

	 for (s=cur->last->prev; s != NULL && s != cur->first->next;
	      s=s->from->prev) {
	    t=SplineNearPoint(s, &cur->first->me, c->resolution);
	    if (t != -1) {
	       SplinePoint *sp=SplineBisect(s, t), *sp2;

	       sp2=chunkalloc(sizeof(SplinePoint));
	       *sp2=*sp;
	       sp->next=NULL;
	       sp2->prev=NULL;
	       sp2->next->from=sp2;
	       next=chunkalloc(sizeof(SplineSet));
	       *next=*cur;
	       cur->last=sp;
	       next->first=sp2;
	       cur->next=next;
	       break;
	    }
	 }
	 if (t==-1) {
	    for (s=cur->first->next; s != NULL && s != cur->last->prev;
		 s=s->to->next) {
	       t=SplineNearPoint(s, &cur->last->me, c->resolution);
	       if (t != -1) {
		  SplinePoint *sp=SplineBisect(s, t), *sp2;

		  sp2=chunkalloc(sizeof(SplinePoint));
		  *sp2=*sp;
		  sp->prev=NULL;
		  sp2->next=NULL;
		  sp2->prev->to=sp2;
		  next=chunkalloc(sizeof(SplineSet));
		  *next=*cur;
		  cur->first=sp;
		  next->last=sp2;
		  cur->next=next;
		  break;
	       }
	    }
	 }
      }
   }
   return (fragments);
}

static int ReversedLines(Spline *line1,Spline *line2,SplinePoint ** start,
			 SplinePoint ** end) {
   BasePoint slope1, slope2;
   double len1, len2, normal_off;
   int f1, f2, t1, t2;

   slope1.x=line1->to->me.x-line1->from->me.x;
   slope1.y=line1->to->me.y-line1->from->me.y;
   slope2.x=line2->to->me.x-line2->from->me.x;
   slope2.y=line2->to->me.y-line2->from->me.y;
   if (slope1.x * slope2.x+slope1.y * slope2.y >= 0)
      return (false);		/* Lines go vaguely in the same direction, not reversed */
   len1=sqrt(slope1.x * slope1.x+slope1.y * slope1.y);
   len2=sqrt(slope2.x * slope2.x+slope2.y * slope2.y);
   if (len1==0 || len2==0)
      return (false);		/* zero length lines are just points. Ignore */

   normal_off=slope1.x * slope2.y/len2-slope1.y * slope2.x/len2;
   if (normal_off<-.1 || normal_off>.1)
      return (false);		/* Not parallel */
   normal_off=slope2.x * slope1.y/len1-slope2.y * slope1.x/len1;
   if (normal_off<-.1 || normal_off>.1)
      return (false);		/* Not parallel */

   /* OK, the lines are parallel, and point in the right direction, but do they */
   /*  intersect? */
   f2=BpWithin(&line1->from->me, &line2->from->me, &line1->to->me);
   t2=BpWithin(&line1->from->me, &line2->to->me, &line1->to->me);
   if (f2 && t2) {
      *start=line2->to;
      *end=line2->from;
      return (true);
   }
   f1=BpWithin(&line2->from->me, &line1->from->me, &line2->to->me);
   if (f1 && f2) {
      *start=line1->from;
      *end=line2->from;
      return (true);
   } else if (f1 && t2) {
      *start=line1->from;
      *end=line2->to;
      return (true);
   }
   t1=BpWithin(&line2->from->me, &line1->to->me, &line2->to->me);
   if (f1 && t1) {
      *start=line1->from;
      *end=line1->to;
      return (true);
   } else if (t1 && f2) {
      *start=line2->from;
      *end=line1->to;
      return (true);
   } else if (t1 && t2) {
      *start=line2->to;
      *end=line1->to;
      return (true);
   }

   return (false);
}

static SplinePoint *SplineInsertPoint(Spline *s,SplinePoint *sp) {
   SplinePoint *to=s->to;

   s->from->nonextcp=true;
   to->noprevcp=true;
   if (sp->me.x==s->from->me.x && sp->me.y==s->from->me.y)
      return (s->from);
   if (sp->me.x==to->me.x && sp->me.y==to->me.y)
      return (to);

   sp=SplinePointCreate(sp->me.x, sp->me.y);
   s->to=sp;
   sp->prev=s;
   SplineMake3(sp, to);
   return (sp);
}

static void BreakLine(Spline *s,SplinePoint *begin,SplinePoint *end,
		      SplinePoint ** newbegin, SplinePoint ** newend) {
   if (s->from->me.x==begin->me.x && s->from->me.y==begin->me.y)
      *newbegin=s->from;
   else {
      *newbegin=SplineInsertPoint(s, begin);
      s=(*newbegin)->next;
   }
   if (s->to->me.x==end->me.x && s->to->me.y==end->me.y)
      *newend=s->to;
   else {
      *newend=SplineInsertPoint(s, end);
   }
}

static SplineSet *RemoveBackForthLine(SplineSet *ss) {
   SplinePoint *sp, *start, *end /*, *nsp, *psp */ ;
   SplineSet *other, *test;
   SplinePoint *sp2, *first1, *first2, *second1, *second2;

   /*int removed=true; */

   ss->next=NULL;

   SSRemoveColinearPoints(ss);

   if (ss->first->prev==NULL)
      return (ss);
   if (ss->first->next->to==ss->first && ss->first->next->knownlinear) {
      /* Entire splineset is a single point */ /* Remove it all */
      SplinePointListFree(ss);
      return (NULL);
   }

   /* OK, here we've gotten rid of all the places where the line doubles back */
   /*  mirrored by one of its end-points. But there could be line segments */
   /*  which double back on another segment somewhere else in the contour. */
   /* Removing them means splitting the contour in two */
   for (sp=ss->first;;) {
      if (sp->next->knownlinear)
	 for (sp2=sp->next->to;;) {
	    if (sp2->next->knownlinear &&
		ReversedLines(sp->next, sp2->next, &start, &end)) {
	       int isnext=sp->next->to==sp2, isprev =
		  sp->prev->from==sp2;
	       BreakLine(sp->next, start, end, &first1, &second1);
	       BreakLine(sp2->next, end, start, &first2, &second2);
	       if (first1->me.x != second2->me.x
		   || first1->me.y != second2->me.y) {
		  ErrorMsg(2,"Confusion reighns!\n");
		  return (ss);
	       }
	       if (second1->me.x != first2->me.x
		   || second1->me.y != first2->me.y) {
		  ErrorMsg(2,"Confusion regnas!\n");
		  return (ss);
	       }
	       if (isnext || isprev) {
		  /* SSRemoveColinearPoints should have caught this but */
		  /*  it has slightly different rounding conditions and sometimes doesn't */
		  if (!isnext) {
		     SplinePoint *t=sp2;

		     sp2=sp;
		     sp=t;
		     second2=second1;
		     first1=first2;
		  }
		  if (second1 != sp2 || first2 != sp2) {
		     ErrorMsg(2,"Confusion wiggles!\n\n");
		     return (ss);
		  }
		  SplineFree(sp2->prev);
		  SplineFree(sp2->next);
		  SplinePointFree(sp2);
		  first1->next=second2->next;
		  first1->nextcp=second2->nextcp;
		  first1->nonextcp=second2->nonextcp;
		  first1->next->from=first1;
		  SplinePointFree(second2);
		  break;
	       }
	       SplineFree(first1->next);
	       SplineFree(first2->next);
	       other=chunkalloc(sizeof(SplineSet));
	       other->first=other->last=second1;
	       second1->prev=first2->prev;
	       second1->prevcp=first2->prevcp;
	       second1->noprevcp=first2->noprevcp;
	       second1->prev->to=second1;
	       SplinePointFree(first2);
	       first1->next=second2->next;
	       first1->nextcp=second2->nextcp;
	       first1->nonextcp=second2->nonextcp;
	       first1->next->from=first1;
	       SplinePointFree(second2);
	       ss->first=ss->last=first1;
	       ss=RemoveBackForthLine(ss);
	       other=RemoveBackForthLine(other);
	       if (ss==NULL)
		  ss=other;
	       else {
		  for (test=ss; test->next != NULL; test=test->next);
		  test->next=other;
	       }
	       return (ss);
	    }
	    sp2=sp2->next->to;
	    if (sp2==ss->first)
	       break;
	 }
      sp=sp->next->to;
      if (sp==ss->first)
	 break;
   }

   return (ss);
}

static SplineSet *SSRemoveBackForthLine(SplineSet *contours) {
   /* Similar to the above. If we have a stem which is exactly 2*radius wide */
   /*  then we will have a line running down the middle of the stem which */
   /*  encloses no area. Get rid of it. More complicated cases can occur (a */
   /*  plus sign where each stem is 2*radius, ... */
   SplineSet *prev, *next, *cur, *ret;

   prev=NULL;
   for (cur=contours; cur != NULL; cur=next) {
      next=cur->next;
      ret=RemoveBackForthLine(cur);
      if (ret==NULL) {
	 if (prev==NULL)
	    contours=next;
	 else
	    prev->next=next;
      } else if (cur != ret) {
	 if (prev==NULL)
	    contours=ret;
	 else
	    prev->next=ret;
      }
      if (ret != NULL) {
	 while (ret->next != NULL)
	    ret=ret->next;
	 prev=ret;
	 ret->next=next;
      }
   }
   return (contours);
}

#define MAX_TPOINTS	40

static int InterpolateTPoints(StrokeContext *c,int start_pos,int end_pos,
			      int isleft) {
   /* We have a short segment of the contour here. So short that there are */
   /*  very few points StrokePoints on it. If we've only got 1 StrokePoint */
   /*  we tend to get singular matrices. 2 StrokePoints just gives a bad */
   /*  approximation (or might), etc. */
   /* But we have the original spline. We can add as many points between */
   /*  start and end as we like */
   double start_t, end_t, t, diff_t;
   double start_x, end_x, x, diff_x, start_y, end_y, y, diff_y, r2;
   BasePoint slope, me;
   Spline *s;
   int lt, rt;
   int i, j;
   double len;

   if (start_pos==0)
      return (end_pos-start_pos);

   if (20 >= c->tmax)
      c->tpt=realloc(c->tpt, (c->tmax=20+MAX_TPOINTS)*sizeof(TPoint));

   if (c->all[start_pos].line) {
      me=c->all[start_pos-1].me;
      if (isleft) {
	 slope.x=(c->all[end_pos].left.x-me.x)/6;
	 slope.y=(c->all[end_pos].left.y-me.y)/6;
      } else {
	 slope.x=(c->all[end_pos].right.x-me.x)/6;
	 slope.y=(c->all[end_pos].right.y-me.y)/6;
      }
      for (i=0; i<5; ++i) {
	 c->tpt[i].x=me.x+slope.x * (i+1);
	 c->tpt[i].y=me.y+slope.y * (i+1);
	 c->tpt[i].t=(i+1)/6.0;
      }
      return (5);
   } else if (c->all[start_pos].circle) {
      me=c->all[start_pos].me;	/* Center */
      if (isleft) {
	 start_x=c->all[start_pos-1].left.x-me.x;
	 end_x=c->all[end_pos].left.x-me.x;
	 start_y=c->all[start_pos-1].left.y-me.y;
	 end_y=c->all[end_pos].left.y-me.y;
      } else {
	 start_x=c->all[start_pos-1].right.x-me.x;
	 end_x=c->all[end_pos].right.x-me.x;
	 start_y=c->all[start_pos-1].right.y-me.y;
	 end_y=c->all[end_pos].right.y-me.y;
      }
      if ((diff_x=end_x-start_x)<0)
	 diff_x=-diff_x;
      if ((diff_y=end_y-start_y)<0)
	 diff_y=-diff_y;
      /* By choosing the bigger difference, I insure the other coord will */
      /*  not cross over from negative to positive. Actually this is only */
      /*  true for small segments of the circle. But that's what we've got */
      r2=c->radius * c->radius;
      if (diff_y>diff_x) {
	 diff_y=(end_y-start_y)/11.0;
	 for (y=start_y+diff_y, i=0; i<10; ++i, y += diff_y) {
	    x=sqrt(r2-y * y);
	    if (start_x<0)
	       x=-x;
	    c->tpt[i].x=me.x+x;
	    c->tpt[i].y=me.y+y;
	    c->tpt[i].t=(i+1)/11.0;
	 }
      } else {
	 diff_x=(end_x-start_x)/11.0;
	 for (x=start_x+diff_x, i=0; i<10; ++i, x += diff_x) {
	    y=sqrt(r2-x * x);
	    if (start_y<0)
	       y=-y;
	    c->tpt[i].x=me.x+x;
	    c->tpt[i].y=me.y+y;
	    c->tpt[i].t=(i+1)/11.0;
	 }
      }
      return (10);
   }

   if (c->all[start_pos-1].t==c->all[end_pos].t ||
       c->all[start_pos-1].sp != c->all[end_pos].sp ||
       (isleft && c->all[start_pos-1].lt != c->all[end_pos].lt) ||
       (!isleft && c->all[start_pos-1].rt != c->all[end_pos].rt))
      return (end_pos-start_pos);	/* Well, nothing we can do here */

   start_t=c->all[start_pos-1].t;
   end_t=c->all[end_pos].t;
   diff_t=(end_t-start_t)/11;
   s=c->all[start_pos].sp;
   lt=c->all[start_pos].lt;
   rt=c->all[start_pos].rt;
   for (t=start_t+diff_t, j=i=0; i<10; ++i, t += diff_t) {
      me.x =
	 ((s->splines[0].a * t+s->splines[0].b)*t+s->splines[0].c)*t +
	 s->splines[0].d;
      me.y =
	 ((s->splines[1].a * t+s->splines[1].b)*t+s->splines[1].c)*t +
	 s->splines[1].d;
      slope.x =
	 (3*s->splines[0].a * t+2*s->splines[0].b)*t +
	 s->splines[0].c;
      slope.y =
	 (3*s->splines[1].a * t+2*s->splines[1].b)*t +
	 s->splines[1].c;
      len=slope.x * slope.x+slope.y * slope.y;
      if (len==0 && c->pentype==pt_circle)
	 continue;
      len=sqrt(len);
      slope.x /= len;
      slope.y /= len;
      if (isleft) {
	 if (c->pentype==pt_circle) {
	    c->tpt[j].x=me.x-c->radius * slope.y;
	    c->tpt[j].y=me.y+c->radius * slope.x;
	 } else if (c->pentype==pt_square) {
	    c->tpt[j].x=me.x+c->radius * SquareCorners[lt].x;
	    c->tpt[j].y=me.y+c->radius * SquareCorners[lt].y;
	 } else {
	    c->tpt[j].x=me.x+c->corners[lt].x;
	    c->tpt[j].y=me.y+c->corners[lt].y;
	 }
	 c->tpt[j++].t=(i+1)/11.0;
      } else {
	 if (c->pentype==pt_circle) {
	    c->tpt[j].x=me.x+c->radius * slope.y;
	    c->tpt[j].y=me.y-c->radius * slope.x;
	 } else if (c->pentype==pt_square) {
	    c->tpt[j].x=me.x+c->radius * SquareCorners[rt].x;
	    c->tpt[j].y=me.y+c->radius * SquareCorners[rt].y;
	 } else {
	    c->tpt[j].x=me.x+c->corners[rt].x;
	    c->tpt[j].y=me.y+c->corners[rt].y;
	 }
	 c->tpt[j++].t=(i+1)/11.0;
      }
   }
   return (j);
}

static SplineSet *ApproximateStrokeContours(StrokeContext *c) {
   int end_pos, i, start_pos=0, pos, ipos;
   SplinePoint *first=NULL, *last=NULL, *cur;
   SplineSet *ret=NULL;
   SplineSet *lfragments, *rfragments, *contours;
   int tot, jump, extras, skip, skip_cnt, newcontour;

   /* Normally there will be one contour along the left side, and one contour */
   /*  on the right side. If the original were a closed contour, then each of */
   /*  these new contours should also be closed. If an open contour then we  */
   /*  will probably want to join the two sides into one contour. BUT if the */
   /*  endpoints of an open contour are<radius apart, then the linecaps may */
   /*  merge and we end up with the closed contour case again. */
   /* More serious, if a contour double backs on itself it may squeeze itself */
   /*  out of existance so we might end up with several contour fragments. */
   /* So first collect fragments, then try to join them. */

   lfragments=NULL;

   /* handle the left side */
   if ((!c->remove_outer || c->open) && !AllHiddenLeft(c)) {
      pos=0;
      while (pos<c->cur-1) {
	 last=first=LeftPointFromContext(c, &pos, &newcontour);
	 while (newcontour && pos<c->cur) {
	    SplinePointFree(first);
	    start_pos=pos;
	    if (pos<c->cur)
	       last=first=LeftPointFromContext(c, &pos, &newcontour);
	    else {
	       last=first=NULL;
	       break;
	    }
	    if (pos<start_pos || (pos+start_pos==0)) {
	       /* Wrapped around */
	       SplinePointFree(first);
	       first=last=NULL;
	       pos=c->cur;
	       break;
	    }
	 }
	 for (; pos<c->cur-1 && !newcontour;) {
	    start_pos=pos+1;
	    for (i=start_pos;
		 i<c->cur && !c->all[i].left_hidden
		 && !c->all[i].needs_point_left; ++i);
	    end_pos=pos=i;
	    cur=LeftPointFromContext(c, &pos, &newcontour);
	    if (end_pos>start_pos && c->all[end_pos-1].left.x==cur->me.x
		&& c->all[end_pos-1].left.y==cur->me.y)
	       --end_pos;
	    tot=end_pos-start_pos;
	    jump=1;
	    extras=0;
	    skip=MAX_TPOINTS;
	    if (tot>MAX_TPOINTS) {
	       jump=(tot/MAX_TPOINTS);
	       extras=tot%MAX_TPOINTS;
	       skip=MAX_TPOINTS/(extras+1);
	       tot=MAX_TPOINTS;
	    }
	    if (tot >= c->tmax)
	       c->tpt =
		  realloc(c->tpt,
			  (c->tmax=tot+MAX_TPOINTS)*sizeof(TPoint));
	    /* There is really no point in having a huge number of data points */
	    /*  I don't need 1000 points to approximate the curve, 10 will probably */
	    /*  do. The extra points just slow us down (we need them for the */
	    /*  Hide checks, but no longer) */
	    ipos=start_pos;
	    skip_cnt=0;
	    for (i=0; i <= tot; ++i) {
	       TPoint *tpt=c->tpt+i;
	       StrokePoint *spt=c->all+ipos;

	       tpt->x=spt->left.x;
	       tpt->y=spt->left.y;
	       tpt->t =
		  (ipos-start_pos+1)/(double) (end_pos-start_pos +
						      1);
	       ipos += jump;
	       if (++skip_cnt>skip && extras>0) {
		  ++ipos;
		  --extras;
		  skip_cnt=0;
	       }
	       if (ipos>end_pos)
		  ipos=end_pos;
	    }
	    if (end_pos<start_pos+3)
	       tot=InterpolateTPoints(c, start_pos, end_pos, true);
	    if (end_pos != start_pos) {
	       ApproximateSplineFromPointsSlopes(last, cur, c->tpt, tot,
						 false);
	    } else
	       SplineMake3(last, cur);
	    last=cur;
	    if (pos<end_pos) {
	       pos=c->cur+1;
	       break;
	    }
	 }
	 if (first != NULL) {
	    ret=chunkalloc(sizeof(SplineSet));
	    ret->first=first;
	    ret->last=last;
	    ret->next=lfragments;
	    lfragments=ret;
	 }
      }
   }

   /* handle the right side */
   rfragments=NULL;
   if ((!c->remove_inner || c->open) && !AllHiddenRight(c)) {
      pos=0;
      while (pos<c->cur-1) {
	 last=first=RightPointFromContext(c, &pos, &newcontour);
	 while (newcontour && pos<c->cur) {
	    SplinePointFree(first);
	    start_pos=pos;
	    if (pos<c->cur)
	       last=first=RightPointFromContext(c, &pos, &newcontour);
	    else {
	       last=first=NULL;
	       break;
	    }
	    if (pos<start_pos || (pos+start_pos==0)) {
	       /* Wrapped around */
	       SplinePointFree(first);
	       first=last=NULL;
	       pos=c->cur;
	       break;
	    }
	 }
	 for (; pos<c->cur-1 && !newcontour;) {
	    start_pos=pos+1;
	    for (i=start_pos;
		 i<c->cur && !c->all[i].right_hidden
		 && !c->all[i].needs_point_right; ++i);
	    end_pos=pos=i;
	    cur=RightPointFromContext(c, &pos, &newcontour);
	    if (c->all[end_pos-1].right.x==cur->me.x
		&& c->all[end_pos-1].right.y==cur->me.y)
	       --end_pos;
	    tot=end_pos-start_pos;
	    jump=1;
	    extras=0;
	    skip=MAX_TPOINTS;
	    if (tot>MAX_TPOINTS) {
	       jump=(tot/MAX_TPOINTS);
	       extras=tot%MAX_TPOINTS;
	       skip=MAX_TPOINTS/(extras+1);
	       tot=MAX_TPOINTS;
	    }
	    if (tot >= c->tmax)
	       c->tpt =
		  realloc(c->tpt,
			  (c->tmax=tot+MAX_TPOINTS)*sizeof(TPoint));
	    ipos=start_pos;
	    skip_cnt=0;
	    for (i=0; i <= tot; ++i) {
	       TPoint *tpt=c->tpt+i;
	       StrokePoint *spt=c->all+ipos;

	       tpt->x=spt->right.x;
	       tpt->y=spt->right.y;
	       tpt->t =
		  (ipos-start_pos+1)/(double) (end_pos-start_pos +
						      1);
	       ipos += jump;
	       if (++skip_cnt>skip && extras>0) {
		  ++ipos;
		  --extras;
		  skip_cnt=0;
	       }
	       if (ipos>end_pos)
		  ipos=end_pos;
	    }
	    if (end_pos<start_pos+3)
	       tot=InterpolateTPoints(c, start_pos, end_pos, false);
	    if (end_pos != start_pos) {
	       ApproximateSplineFromPointsSlopes(last, cur, c->tpt, tot,
						 false);
	    } else
	       SplineMake3(last, cur);
	    last=cur;
	    if (last->next != NULL)
	       last=last->next->to;
	    if (pos<end_pos) {
	       pos=c->cur+1;
	       break;
	    }
	 }
	 if (first != NULL) {
	    ret=chunkalloc(sizeof(SplineSet));
	    ret->first=first;
	    ret->last=last;
	    ret->next=rfragments;
	    rfragments=ret;
	 }
      }
   }

   if (c->open || !c->remove_outer) {
      for (ret=rfragments; ret != NULL; ret=ret->next)
	 SplineSetReverse(ret);
   }

   contours=NULL;
   lfragments=JoinFragments(lfragments, &contours, 0);
   rfragments=JoinFragments(rfragments, &contours, 0);
   lfragments=JoinFragments(lfragments, &contours, c->resolution);
   rfragments=JoinFragments(rfragments, &contours, c->resolution);
   /* I handle lfragments and rfragments separately up to this point */
   /*  as an optimization. An lfragment should not join an rfragment */
   /*  (except in the case of an open contour which does not join itself) */
   /*  so there are fewer things to check (n^2 check) if we do it separately */
   /* But everything should join on the first pass, except for an open */
   /*  contour. So now forget about optimizations and just deal with any */
   /*  remainders in one big lump. Also catch the open contour case */
   if (lfragments != NULL) {
      for (ret=lfragments; ret->next != NULL; ret=ret->next);
      ret->next=rfragments;
      lfragments=JoinFragments(lfragments, &contours, 0);
   } else
      lfragments=rfragments;
   for (i=2; lfragments != NULL && i<c->radius/2; ++i)
      lfragments=JoinFragments(lfragments, &contours, i * c->resolution);
   lfragments=EdgeEffects(lfragments, c);
   lfragments=JoinFragments(lfragments, &contours, c->resolution);
   lfragments=EdgeEffects(lfragments, c);
   lfragments=JoinFragments(lfragments, &contours, c->resolution);
   if (lfragments != NULL) {
      ErrorMsg(2,(glyphname==NULL)?"Some fragments did not join\n":
	       "Some fragments did not join in %s\n", glyphname);
      for (ret=lfragments; ret->next != NULL; ret=ret->next);
      ret->next=contours;
      contours=lfragments;
   }
   contours=SSRemoveBackForthLine(contours);

   return (contours);
}

static SplineSet *SplineSet_Stroke(SplineSet *ss,struct strokecontext *c,
				   int order2) {
   SplineSet *base;
   SplineSet *ret;

   base=SplinePointListCopy(ss);
   base=SSRemoveZeroLengthSplines(base);	/* Hard to get a slope for a zero length spline, that causes problems */
   if (base==NULL)
      return (NULL);
   if (c->transform_needed)
      base=SplinePointListTransform(base, c->transform, tpt_AllPoints);
   if (base->first->next==NULL)
      ret=SinglePointStroke(base->first, c);
   else {
      if (c->cur != 0)
	 memset(c->all, 0, c->cur * sizeof(StrokePoint));
      c->cur=0;
      c->ecur=0;
      c->open=base->first->prev==NULL;
      switch (c->pentype) {
	case pt_circle:
	   FindStrokePointsCircle(base, c);
	   break;
	case pt_square:
	   FindStrokePointsSquare(base, c);
	   break;
	case pt_poly:
	   FindStrokePointsPoly(base, c);
	   break;
      }
      HideSomeMorePoints(c);
      AddUnhiddenPoints(c);
      ret=ApproximateStrokeContours(c);
   }
   if (c->transform_needed)
      ret=SplinePointListTransform(ret, c->inverse, tpt_AllPoints);
   if (order2)
      ret=SplineSetsConvertOrder(ret, order2);
   SplinePointListFree(base);
   return (ret);
}

static SplineSet *SplineSets_Stroke(SplineSet *ss,struct strokecontext *c,
				    int order2) {
   SplineSet *first=NULL, *last=NULL, *cur;

   while (ss != NULL) {
      cur=SplineSet_Stroke(ss, c, order2);
      if (first==NULL)
	 first=last=cur;
      else
	 last->next=cur;
      if (last != NULL)
	 while (last->next != NULL)
	    last=last->next;
      ss=ss->next;
   }
   return (first);
}

SplineSet *SplineSetStroke(SplineSet * ss, StrokeInfo * si, int order2) {
   StrokeContext c;
   SplineSet *first, *last, *cur, *ret, *active, *anext;
   SplinePoint *sp, *nsp;
   int n, max;
   double d2, maxd2, len, maxlen;
   DBounds b;
   BasePoint center;
   double trans[6];

   if (si->stroke_type==si_centerline)
      ErrorMsg(2,"centerline not handled\n");

   memset(&c, 0, sizeof(c));
   c.resolution=si->resolution;
   if (si->resolution==0)
      c.resolution=1;
   c.pentype=si->stroke_type==si_std?pt_circle :
      si->stroke_type==si_caligraphic?pt_square:pt_poly;
   c.join=si->join;
   c.cap=si->cap;
   c.miterlimit =
      /* -cos(theta) */ -.98 /* theta=~11 degrees, PS miterlimit=10 */ ;
   c.radius=si->radius;
   c.radius2=si->radius * si->radius;
   c.remove_inner=si->removeinternal;
   c.remove_outer=si->removeexternal;
   c.leave_users_center=si->leave_users_center;
   c.scaled_or_rotated=si->factor != NULL;
   if (c.pentype==pt_circle || c.pentype==pt_square) {
      if (si->minorradius==0)
	 si->minorradius=si->radius;
      if (si->minorradius != si->radius || (si->penangle != 0 && si->stroke_type != si_std)) {	/* rotating a circle is irrelevant (rotating an elipse means something) */
	 double sn, co, factor;

	 c.transform_needed=true;
	 sn=sin(si->penangle);
	 co=cos(si->penangle);
	 factor=si->radius/si->minorradius;
	 c.transform[0]=c.transform[3]=co;
	 c.transform[1]=-sn;
	 c.transform[2]=sn;
	 c.transform[1] *= factor;
	 c.transform[3] *= factor;
	 c.inverse[0]=c.inverse[3]=co;
	 c.inverse[1]=sn;
	 c.inverse[2]=-sn;
	 c.inverse[3] /= factor;
	 c.inverse[2] /= factor;
      }
      if (si->resolution==0 && c.resolution>c.radius/3)
	 c.resolution=c.radius/3;
      ret=SplineSets_Stroke(ss, &c, order2);
   } else {
      first=last=NULL;
      max=0;
      memset(&center, 0, sizeof(center));
      for (active=si->poly; active != NULL; active=active->next) {
	 for (sp=active->first, n=0;;) {
	    ++n;
	    if (sp->next==NULL)
	       return (NULL);	/* That's an error, must be closed */
	    sp=sp->next->to;
	    if (sp==active->first)
	       break;
	 }
	 if (n>max)
	    max=n;
      }
      c.corners=malloc(max * sizeof(BasePoint));
      c.slopes=malloc(max * sizeof(BasePoint));
      memset(trans, 0, sizeof(trans));
      trans[0]=trans[3]=1;
      if (!c.leave_users_center) {
	 SplineSetQuickBounds(si->poly, &b);
	 trans[4]=-(b.minx+b.maxx)/2;
	 trans[5]=-(b.miny+b.maxy)/2;
	 SplinePointListTransform(si->poly, trans, tpt_AllPoints);
      }
      for (active=si->poly; active != NULL; active=anext) {
	 int reversed=false;

	 if (!SplinePointListIsClockwise(active)) {
	    reversed=true;
	    SplineSetReverse(active);
	 }
	 if (!c.scaled_or_rotated) {
	    anext=active->next;
	    active->next=NULL;
	    SplineSetQuickBounds(active, &b);
	    trans[4]=-(b.minx+b.maxx)/2;
	    trans[5]=-(b.miny+b.maxy)/2;
	    SplinePointListTransform(active, trans, tpt_AllPoints);	/* Only works if pen is fixed and does not rotate or get scaled */
	    active->next=anext;
	 }
	 maxd2=0;
	 maxlen=0;
	 for (sp=active->first, n=0;;) {
	    nsp=sp->next->to;
	    c.corners[n]=sp->me;
	    c.slopes[n].x=nsp->me.x-sp->me.x;
	    c.slopes[n].y=nsp->me.y-sp->me.y;
	    len =
	       c.slopes[n].x * c.slopes[n].x+c.slopes[n].y * c.slopes[n].y;
	    len=sqrt(len);
	    if (len>maxlen)
	       maxlen=len;
	    if (len != 0) {
	       c.slopes[n].x /= len;
	       c.slopes[n].y /= len;
	    }
	    d2=sp->me.x * sp->me.x+sp->me.y * sp->me.y;
	    if (d2>maxd2)
	       maxd2=d2;
	    ++n;
	    sp=nsp;
	    if (sp==active->first)
	       break;
	 }
	 c.n=n;
	 c.largest_distance2=maxd2;
	 c.longest_edge=maxlen;
	 c.radius=sqrt(maxd2);
	 c.radius2=maxd2;
	 if (si->resolution==0 && c.resolution>c.radius/3)
	    c.resolution=c.radius/3;
	 cur=SplineSets_Stroke(ss, &c, order2);
	 if (!c.scaled_or_rotated) {
	    trans[4]=-trans[4];
	    trans[5]=-trans[5];
	    SplinePointListTransform(cur, trans, tpt_AllPoints);
	    anext=active->next;
	    active->next=NULL;
	    SplinePointListTransform(active, trans, tpt_AllPoints);
	    active->next=anext;
	 }
	 if (reversed) {
	    SplineSet *ss;

	    for (ss=cur; ss != NULL; ss=ss->next)
	       SplineSetReverse(ss);
	    SplineSetReverse(active);
	 }
	 if (first==NULL)
	    first=last=cur;
	 else
	    last->next=cur;
	 if (last != NULL)
	    while (last->next != NULL)
	       last=last->next;
      }
      free(c.corners);
      free(c.slopes);
      ret=first;
   }
   free(c.all);
   free(c.tpt);
   return (ret);
}

#include "baseviews.h"

void FVStrokeItScript(void *_fv, StrokeInfo * si, int pointless_argument) {
   FontViewBase *fv=_fv;
   int layer=fv->active_layer;
   SplineSet *temp;
   int i, cnt=0, gid;
   SplineChar *sc;

   for (i=0; i<fv->map->enccount; ++i)
      if ((gid=fv->map->map[i]) != -1 && fv->sf->glyphs[gid] != NULL
	  && fv->selected[i])
	 ++cnt;

   SFUntickAll(fv->sf);
   for (i=0; i<fv->map->enccount; ++i) {
      if ((gid=fv->map->map[i]) != -1 && (sc=fv->sf->glyphs[gid]) != NULL
	  && !sc->ticked && fv->selected[i]) {
	 sc->ticked=true;
	 glyphname=sc->name;
	 if (sc->parent->multilayer) {
	    SCPreserveState(sc, false);
	    for (layer=ly_fore; layer<sc->layer_cnt; ++layer) {
	       temp =
		  SplineSetStroke(sc->layers[layer].splines, si,
				  sc->layers[layer].order2);
	       SplinePointListsFree(sc->layers[layer].splines);
	       sc->layers[layer].splines=temp;
	    }
	    SCCharChangedUpdate(sc, ly_all, true);
	 } else {
	    SCPreserveLayer(sc, layer, false);
	    temp =
	       SplineSetStroke(sc->layers[layer].splines, si,
			       sc->layers[layer].order2);
	    SplinePointListsFree(sc->layers[layer].splines);
	    sc->layers[layer].splines=temp;
	    SCCharChangedUpdate(sc, layer, true);
	 }
      }
   }
   glyphname=NULL;
}
