summaryrefslogtreecommitdiffstats
path: root/src/path/splinefit/splinefont.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/path/splinefit/splinefont.c1174
1 files changed, 1174 insertions, 0 deletions
diff --git a/src/path/splinefit/splinefont.c b/src/path/splinefit/splinefont.c
new file mode 100644
index 0000000..4681628
--- /dev/null
+++ b/src/path/splinefit/splinefont.c
@@ -0,0 +1,1174 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <math.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "splinefont.h"
+#include "splinefit.h"
+
+#define FONTFORGE_CONFIG_USE_DOUBLE 1
+
+bigreal BPDot(BasePoint v1, BasePoint v2) {
+ return v1.x * v2.x + v1.y * v2.y;
+}
+
+bigreal BPCross(BasePoint v1, BasePoint v2) {
+ return v1.x * v2.y - v1.y * v2.x;
+}
+
+BasePoint BPRev(BasePoint v) {
+ return (BasePoint) { -v.x, -v.y };
+}
+
+int RealWithin(real a,real b,real fudge) {
+ return( b>=a-fudge && b<=a+fudge );
+}
+
+BOOL RealNear(real a,real b) {
+ real d = a-b;
+#ifdef FONTFORGE_CONFIG_USE_DOUBLE
+ // These tighter equals-zero tests are retained for code tuned when
+ // passing zero as a constant
+ if ( a==0 )
+ return b>-1e-8 && b<1e-8;
+ if ( b==0 )
+ return a>-1e-8 && a<1e-8;
+
+ return d>-1e-6 && d<1e-6;
+#else /* For floats */
+ return d>-1e-5 && d<1e-5
+#endif
+}
+
+int RealApprox(real a,real b) {
+
+ if ( a==0 ) {
+ if ( b<.0001 && b>-.0001 )
+return( true );
+ } else if ( b==0 ) {
+ if ( a<.0001 && a>-.0001 )
+return( true );
+ } else {
+ a /= b;
+ if ( a>=.95 && a<=1.05 )
+return( true );
+ }
+return( false );
+}
+
+void LineListFree(LineList *ll) {
+ LineList *next;
+
+ while ( ll!=NULL ) {
+ next = ll->next;
+ chunkfree(ll,sizeof(LineList));
+ ll = next;
+ }
+}
+
+void LinearApproxFree(LinearApprox *la) {
+ LinearApprox *next;
+
+ while ( la!=NULL ) {
+ next = la->next;
+ LineListFree(la->lines);
+ chunkfree(la,sizeof(LinearApprox));
+ la = next;
+ }
+}
+
+void SplineFree(Spline *spline) {
+ LinearApproxFree(spline->approx);
+ chunkfree(spline,sizeof(Spline));
+}
+
+SplinePoint *SplinePointCreate(real x, real y) {
+ SplinePoint *sp;
+ if ( (sp=chunkalloc(sizeof(SplinePoint)))!=NULL ) {
+ sp->me.x = x; sp->me.y = y;
+ sp->nextcp = sp->prevcp = sp->me;
+ sp->nonextcp = sp->noprevcp = true;
+ sp->nextcpdef = sp->prevcpdef = false;
+ sp->ttfindex = sp->nextcpindex = 0xfffe;
+ sp->name = NULL;
+ }
+ return( sp );
+}
+
+void SplinePointsFree(SplinePointList *spl) {
+ Spline *first, *spline, *next;
+ int nonext;
+
+ if ( spl==NULL )
+ return;
+ if ( spl->first!=NULL ) {
+ nonext = spl->first->next==NULL; // If there is no spline, we set a flag.
+ first = NULL;
+ // We start on the first spline if it exists.
+ for ( spline = spl->first->next; spline!=NULL && spline!=first; spline = next ) {
+ next = spline->to->next; // Cache the location of the next spline.
+ SplinePointFree(spline->to); // Free the destination point.
+ SplineFree(spline); // Free the spline.
+ if ( first==NULL ) first = spline; // We want to avoid repeating the circuit.
+ }
+ // If the path is open or has no splines, free the starting point.
+ if ( spl->last!=spl->first || nonext )
+ SplinePointFree(spl->first);
+ }
+}
+
+void SplinePointListFree(SplinePointList *spl) {
+
+ if ( spl==NULL ) return;
+ SplinePointsFree(spl);
+ // free(spl->spiros);
+ free(spl->contour_name);
+ chunkfree(spl,sizeof(SplinePointList));
+}
+
+void SplineRefigure2(Spline *spline) {
+ SplinePoint *from = spline->from, *to = spline->to;
+ Spline1D *xsp = &spline->splines[0], *ysp = &spline->splines[1];
+ Spline old;
+
+#ifdef DEBUG
+ if ( RealNear(from->me.x,to->me.x) && RealNear(from->me.y,to->me.y))
+ IError("Zero length spline created");
+#endif
+ if ( spline->acceptableextrema )
+ old = *spline;
+
+ if ( ( from->nextcp.x==from->me.x && from->nextcp.y==from->me.y && from->nextcpindex>=0xfffe )
+ || ( to->prevcp.x==to->me.x && to->prevcp.y==to->me.y && from->nextcpindex>=0xfffe ) ) {
+ from->nonextcp = to->noprevcp = true;
+ from->nextcp = from->me;
+ to->prevcp = to->me;
+ } else {
+ from->nonextcp = to->noprevcp = false;
+ if ( from->nextcp.x==from->me.x && from->nextcp.y==from->me.y )
+ to->prevcp = from->me;
+ else if ( to->prevcp.x==to->me.x && to->prevcp.y==to->me.y )
+ from->nextcp = to->me;
+ }
+
+ if ( from->nonextcp && to->noprevcp )
+ /* Ok */;
+ else if ( from->nextcp.x!=to->prevcp.x || from->nextcp.y!=to->prevcp.y ) {
+ if ( RealNear(from->nextcp.x,to->prevcp.x) &&
+ RealNear(from->nextcp.y,to->prevcp.y)) {
+ from->nextcp.x = to->prevcp.x = (from->nextcp.x+to->prevcp.x)/2;
+ from->nextcp.y = to->prevcp.y = (from->nextcp.y+to->prevcp.y)/2;
+ } else {
+ IError("Invalid 2nd order spline in SplineRefigure2" );
+#ifndef GWW_TEST
+ /* I don't want these to go away when I'm debugging. I want to */
+ /* know how I got them */
+ from->nextcp.x = to->prevcp.x = (from->nextcp.x+to->prevcp.x)/2;
+ from->nextcp.y = to->prevcp.y = (from->nextcp.y+to->prevcp.y)/2;
+#endif
+ }
+ }
+
+ xsp->d = from->me.x; ysp->d = from->me.y;
+ if ( from->nonextcp && to->noprevcp ) {
+ spline->islinear = true;
+ xsp->c = to->me.x-from->me.x;
+ ysp->c = to->me.y-from->me.y;
+ xsp->a = xsp->b = 0;
+ ysp->a = ysp->b = 0;
+ } else {
+ /* from p. 393 (Operator Details, curveto) PostScript Lang. Ref. Man. (Red book) */
+ xsp->c = 2*(from->nextcp.x-from->me.x);
+ ysp->c = 2*(from->nextcp.y-from->me.y);
+ xsp->b = to->me.x-from->me.x-xsp->c;
+ ysp->b = to->me.y-from->me.y-ysp->c;
+ xsp->a = 0;
+ ysp->a = 0;
+ if ( RealNear(xsp->c,0)) xsp->c=0;
+ if ( RealNear(ysp->c,0)) ysp->c=0;
+ if ( RealNear(xsp->b,0)) xsp->b=0;
+ if ( RealNear(ysp->b,0)) ysp->b=0;
+ spline->islinear = false;
+ if ( ysp->b==0 && xsp->b==0 )
+ spline->islinear = true; /* This seems extremely unlikely... */
+ if ( from->nextcpselected || to->prevcpselected ) {
+ // The convention for tracking selection of quadratic control
+ // points is to use nextcpselected except at the tail of the
+ // list, where it's prevcpselected on the first point.
+ from->nextcpselected = true;
+ to->prevcpselected = false;
+ }
+ }
+ if ( isnan(ysp->b) || isnan(xsp->b) )
+ IError("NaN value in spline creation");
+ LinearApproxFree(spline->approx);
+ spline->approx = NULL;
+ spline->knowncurved = false;
+ spline->knownlinear = spline->islinear;
+ SplineIsLinear(spline);
+ spline->isquadratic = !spline->knownlinear;
+ spline->order2 = true;
+
+ if ( spline->acceptableextrema ) {
+ /* I don't check "d", because changes to that reflect simple */
+ /* translations which will not affect the shape of the spline */
+ /* (I don't check "a" because it is always 0 in a quadratic spline) */
+ if ( !RealNear(old.splines[0].b,spline->splines[0].b) ||
+ !RealNear(old.splines[0].c,spline->splines[0].c) ||
+ !RealNear(old.splines[1].b,spline->splines[1].b) ||
+ !RealNear(old.splines[1].c,spline->splines[1].c) )
+ spline->acceptableextrema = false;
+ }
+}
+
+Spline *SplineMake(SplinePoint *from, SplinePoint *to, int order2) {
+ if (order2 > 0)
+return( SplineMake2(from,to));
+ else
+return( SplineMake3(from,to));
+}
+
+Spline *SplineMake2(SplinePoint *from, SplinePoint *to) {
+ Spline *spline = chunkalloc(sizeof(Spline));
+
+ spline->from = from; spline->to = to;
+ from->next = to->prev = spline;
+ spline->order2 = true;
+ SplineRefigure2(spline);
+return( spline );
+}
+
+Spline *SplineMake3(SplinePoint *from, SplinePoint *to) {
+ Spline *spline = chunkalloc(sizeof(Spline));
+
+ spline->from = from; spline->to = to;
+ from->next = to->prev = spline;
+ SplineRefigure3(spline);
+return( spline );
+}
+
+void SplinePointFree(SplinePoint *sp) {
+ // chunkfree(sp->hintmask,sizeof(HintMask));
+ free(sp->name);
+ chunkfree(sp,sizeof(SplinePoint));
+}
+
+void SplineRefigure(Spline *spline) {
+ if ( spline==NULL )
+return;
+ if ( spline->order2 )
+ SplineRefigure2(spline);
+ else
+ SplineRefigure3(spline);
+}
+
+# define RE_NearZero .00000001
+# define RE_Factor (1024.0*1024.0*1024.0*1024.0*1024.0*2.0) /* 52 bits => divide by 2^51 */
+
+int Within16RoundingErrors(bigreal v1, bigreal v2) {
+ bigreal temp=v1*v2;
+ bigreal re;
+
+ if ( temp<0 ) /* Ok, if the two values are on different sides of 0 there */
+return( false ); /* is no way they can be within a rounding error of each other */
+ else if ( temp==0 ) {
+ if ( v1==0 )
+return( v2<RE_NearZero && v2>-RE_NearZero );
+ else
+return( v1<RE_NearZero && v1>-RE_NearZero );
+ } else if ( v1>0 ) {
+ if ( v1>v2 ) { /* Rounding error from the biggest absolute value */
+ re = v1/ (RE_Factor/16);
+return( v1-v2 < re );
+ } else {
+ re = v2/ (RE_Factor/16);
+return( v2-v1 < re );
+ }
+ } else {
+ if ( v1<v2 ) {
+ re = v1/ (RE_Factor/16); /* This will be a negative number */
+return( v1-v2 > re );
+ } else {
+ re = v2/ (RE_Factor/16);
+return( v2-v1 > re );
+ }
+ }
+}
+
+/* An IEEE double has 52 bits of precision. So one unit of rounding error will be */
+/* the number divided by 2^51 */
+# define D_RE_Factor (1024.0*1024.0*1024.0*1024.0*1024.0*2.0)
+/* But that's not going to work near 0, so, since the t values we care about */
+/* are [0,1], let's use 1.0/D_RE_Factor */
+
+double CheckExtremaForSingleBitErrors(const Spline1D *sp, double t, double othert) {
+ double u1, um1;
+ double slope, slope1, slopem1;
+ int err;
+ double diff, factor;
+
+ if ( t<0 || t>1 )
+return( t );
+
+ factor = t*0x40000/D_RE_Factor;
+ if ( (diff = t-othert)<0 ) diff= -diff;
+ if ( factor>diff/4 && diff!=0 ) /* This little check is to insure we don't skip beyond the well of this extremum into the next */
+ factor = diff/4;
+
+ slope = (3*(double) sp->a*t+2*sp->b)*t+sp->c;
+ if ( slope<0 ) slope = -slope;
+
+ for ( err = 0x40000; err!=0; err>>=1 ) {
+ u1 = t+factor;
+ slope1 = (3*(double) sp->a*u1+2*sp->b)*u1+sp->c;
+ if ( slope1<0 ) slope1 = -slope1;
+
+ um1 = t-factor;
+ slopem1 = (3*(double) sp->a*um1+2*sp->b)*um1+sp->c;
+ if ( slopem1<0 ) slopem1 = -slopem1;
+
+ if ( slope1<slope && slope1<=slopem1 && u1<=1.0 ) {
+ t = u1;
+ } else if ( slopem1<slope && slopem1<=slope1 && um1>=0.0 ) {
+ t = um1;
+ }
+ factor /= 2.0;
+ }
+ /* that seems as good as it gets */
+
+return( t );
+}
+
+void SplineFindExtrema(const Spline1D *sp, extended *_t1, extended *_t2 ) {
+ extended t1= -1, t2= -1;
+ extended b2_fourac;
+
+ /* Find the extreme points on the curve */
+ /* Set to -1 if there are none or if they are outside the range [0,1] */
+ /* Order them so that t1<t2 */
+ /* If only one valid extremum it will be t1 */
+ /* (Does not check the end points unless they have derivative==0) */
+ /* (Does not check to see if d/dt==0 points are inflection points (rather than extrema) */
+ if ( sp->a!=0 ) {
+ /* cubic, possibly 2 extrema (possibly none) */
+ b2_fourac = 4*(extended) sp->b*sp->b - 12*(extended) sp->a*sp->c;
+ if ( b2_fourac>=0 ) {
+ b2_fourac = sqrt(b2_fourac);
+ t1 = (-2*sp->b - b2_fourac) / (6*sp->a);
+ t2 = (-2*sp->b + b2_fourac) / (6*sp->a);
+ t1 = CheckExtremaForSingleBitErrors(sp,t1,t2);
+ t2 = CheckExtremaForSingleBitErrors(sp,t2,t1);
+ if ( t1>t2 ) { extended temp = t1; t1 = t2; t2 = temp; }
+ else if ( t1==t2 ) t2 = -1;
+ if ( RealNear(t1,0)) t1=0; else if ( RealNear(t1,1)) t1=1;
+ if ( RealNear(t2,0)) t2=0; else if ( RealNear(t2,1)) t2=1;
+ if ( t2<=0 || t2>=1 ) t2 = -1;
+ if ( t1<=0 || t1>=1 ) { t1 = t2; t2 = -1; }
+ }
+ } else if ( sp->b!=0 ) {
+ /* Quadratic, at most one extremum */
+ t1 = -sp->c/(2.0*(extended) sp->b);
+ if ( t1<=0 || t1>=1 ) t1 = -1;
+ } else /*if ( sp->c!=0 )*/ {
+ /* linear, no extrema */
+ }
+ *_t1 = t1; *_t2 = t2;
+}
+
+int IntersectLines(BasePoint *inter,
+ BasePoint *line1_1, BasePoint *line1_2,
+ BasePoint *line2_1, BasePoint *line2_2) {
+ // A lot of functions call this with the same address as an input and the output.
+ // In order to avoid unexpected behavior, we delay writing to the output until the end.
+ bigreal s1, s2;
+ BasePoint _output;
+ BasePoint * output = &_output;
+ if ( line1_1->x == line1_2->x ) {
+ // Line 1 is vertical.
+ output->x = line1_1->x;
+ if ( line2_1->x == line2_2->x ) {
+ // Line 2 is vertical.
+ if ( line2_1->x!=line1_1->x )
+ return( false ); /* Parallel vertical lines */
+ output->y = (line1_1->y+line2_1->y)/2;
+ } else {
+ output->y = line2_1->y + (output->x-line2_1->x) * (line2_2->y - line2_1->y)/(line2_2->x - line2_1->x);
+ }
+ *inter = *output;
+ return( true );
+ } else if ( line2_1->x == line2_2->x ) {
+ // Line 2 is vertical, but we know that line 1 is not.
+ output->x = line2_1->x;
+ output->y = line1_1->y + (output->x-line1_1->x) * (line1_2->y - line1_1->y)/(line1_2->x - line1_1->x);
+ *inter = *output;
+ return( true );
+ } else {
+ // Both lines are oblique.
+ s1 = (line1_2->y - line1_1->y)/(line1_2->x - line1_1->x);
+ s2 = (line2_2->y - line2_1->y)/(line2_2->x - line2_1->x);
+ if ( RealNear(s1,s2)) {
+ if ( !RealNear(line1_1->y + (line2_1->x-line1_1->x) * s1,line2_1->y))
+ return( false );
+ output->x = (line1_2->x+line2_2->x)/2;
+ output->y = (line1_2->y+line2_2->y)/2;
+ } else {
+ output->x = (s1*line1_1->x - s2*line2_1->x - line1_1->y + line2_1->y)/(s1-s2);
+ output->y = line1_1->y + (output->x-line1_1->x) * s1;
+ }
+ *inter = *output;
+ return( true );
+ }
+}
+
+static int MinMaxWithin(Spline *spline) {
+ extended dx, dy;
+ int which;
+ extended t1, t2;
+ extended w;
+ /* We know that this "spline" is basically one dimensional. As long as its*/
+ /* extrema are between the start and end points on that line then we can */
+ /* treat it as a line. If the extrema are way outside the line segment */
+ /* then it's a line that backtracks on itself */
+
+ if ( (dx = spline->to->me.x - spline->from->me.x)<0 ) dx = -dx;
+ if ( (dy = spline->to->me.y - spline->from->me.y)<0 ) dy = -dy;
+ which = dx<dy;
+ SplineFindExtrema(&spline->splines[which],&t1,&t2);
+ if ( t1==-1 )
+return( true );
+ w = ((spline->splines[which].a*t1 + spline->splines[which].b)*t1
+ + spline->splines[which].c)*t1 + spline->splines[which].d;
+ if ( RealNear(w, (&spline->to->me.x)[which]) || RealNear(w, (&spline->from->me.x)[which]) )
+ /* Close enough */;
+ else if ( (w<(&spline->to->me.x)[which] && w<(&spline->from->me.x)[which]) ||
+ (w>(&spline->to->me.x)[which] && w>(&spline->from->me.x)[which]) )
+return( false ); /* Outside */
+
+ w = ((spline->splines[which].a*t2 + spline->splines[which].b)*t2
+ + spline->splines[which].c)*t2 + spline->splines[which].d;
+ if ( RealNear(w, (&spline->to->me.x)[which]) || RealNear(w, (&spline->from->me.x)[which]) )
+ /* Close enough */;
+ else if ( (w<(&spline->to->me.x)[which] && w<(&spline->from->me.x)[which]) ||
+ (w>(&spline->to->me.x)[which] && w>(&spline->from->me.x)[which]) )
+return( false ); /* Outside */
+
+return( true );
+}
+
+int SplineIsLinear(Spline *spline) {
+ bigreal t1,t2, t3,t4;
+ int ret;
+
+ if ( spline->knownlinear )
+return( true );
+ if ( spline->knowncurved )
+return( false );
+
+ if ( spline->splines[0].a==0 && spline->splines[0].b==0 &&
+ spline->splines[1].a==0 && spline->splines[1].b==0 )
+return( true );
+
+ /* Something is linear if the control points lie on the line between the */
+ /* two base points */
+
+ /* Vertical lines */
+ if ( RealNear(spline->from->me.x,spline->to->me.x) ) {
+ ret = RealNear(spline->from->me.x,spline->from->nextcp.x) &&
+ RealNear(spline->from->me.x,spline->to->prevcp.x);
+ if ( ret && ! ((spline->from->nextcp.y >= spline->from->me.y &&
+ spline->from->nextcp.y <= spline->to->me.y &&
+ spline->to->prevcp.y >= spline->from->me.y &&
+ spline->to->prevcp.y <= spline->to->me.y ) ||
+ (spline->from->nextcp.y <= spline->from->me.y &&
+ spline->from->nextcp.y >= spline->to->me.y &&
+ spline->to->prevcp.y <= spline->from->me.y &&
+ spline->to->prevcp.y >= spline->to->me.y )) )
+ ret = MinMaxWithin(spline);
+ /* Horizontal lines */
+ } else if ( RealNear(spline->from->me.y,spline->to->me.y) ) {
+ ret = RealNear(spline->from->me.y,spline->from->nextcp.y) &&
+ RealNear(spline->from->me.y,spline->to->prevcp.y);
+ if ( ret && ! ((spline->from->nextcp.x >= spline->from->me.x &&
+ spline->from->nextcp.x <= spline->to->me.x &&
+ spline->to->prevcp.x >= spline->from->me.x &&
+ spline->to->prevcp.x <= spline->to->me.x) ||
+ (spline->from->nextcp.x <= spline->from->me.x &&
+ spline->from->nextcp.x >= spline->to->me.x &&
+ spline->to->prevcp.x <= spline->from->me.x &&
+ spline->to->prevcp.x >= spline->to->me.x)) )
+ ret = MinMaxWithin(spline);
+ } else {
+ ret = true;
+ t1 = (spline->from->nextcp.y-spline->from->me.y)/(spline->to->me.y-spline->from->me.y);
+ t2 = (spline->from->nextcp.x-spline->from->me.x)/(spline->to->me.x-spline->from->me.x);
+ t3 = (spline->to->me.y-spline->to->prevcp.y)/(spline->to->me.y-spline->from->me.y);
+ t4 = (spline->to->me.x-spline->to->prevcp.x)/(spline->to->me.x-spline->from->me.x);
+ ret = (Within16RoundingErrors(t1,t2) || (RealApprox(t1,0) && RealApprox(t2,0))) &&
+ (Within16RoundingErrors(t3,t4) || (RealApprox(t3,0) && RealApprox(t4,0)));
+ if ( ret ) {
+ if ( t1<0 || t2<0 || t3<0 || t4<0 ||
+ t1>1 || t2>1 || t3>1 || t4>1 )
+ ret = MinMaxWithin(spline);
+ }
+ }
+ spline->knowncurved = !ret;
+ spline->knownlinear = ret;
+ if ( ret ) {
+ /* A few places that if the spline is knownlinear then its splines[?] */
+ /* are linear. So give the linear version and not that suggested by */
+ /* the control points */
+ spline->splines[0].a = spline->splines[0].b = 0;
+ spline->splines[0].d = spline->from->me.x;
+ spline->splines[0].c = spline->to->me.x-spline->from->me.x;
+ spline->splines[1].a = spline->splines[1].b = 0;
+ spline->splines[1].d = spline->from->me.y;
+ spline->splines[1].c = spline->to->me.y-spline->from->me.y;
+ }
+return( ret );
+}
+
+static bigreal FindZero5(bigreal w[7],bigreal tlow, bigreal thigh) {
+ /* Somewhere between tlow and thigh there is a value of t where w(t)==0 */
+ /* It is conceiveable that there might be 3 such ts if there are some high frequency effects */
+ /* but I ignore that for now */
+ bigreal t, test;
+ int bot_negative;
+
+ t = tlow;
+ test = ((((w[5]*t+w[4])*t+w[3])*t+w[2])*t+w[1])*t + w[0];
+ bot_negative = test<0;
+
+ for (;;) {
+ t = (thigh+tlow)/2;
+ if ( thigh==t || tlow==t )
+return( t ); /* close as we can get */
+ test = ((((w[5]*t+w[4])*t+w[3])*t+w[2])*t+w[1])*t + w[0];
+ if ( test==0 )
+return( t );
+ if ( bot_negative ) {
+ if ( test<0 )
+ tlow = t;
+ else
+ thigh = t;
+ } else {
+ if ( test<0 )
+ thigh = t;
+ else
+ tlow = t;
+ }
+ }
+}
+
+static bigreal FindZero3(bigreal w[7],bigreal tlow, bigreal thigh) {
+ /* Somewhere between tlow and thigh there is a value of t where w(t)==0 */
+ /* It is conceiveable that there might be 3 such ts if there are some high frequency effects */
+ /* but I ignore that for now */
+ bigreal t, test;
+ int bot_negative;
+
+ t = tlow;
+ test = ((w[3]*t+w[2])*t+w[1])*t + w[0];
+ bot_negative = test<0;
+
+ for (;;) {
+ t = (thigh+tlow)/2;
+ if ( thigh==t || tlow==t )
+return( t ); /* close as we can get */
+ test = ((w[3]*t+w[2])*t+w[1])*t + w[0];
+ if ( test==0 )
+return( t );
+ if ( bot_negative ) {
+ if ( test<0 )
+ tlow = t;
+ else
+ thigh = t;
+ } else {
+ if ( test<0 )
+ thigh = t;
+ else
+ tlow = t;
+ }
+ }
+}
+
+bigreal SplineMinDistanceToPoint(Spline *s, BasePoint *p) {
+ /* So to find the minimum distance we want the sqrt( (sx(t)-px)^2 + (sy(t)-py)^2 ) */
+ /* Same minima as (sx(t)-px)^2 + (sy(t)-py)^2, which is easier to deal with */
+ bigreal w[7];
+ Spline1D *x = &s->splines[0], *y = &s->splines[1];
+ bigreal off[2], best;
+
+ off[0] = (x->d-p->x); off[1] = (y->d-p->y);
+
+ w[6] = (x->a*x->a) + (y->a*y->a);
+ w[5] = 2*(x->a*x->b + y->a*y->b);
+ w[4] = (x->b*x->b) + 2*(x->a*x->c) + (y->b*y->b) + 2*(y->a*y->c);
+ w[3] = 2* (x->b*x->c + x->a*off[0] + y->b*y->c + y->a*off[1]);
+ w[2] = (x->c*x->c) + 2*(x->b*off[0]) + (y->c*y->c) + 2*y->b*off[1];
+ w[1] = 2*(x->c*off[0] + y->c*off[1]);
+ w[0] = off[0]*off[0] + off[1]*off[1];
+
+ /* Take derivative */
+ w[0] = w[1];
+ w[1] = 2*w[2];
+ w[2] = 3*w[3];
+ w[3] = 4*w[4];
+ w[4] = 5*w[5];
+ w[5] = 6*w[6];
+ w[6] = 0;
+
+ if ( w[5]!=0 ) {
+ bigreal tzeros[8], t, incr, test, lasttest, zerot;
+ int i, zcnt=0;
+ /* Well, we've got a 5th degree poly and no way to play cute tricks. */
+ /* brute force it */
+ incr = 1.0/1024;
+ lasttest = w[0];
+ for ( t = incr; t<=1.0; t += incr ) {
+ test = ((((w[5]*t+w[4])*t+w[3])*t+w[2])*t+w[1])*t + w[0];
+ if ( test==0 )
+ tzeros[zcnt++] = t;
+ else {
+ if ( lasttest!=0 && (test>0) != (lasttest>0) ) {
+ zerot = FindZero5(w,t-incr,t);
+ if ( zerot>0 )
+ tzeros[zcnt++] = zerot;
+ }
+ }
+ lasttest = test;
+ }
+ best = off[0]*off[0] + off[1]*off[1]; /* t==0 */
+ test = (x->a+x->b+x->c+off[0])*(x->a+x->b+x->c+off[0]) +
+ (y->a+y->b+y->c+off[1])*(y->a+y->b+y->c+off[1]); /* t==1 */
+ if ( best>test ) best = test;
+ for ( i=0; i<zcnt; ++i ) {
+ bigreal tx, ty;
+ tx = ((x->a*tzeros[i]+x->b)*tzeros[i]+x->c)*tzeros[i] + off[0];
+ ty = ((y->a*tzeros[i]+y->b)*tzeros[i]+y->c)*tzeros[i] + off[1];
+ test = tx*tx + ty*ty;
+ if ( best>test ) best = test;
+ }
+return( sqrt(best));
+ } else if ( w[4]==0 && w[3]!=0 ) {
+ /* Started with a quadratic -- now, find 0s of a cubic */
+ /* We could find the extrema, so we have a bunch of monotonics */
+ /* Or we could brute force it as above */
+ bigreal tzeros[8], test, zerot;
+ bigreal quad[3], disc, e[5], t1, t2;
+ int i, zcnt=0, ecnt;
+
+ quad[2] = 3*w[3]; quad[1] = 2*w[2]; quad[0] = w[1];
+ disc = (-quad[1]*quad[1] - 4*quad[2]*quad[0]);
+ e[0] = 0;
+ if ( disc<0 ) {
+ e[1] = 1.0;
+ ecnt = 2;
+ } else
+ disc = sqrt(disc);
+ t1 = (-w[1] - disc) / (2*w[2]);
+ t2 = (-w[1] + disc) / (2*w[2]);
+ if ( t1>t2 ) {
+ bigreal temp = t1;
+ t1 = t2;
+ t2 = temp;
+ }
+ ecnt=1;
+ if ( t1>0 && t1<1 )
+ e[ecnt++] = t1;
+ if ( t2>0 && t2<1 && t1!=t2 )
+ e[ecnt++] = t2;
+ e[ecnt++] = 1.0;
+ for ( i=1; i<ecnt; ++i ) {
+ zerot = FindZero3(w,e[i-1],e[i]);
+ if ( zerot>0 )
+ tzeros[zcnt++] = zerot;
+ }
+ best = off[0]*off[0] + off[1]*off[1]; /* t==0 */
+ test = (x->b+x->c+off[0])*(x->b+x->c+off[0]) +
+ (y->b+y->c+off[1])*(y->b+y->c+off[1]); /* t==1 */
+ if ( best>test ) best = test;
+ for ( i=0; i<zcnt; ++i ) {
+ bigreal tx, ty;
+ tx = (x->b*tzeros[i]+x->c)*tzeros[i] + off[0];
+ ty = (y->b*tzeros[i]+y->c)*tzeros[i] + off[1];
+ test = tx*tx + ty*ty;
+ if ( best>test ) best = test;
+ }
+return( sqrt(best));
+ } else if ( w[2]==0 && w[1]!=0 ) {
+ /* Started with a line */
+ bigreal t = -w[0]/w[1], test, best;
+ best = off[0]*off[0] + off[1]*off[1]; /* t==0 */
+ test = (x->c+off[0])*(x->c+off[0]) + (y->c+off[1])*(y->c+off[1]); /* t==1 */
+ if ( best>test ) best = test;
+ if ( t>0 && t<1 ) {
+ test = (x->c*t+off[0])*(x->c*t+off[0]) + (y->c*t+off[1])*(y->c*t+off[1]);
+ if ( best>test ) best = test;
+ }
+return(sqrt(best));
+ } else if ( w[4]!=0 && w[3]!=0 && w[2]!=0 && w[1]!=0 ) {
+ IError( "Impossible condition in SplineMinDistanceToPoint");
+ } else {
+ /* It's a point, minimum distance is the only distance */
+return( sqrt(off[0]*off[0] + off[1]*off[1]) );
+ }
+return( -1 );
+}
+
+/* This returns all real solutions, even those out of bounds */
+/* I use -999999 as an error flag, since we're really only interested in */
+/* solns near 0 and 1 that should be ok. -1 is perhaps a little too close */
+/* Sigh. When solutions are near 0, the rounding errors are appalling. */
+int _CubicSolve(const Spline1D *sp,bigreal sought, extended ts[3]) {
+ extended d, xN, yN, delta2, temp, delta, h, t2, t3, theta;
+ extended sa=sp->a, sb=sp->b, sc=sp->c, sd=sp->d-sought;
+ int i=0;
+
+ ts[0] = ts[1] = ts[2] = -999999;
+ if ( sd==0 && sa!=0 ) {
+ /* one of the roots is 0, the other two are the soln of a quadratic */
+ ts[0] = 0;
+ if ( sc==0 ) {
+ ts[1] = -sb/(extended) sa; /* two zero roots */
+ } else {
+ temp = sb*(extended) sb-4*(extended) sa*sc;
+ if ( RealNear(temp,0))
+ ts[1] = -sb/(2*(extended) sa);
+ else if ( temp>=0 ) {
+ temp = sqrt(temp);
+ ts[1] = (-sb+temp)/(2*(extended) sa);
+ ts[2] = (-sb-temp)/(2*(extended) sa);
+ }
+ }
+ } else if ( sa!=0 ) {
+ /* http://www.m-a.org.uk/eb/mg/mg077ch.pdf */
+ /* this nifty solution to the cubic neatly avoids complex arithmetic */
+ xN = -sb/(3*(extended) sa);
+ yN = ((sa*xN + sb)*xN+sc)*xN + sd;
+
+ delta2 = (sb*(extended) sb-3*(extended) sa*sc)/(9*(extended) sa*sa);
+ /*if ( RealWithin(delta2,0,.00000001) ) delta2 = 0;*/
+
+ /* the descriminant is yN^2-h^2, but delta might be <0 so avoid using h */
+ d = yN*yN - 4*sa*sa*delta2*delta2*delta2;
+ if ( ((yN>.01 || yN<-.01) && RealNear(d/yN,0)) || ((yN<=.01 && yN>=-.01) && RealNear(d,0)) )
+ d = 0;
+ if ( d>0 ) {
+ temp = sqrt(d);
+ t2 = (-yN-temp)/(2*sa);
+ t2 = (t2==0) ? 0 : (t2<0) ? -pow(-t2,1./3.) : pow(t2,1./3.);
+ t3 = (-yN+temp)/(2*sa);
+ t3 = t3==0 ? 0 : (t3<0) ? -pow(-t3,1./3.) : pow(t3,1./3.);
+ ts[0] = xN + t2 + t3;
+ } else if ( d<0 ) {
+ if ( delta2>=0 ) {
+ delta = sqrt(delta2);
+ h = 2*sa*delta2*delta;
+ temp = -yN/h;
+ if ( temp>=-1.0001 && temp<=1.0001 ) {
+ if ( temp<-1 ) temp = -1; else if ( temp>1 ) temp = 1;
+ theta = acos(temp)/3;
+ ts[i++] = xN+2*delta*cos(theta);
+ ts[i++] = xN+2*delta*cos(2.0943951+theta); /* 2*pi/3 */
+ ts[i++] = xN+2*delta*cos(4.1887902+theta); /* 4*pi/3 */
+ }
+ }
+ } else if ( /* d==0 && */ delta2!=0 ) {
+ delta = yN/(2*sa);
+ delta = delta==0 ? 0 : delta>0 ? pow(delta,1./3.) : -pow(-delta,1./3.);
+ ts[i++] = xN + delta; /* this root twice, but that's irrelevant to me */
+ ts[i++] = xN - 2*delta;
+ } else if ( /* d==0 && */ delta2==0 ) {
+ if ( xN>=-0.0001 && xN<=1.0001 ) ts[0] = xN;
+ }
+ } else if ( sb!=0 ) {
+ extended d = sc*(extended) sc-4*(extended) sb*sd;
+ if ( d<0 && RealNear(d,0)) d=0;
+ if ( d<0 )
+return(false); /* All roots imaginary */
+ d = sqrt(d);
+ ts[0] = (-sc-d)/(2*(extended) sb);
+ ts[1] = (-sc+d)/(2*(extended) sb);
+ } else if ( sc!=0 ) {
+ ts[0] = -sd/(extended) sc;
+ } else {
+ /* If it's a point then either everything is a solution, or nothing */
+ }
+return( ts[0]!=-999999 );
+}
+
+int _QuarticSolve(Quartic *q,extended ts[4]) {
+ extended extrema[5];
+ Spline1D sp;
+ int ecnt = 0, i, zcnt;
+
+ /* Two special cases */
+ if ( q->a==0 ) { /* It's really a cubic */
+ sp.a = q->b;
+ sp.b = q->c;
+ sp.c = q->d;
+ sp.d = q->e;
+ ts[3] = -999999;
+return( _CubicSolve(&sp,0,ts));
+ } else if ( q->e==0 ) { /* we can factor out a zero root */
+ sp.a = q->a;
+ sp.b = q->b;
+ sp.c = q->c;
+ sp.d = q->d;
+ ts[0] = 0;
+return( _CubicSolve(&sp,0,ts+1)+1);
+ }
+
+ sp.a = 4*q->a;
+ sp.b = 3*q->b;
+ sp.c = 2*q->c;
+ sp.d = q->d;
+ if ( _CubicSolve(&sp,0,extrema)) {
+ ecnt = 1;
+ if ( extrema[1]!=-999999 ) {
+ ecnt = 2;
+ if ( extrema[1]<extrema[0] ) {
+ extended temp = extrema[1]; extrema[1] = extrema[0]; extrema[0]=temp;
+ }
+ if ( extrema[2]!=-999999 ) {
+ ecnt = 3;
+ if ( extrema[2]<extrema[0] ) {
+ extended temp = extrema[2]; extrema[2] = extrema[0]; extrema[0]=temp;
+ }
+ if ( extrema[2]<extrema[1] ) {
+ extended temp = extrema[2]; extrema[2] = extrema[1]; extrema[1]=temp;
+ }
+ }
+ }
+ }
+ for ( i=ecnt-1; i>=0 ; --i )
+ extrema[i+1] = extrema[i];
+ /* Upper and lower bounds within which we'll search */
+ extrema[0] = -999;
+ extrema[ecnt+1] = 999;
+ ecnt += 2;
+ /* divide into monotonic sections & use binary search to find zeroes */
+ for ( i=zcnt=0; i<ecnt-1; ++i ) {
+ extended top, bottom, val;
+ extended topt, bottomt, t;
+ topt = extrema[i+1];
+ bottomt = extrema[i];
+ top = (((q->a*topt+q->b)*topt+q->c)*topt+q->d)*topt+q->e;
+ bottom = (((q->a*bottomt+q->b)*bottomt+q->c)*bottomt+q->d)*bottomt+q->e;
+ if ( top<bottom ) {
+ extended temp = top; top = bottom; bottom = temp;
+ temp = topt; topt = bottomt; bottomt = temp;
+ }
+ if ( bottom>.001 ) /* this monotonic is all above 0 */
+ continue;
+ if ( top<-.001 ) /* this monotonic is all below 0 */
+ continue;
+ if ( bottom>0 ) {
+ ts[zcnt++] = bottomt;
+ continue;
+ }
+ if ( top<0 ) {
+ ts[zcnt++] = topt;
+ continue;
+ }
+ for (;;) {
+ t = (topt+bottomt)/2;
+ if ( isnan(t) ) {
+ break;
+ } else if ( t==topt || t==bottomt ) {
+ ts[zcnt++] = t;
+ break;
+ }
+
+ val = (((q->a*t+q->b)*t+q->c)*t+q->d)*t+q->e;
+ if ( val>-.0001 && val<.0001 ) {
+ ts[zcnt++] = t;
+ break;
+ } else if ( val>0 ) {
+ top = val;
+ topt = t;
+ } else {
+ bottom = val;
+ bottomt = t;
+ }
+ }
+ }
+ for ( i=zcnt; i<4; ++i )
+ ts[i] = -999999;
+return( zcnt );
+}
+
+/* calculating the actual length of a spline is hard, this gives a very */
+/* rough (but quick) approximation */
+static bigreal SplineLenApprox(Spline *spline) {
+ bigreal len, slen, temp;
+
+ if ( (temp = spline->to->me.x-spline->from->me.x)<0 ) temp = -temp;
+ len = temp;
+ if ( (temp = spline->to->me.y-spline->from->me.y)<0 ) temp = -temp;
+ len += temp;
+ if ( !spline->to->noprevcp || !spline->from->nonextcp ) {
+ if ( (temp = spline->from->nextcp.x-spline->from->me.x)<0 ) temp = -temp;
+ slen = temp;
+ if ( (temp = spline->from->nextcp.y-spline->from->me.y)<0 ) temp = -temp;
+ slen += temp;
+ if ( (temp = spline->to->prevcp.x-spline->from->nextcp.x)<0 ) temp = -temp;
+ slen += temp;
+ if ( (temp = spline->to->prevcp.y-spline->from->nextcp.y)<0 ) temp = -temp;
+ slen += temp;
+ if ( (temp = spline->to->me.x-spline->to->prevcp.x)<0 ) temp = -temp;
+ slen += temp;
+ if ( (temp = spline->to->me.y-spline->to->prevcp.y)<0 ) temp = -temp;
+ slen += temp;
+ len = (len + slen)/2;
+ }
+return( len );
+}
+
+FitPoint *SplinesFigureFPsBetween(SplinePoint *from, SplinePoint *to,
+ int *tot) {
+ int cnt, i, j, pcnt;
+ bigreal len, slen, lbase;
+ SplinePoint *np;
+ FitPoint *fp;
+ bigreal _lens[10], *lens = _lens;
+ int _cnts[10], *cnts = _cnts;
+ /* I used just to give every spline 10 points. But that gave much more */
+ /* weight to small splines than to big ones */
+
+ cnt = 0;
+ for ( np = from->next->to; ; np = np->next->to ) {
+ ++cnt;
+ if ( np==to )
+ break;
+ }
+ if ( cnt>10 ) {
+ lens = malloc(cnt*sizeof(bigreal));
+ cnts = malloc(cnt*sizeof(int));
+ }
+ cnt = 0; len = 0;
+ for ( np = from->next->to; ; np = np->next->to ) {
+ lens[cnt] = SplineLenApprox(np->prev);
+ len += lens[cnt];
+ ++cnt;
+ if ( np==to )
+ break;
+ }
+ if ( len!=0 ) {
+ pcnt = 0;
+ for ( i=0; i<cnt; ++i ) {
+ int pnts = rint( (10*cnt*lens[i])/len );
+ if ( pnts<2 ) pnts = 2;
+ cnts[i] = pnts;
+ pcnt += pnts;
+ }
+ } else
+ pcnt = 2*cnt;
+
+ fp = malloc((pcnt+1)*sizeof(FitPoint)); i = 0;
+ if ( len==0 ) {
+ for ( i=0; i<=pcnt; ++i ) {
+ fp[i].t = i/(pcnt);
+ fp[i].p.x = from->me.x;
+ fp[i].p.y = from->me.y;
+ }
+ } else {
+ lbase = 0;
+ for ( i=cnt=0, np = from->next->to; ; np = np->next->to, ++cnt ) {
+ slen = SplineLenApprox(np->prev);
+ for ( j=0; j<cnts[cnt]; ++j ) {
+ bigreal t = j/(bigreal) cnts[cnt];
+ fp[i].t = (lbase+ t*slen)/len;
+ fp[i].p.x = ((np->prev->splines[0].a*t+np->prev->splines[0].b)*t+np->prev->splines[0].c)*t + np->prev->splines[0].d;
+ fp[i++].p.y = ((np->prev->splines[1].a*t+np->prev->splines[1].b)*t+np->prev->splines[1].c)*t + np->prev->splines[1].d;
+ }
+ lbase += slen;
+ if ( np==to )
+ break;
+ }
+ }
+ if ( cnts!=_cnts ) free(cnts);
+ if ( lens!=_lens ) free(lens);
+
+ *tot = i;
+
+return( fp );
+}
+
+static int SplinePointCategory(SplinePoint *sp) {
+ enum pointtype pt;
+
+ pt = pt_corner;
+ if ( sp->next==NULL && sp->prev==NULL )
+ ;
+ else if ( (sp->next!=NULL && sp->next->to->me.x==sp->me.x && sp->next->to->me.y==sp->me.y) ||
+ (sp->prev!=NULL && sp->prev->from->me.x==sp->me.x && sp->prev->from->me.y==sp->me.y ))
+ ;
+ else if ( sp->next==NULL ) {
+ pt = sp->noprevcp ? pt_corner : pt_curve;
+ } else if ( sp->prev==NULL ) {
+ pt = sp->nonextcp ? pt_corner : pt_curve;
+ } else if ( sp->nonextcp && sp->noprevcp ) {
+ ;
+ } else {
+ BasePoint ndir, ncdir, ncunit, pdir, pcdir, pcunit;
+ bigreal nlen, nclen, plen, pclen;
+ bigreal cross, bounds;
+
+ ncdir.x = sp->nextcp.x - sp->me.x; ncdir.y = sp->nextcp.y - sp->me.y;
+ pcdir.x = sp->prevcp.x - sp->me.x; pcdir.y = sp->prevcp.y - sp->me.y;
+ ndir.x = ndir.y = pdir.x = pdir.y = 0;
+ if ( sp->next!=NULL ) {
+ ndir.x = sp->next->to->me.x - sp->me.x; ndir.y = sp->next->to->me.y - sp->me.y;
+ }
+ if ( sp->prev!=NULL ) {
+ pdir.x = sp->prev->from->me.x - sp->me.x; pdir.y = sp->prev->from->me.y - sp->me.y;
+ }
+ nclen = sqrt(ncdir.x*ncdir.x + ncdir.y*ncdir.y);
+ pclen = sqrt(pcdir.x*pcdir.x + pcdir.y*pcdir.y);
+ nlen = sqrt(ndir.x*ndir.x + ndir.y*ndir.y);
+ plen = sqrt(pdir.x*pdir.x + pdir.y*pdir.y);
+ ncunit = ncdir; pcunit = pcdir;
+ if ( nclen!=0 ) { ncunit.x /= nclen; ncunit.y /= nclen; }
+ if ( pclen!=0 ) { pcunit.x /= pclen; pcunit.y /= pclen; }
+ if ( nlen!=0 ) { ndir.x /= nlen; ndir.y /= nlen; }
+ if ( plen!=0 ) { pdir.x /= plen; pdir.y /= plen; }
+
+ /* find out which side has the shorter control vector. Cross that vector */
+ /* with the normal of the unit vector on the other side. If the */
+ /* result is less than 1 em-unit then we've got colinear control points */
+ /* (within the resolution of the integer grid) */
+ /* Not quite... they could point in the same direction */
+ if ( sp->pointtype==pt_curve )
+ bounds = 4.0;
+ else
+ bounds = 1.0;
+ if ( nclen!=0 && pclen!=0 &&
+ ((nclen>=pclen && (cross = pcdir.x*ncunit.y - pcdir.y*ncunit.x)<bounds && cross>-bounds ) ||
+ (pclen>nclen && (cross = ncdir.x*pcunit.y - ncdir.y*pcunit.x)<bounds && cross>-bounds )) &&
+ ncdir.x*pcdir.x + ncdir.y*pcdir.y < 0 )
+ pt = pt_curve;
+ /* Cross product of control point with unit vector normal to line in */
+ /* opposite direction should be less than an em-unit for a tangent */
+ else if ( ( nclen==0 && pclen!=0
+ && (cross = pcdir.x*ndir.y-pcdir.y*ndir.x)<bounds
+ && cross>-bounds && (pcdir.x*ndir.x+pcdir.y*ndir.y)<0 )
+ ||
+ ( pclen==0 && nclen!=0
+ && (cross = ncdir.x*pdir.y-ncdir.y*pdir.x)<bounds
+ && cross>-bounds && (ncdir.x*pdir.x+ncdir.y*pdir.y)<0 ) )
+ pt = pt_tangent;
+
+ if (pt == pt_curve &&
+ ((sp->nextcp.x==sp->me.x && sp->prevcp.x==sp->me.x && sp->nextcp.y!=sp->me.y) ||
+ (sp->nextcp.y==sp->me.y && sp->prevcp.y==sp->me.y && sp->nextcp.x!=sp->me.x)))
+ pt = pt_hvcurve;
+ }
+ return pt;
+}
+
+static enum pointtype SplinePointDowngrade(int current, int geom) {
+ enum pointtype np = current;
+
+ if ( current==pt_curve && geom!=pt_curve ) {
+ if ( geom==pt_hvcurve )
+ np = pt_curve;
+ else
+ np = pt_corner;
+ } else if ( current==pt_hvcurve && geom!=pt_hvcurve ) {
+ if ( geom==pt_curve )
+ np = pt_curve;
+ else
+ np = pt_corner;
+ } else if ( current==pt_tangent && geom!=pt_tangent ) {
+ np = pt_corner;
+ }
+
+ return np;
+}
+
+// Assumes flag combinations are already verified. Only returns false
+// when called with check_compat
+int _SplinePointCategorize(SplinePoint *sp, int flags) {
+ enum pointtype geom, dg, cur;
+
+ if ( flags & pconvert_flag_none )
+ // No points selected for conversion -- keep type as is
+ return true;
+ if ( flags & pconvert_flag_smooth && sp->pointtype == pt_corner )
+ // Convert only "smooth" points, not corners
+ return true;
+
+ geom = SplinePointCategory(sp);
+ dg = SplinePointDowngrade(sp->pointtype, geom);
+
+ if ( flags & pconvert_flag_incompat && sp->pointtype == dg )
+ // Only convert points incompatible with current type
+ return true;
+
+ if ( flags & pconvert_flag_by_geom ) {
+ if ( ! ( flags & pconvert_flag_hvcurve ) && geom == pt_hvcurve )
+ sp->pointtype = pt_curve;
+ else
+ sp->pointtype = geom;
+ } else if ( flags & pconvert_flag_downgrade ) {
+ sp->pointtype = dg;
+ } else if ( flags & pconvert_flag_force_type ) {
+ if ( sp->pointtype != dg ) {
+ cur = sp->pointtype;
+ sp->pointtype = dg;
+ /* SPChangePointType(sp,cur); */
+ }
+ } else if ( flags & pconvert_flag_check_compat ) {
+ if ( sp->pointtype != dg )
+ return false;
+ }
+ return true;
+}
+
+void SplinePointCategorize(SplinePoint *sp) {
+ _SplinePointCategorize(sp, pconvert_flag_all|pconvert_flag_by_geom);
+}
+
+static void SplinePointReCategorize(SplinePoint *sp,int oldpt) {
+ SplinePointCategorize(sp);
+ if ( sp->pointtype!=oldpt ) {
+ if ( sp->pointtype==pt_curve && oldpt==pt_hvcurve &&
+ ((sp->nextcp.x == sp->me.x && sp->nextcp.y != sp->me.y ) ||
+ (sp->nextcp.y == sp->me.y && sp->nextcp.x != sp->me.x )))
+ sp->pointtype = pt_hvcurve;
+ }
+}
+
+void SplinesRemoveBetween(SplinePoint *from, SplinePoint *to, int type) {
+ int tot;
+ FitPoint *fp;
+ SplinePoint *np, oldfrom;
+ int oldfpt = from->pointtype, oldtpt = to->pointtype;
+ Spline *sp;
+ int order2 = from->next->order2;
+
+ oldfrom = *from;
+ fp = SplinesFigureFPsBetween(from,to,&tot);
+
+ if ( type==1 )
+ ApproximateSplineFromPointsSlopes(from,to,fp,tot-1,order2,mt_levien);
+ else
+ ApproximateSplineFromPoints(from,to,fp,tot-1,order2);
+
+ /* Have to do the frees after the approximation because the approx */
+ /* uses the splines to determine slopes */
+ for ( sp = oldfrom.next; ; ) {
+ np = sp->to;
+ SplineFree(sp);
+ if ( np==to )
+ break;
+ sp = np->next;
+ // SplinePointMDFree(sc,np);
+ }
+
+ free(fp);
+
+ SplinePointReCategorize(from,oldfpt);
+ SplinePointReCategorize(to,oldtpt);
+}