Fixed spline error calculation. New effort to compute spline initial/final slopes even for degenerate splines, (degenerate splines are still broken somewhere though).

This commit is contained in:
Carl Worth 2002-09-10 08:01:00 +00:00
parent 44ca388c90
commit 5f1938f26f
17 changed files with 348 additions and 116 deletions

View file

@ -26,6 +26,7 @@ SRCS = xr.c \
xrcolor.c \
xrfiller.c \
xrgstate.c \
xrmisc.c \
xrpath.c \
xrpen.c \
xrpolygon.c \
@ -40,6 +41,7 @@ OBJS = xr.o \
xrcolor.o \
xrfiller.o \
xrgstate.o \
xrmisc.o \
xrpath.o \
xrpen.o \
xrpolygon.o \

View file

@ -95,12 +95,21 @@ XrGStateInitCopy(XrGState *gstate, XrGState *other)
XrSurfaceSetSolidColor(&gstate->src, &gstate->color, gstate->solidFormat);
err = XrPathInitCopy(&gstate->path, &other->path);
if (err) {
if (gstate->dashes) {
free (gstate->dashes);
gstate->dashes = 0;
}
}
if (err)
goto CLEANUP_DASHES;
err = XrPenInitCopy(&gstate->pen_regular, &other->pen_regular);
if (err)
goto CLEANUP_PATH;
return err;
CLEANUP_PATH:
XrPathDeinit(&gstate->path);
CLEANUP_DASHES:
free (gstate->dashes);
gstate->dashes = NULL;
return err;
}
@ -117,8 +126,10 @@ XrGStateDeinit(XrGState *gstate)
XrPenDeinit(&gstate->pen_regular);
if (gstate->dashes)
if (gstate->dashes) {
free (gstate->dashes);
gstate->dashes = NULL;
}
}
void

View file

@ -122,20 +122,23 @@ typedef struct _XrPolygon {
int closed;
} XrPolygon;
typedef struct _XrSpline {
XPointFixed a, b, c, d;
int num_pts;
int pts_size;
XPointFixed *pts;
} XrSpline;
typedef struct _XrSlopeFixed
{
XFixed dx;
XFixed dy;
} XrSlopeFixed;
typedef struct _XrSpline {
XPointFixed a, b, c, d;
XrSlopeFixed initial_slope;
XrSlopeFixed final_slope;
int num_pts;
int pts_size;
XPointFixed *pts;
} XrSpline;
typedef enum _XrPenVertexTag {
XrPenVertexTagNone,
XrPenVertexTagForward,
@ -580,5 +583,10 @@ XrTrapsTessellateRectangle (XrTraps *traps, XPointFixed q[4]);
XrError
XrTrapsTessellatePolygon (XrTraps *traps, XrPolygon *poly, int winding);
/* xrmisc.c */
void
ComputeSlope(XPointFixed *a, XPointFixed *b, XrSlopeFixed *slope);
#endif

34
src/xrmisc.c Normal file
View file

@ -0,0 +1,34 @@
/*
* $XFree86: $
*
* Copyright © 2002 Carl D. Worth
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of Carl
* D. Worth not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior
* permission. Carl D. Worth makes no representations about the
* suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* CARL D. WORTH DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL CARL D. WORTH BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "xrint.h"
void
ComputeSlope(XPointFixed *a, XPointFixed *b, XrSlopeFixed *slope)
{
slope->dx = b->x - a->x;
slope->dy = b->y - a->y;
}

View file

@ -221,7 +221,6 @@ _XrPathOpBufCreate(void)
static void
_XrPathOpBufDestroy(XrPathOpBuf *op)
{
op->num_ops = 0;
free(op);
}
@ -249,7 +248,6 @@ _XrPathArgBufCreate(void)
static void
_XrPathArgBufDestroy(XrPathArgBuf *arg)
{
arg->num_pts = 0;
free(arg);
}

View file

@ -31,9 +31,6 @@ _XrPenVerticesNeeded(double radius, double tolerance, XrTransform *matrix);
static void
_XrPenComputeSlopes(XrPen *pen);
static void
_FindSlope(XPointFixed *a, XPointFixed *b, XrSlopeFixed *slope);
static int
_SlopeClockwise(XrSlopeFixed *a, XrSlopeFixed *b);
@ -231,18 +228,11 @@ _XrPenComputeSlopes(XrPen *pen)
v = &pen->vertex[i];
next = &pen->vertex[(i + 1) % pen->num_vertices];
_FindSlope(&prev->pt, &v->pt, &v->slope_cw);
_FindSlope(&v->pt, &next->pt, &v->slope_ccw);
ComputeSlope(&prev->pt, &v->pt, &v->slope_cw);
ComputeSlope(&v->pt, &next->pt, &v->slope_ccw);
}
}
static void
_FindSlope(XPointFixed *a, XPointFixed *b, XrSlopeFixed *slope)
{
slope->dx = b->x - a->x;
slope->dy = b->y - a->y;
}
static int
_SlopeClockwise(XrSlopeFixed *a, XrSlopeFixed *b)
{
@ -283,12 +273,14 @@ _XrPenStrokeSplineHalf(XrPen *pen, XrSpline *spline, XrPenVertexTag dir, XrPolyg
start = 0;
stop = num_pts;
step = 1;
_FindSlope(&spline->c, &spline->d, &final_slope);
final_slope = spline->final_slope;
} else {
start = num_pts - 1;
stop = -1;
step = -1;
_FindSlope(&spline->b, &spline->a, &final_slope);
final_slope = spline->initial_slope;
final_slope.dx = -final_slope.dx;
final_slope.dy = -final_slope.dy;
}
i = start;
@ -302,7 +294,7 @@ _XrPenStrokeSplineHalf(XrPen *pen, XrSpline *spline, XrPenVertexTag dir, XrPolyg
if (i + step == stop)
slope = final_slope;
else
_FindSlope(&pt[i], &pt[i+step], &slope);
ComputeSlope(&pt[i], &pt[i+step], &slope);
if (_SlopeCounterClockwise(&slope, &pen->vertex[active].slope_ccw)) {
if (++active == pen->num_vertices)
active = 0;

View file

@ -56,6 +56,7 @@ XrPolygonDeinit(XrPolygon *polygon)
{
if (polygon->edges_size) {
free(polygon->edges);
polygon->edges = NULL;
polygon->edges_size = 0;
polygon->num_edges = 0;
}

View file

@ -38,7 +38,7 @@ static void
_DeCastlejau(XrSpline *spline, XrSpline *s1, XrSpline *s2);
static double
_XrSplineErrorSquared(XrSpline *spline, XPointFixed *p);
_XrSplineErrorSquared(XrSpline *spline);
static XrError
_XrSplineDecomposeInto(XrSpline *spline, double tolerance_squared, XrSpline *result);
@ -53,6 +53,30 @@ XrSplineInit(XrSpline *spline, XPointFixed *a, XPointFixed *b, XPointFixed *c,
spline->c = *c;
spline->d = *d;
if (a->x != b->x || a->y != b->y) {
ComputeSlope(&spline->a, &spline->b, &spline->initial_slope);
} else if (a->x != c->x || a->y != c->y) {
ComputeSlope(&spline->a, &spline->c, &spline->initial_slope);
} else if (a->x != d->x || a->y != d->y) {
ComputeSlope(&spline->a, &spline->d, &spline->initial_slope);
} else {
/* XXX: Completely degenerate spline (single point). I'm still
not sure what the fallout from this is. */
spline->initial_slope.dx = 0;
spline->initial_slope.dy = 0;
}
if (c->x != d->x || c->y != d->y) {
ComputeSlope(&spline->c, &spline->d, &spline->final_slope);
} else if (b->x != d->x || b->y != d->y) {
ComputeSlope(&spline->b, &spline->b, &spline->final_slope);
} else if (a->x != d->x || a->y != d->y) {
ComputeSlope(&spline->a, &spline->d, &spline->final_slope);
} else {
spline->final_slope.dx = 0;
spline->final_slope.dy = 0;
}
spline->num_pts = 0;
spline->pts_size = 0;
spline->pts = NULL;
@ -64,6 +88,7 @@ XrSplineDeinit(XrSpline *spline)
spline->num_pts = 0;
spline->pts_size = 0;
free(spline->pts);
spline->pts = NULL;
}
static XrError
@ -139,44 +164,89 @@ _DeCastlejau(XrSpline *spline, XrSpline *s1, XrSpline *s2)
}
static double
_XrSplineErrorSquared(XrSpline *spline, XPointFixed *p)
_PointDistanceSquaredToPoint(XPointFixed *a, XPointFixed *b)
{
XPointFixed mid;
double dx, dy;
_LerpHalf(&spline->a, &spline->d, &mid);
dx = XFixedToDouble(mid.x - p->x);
dy = XFixedToDouble(mid.y - p->y);
double dx = XFixedToDouble(b->x - a->x);
double dy = XFixedToDouble(b->y - a->y);
return dx*dx + dy*dy;
}
static double
_PointDistanceSquaredToSegment(XPointFixed *p, XPointFixed *p1, XPointFixed *p2)
{
double u;
double dx, dy;
double pdx, pdy;
XPointFixed px;
/* intersection point (px):
px = p1 + u(p2 - p1)
(p - px) . (p2 - p1) = 0
Thus:
u = ((p - p1) . (p2 - p1)) / (||(p2 - p1)|| ^ 2);
*/
dx = XFixedToDouble(p2->x - p1->x);
dy = XFixedToDouble(p2->y - p1->y);
if (dx == 0 && dy == 0)
return _PointDistanceSquaredToPoint(p, p1);
pdx = XFixedToDouble(p->x - p1->x);
pdy = XFixedToDouble(p->y - p1->y);
u = (pdx * dx + pdy * dy) / (dx*dx + dy*dy);
if (u <= 0)
return _PointDistanceSquaredToPoint(p, p1);
else if (u >= 1)
return _PointDistanceSquaredToPoint(p, p2);
px.x = p1->x + u * (p2->x - p1->x);
px.y = p1->y + u * (p2->y - p1->y);
return _PointDistanceSquaredToPoint(p, &px);
}
/* Return an upper bound on the error (squared) that could result from approximating
a spline as a line segment connecting the two endpoints */
static double
_XrSplineErrorSquared(XrSpline *spline)
{
double berr, cerr;
berr = _PointDistanceSquaredToSegment(&spline->b, &spline->a, &spline->d);
cerr = _PointDistanceSquaredToSegment(&spline->c, &spline->a, &spline->d);
if (berr > cerr)
return berr;
else
return cerr;
}
static XrError
_XrSplineDecomposeInto(XrSpline *spline, double tolerance_squared, XrSpline *result)
{
XrError err;
XrSpline s1, s2;
if (_XrSplineErrorSquared(spline) < tolerance_squared) {
return _XrSplineAddPoint(result, &spline->a);
}
_DeCastlejau(spline, &s1, &s2);
if (_XrSplineErrorSquared(spline, &s1.d) < tolerance_squared) {
err = _XrSplineAddPoint(result, &s1.a);
if (err)
return err;
err = _XrSplineAddPoint(result, &s1.d);
if (err)
return err;
} else {
err = _XrSplineDecomposeInto(&s1, tolerance_squared, result);
if (err)
return err;
err = _XrSplineDecomposeInto(&s2, tolerance_squared, result);
if (err)
return err;
}
err = _XrSplineDecomposeInto(&s1, tolerance_squared, result);
if (err)
return err;
err = _XrSplineDecomposeInto(&s2, tolerance_squared, result);
if (err)
return err;
return XrErrorSuccess;
}

View file

@ -74,6 +74,7 @@ XrTrapsDeinit(XrTraps *traps)
{
if (traps->xtraps_size) {
free(traps->xtraps);
traps->xtraps = NULL;
traps->xtraps_size = 0;
traps->num_xtraps = 0;
}

View file

@ -95,12 +95,21 @@ XrGStateInitCopy(XrGState *gstate, XrGState *other)
XrSurfaceSetSolidColor(&gstate->src, &gstate->color, gstate->solidFormat);
err = XrPathInitCopy(&gstate->path, &other->path);
if (err) {
if (gstate->dashes) {
free (gstate->dashes);
gstate->dashes = 0;
}
}
if (err)
goto CLEANUP_DASHES;
err = XrPenInitCopy(&gstate->pen_regular, &other->pen_regular);
if (err)
goto CLEANUP_PATH;
return err;
CLEANUP_PATH:
XrPathDeinit(&gstate->path);
CLEANUP_DASHES:
free (gstate->dashes);
gstate->dashes = NULL;
return err;
}
@ -117,8 +126,10 @@ XrGStateDeinit(XrGState *gstate)
XrPenDeinit(&gstate->pen_regular);
if (gstate->dashes)
if (gstate->dashes) {
free (gstate->dashes);
gstate->dashes = NULL;
}
}
void

24
xrint.h
View file

@ -122,20 +122,23 @@ typedef struct _XrPolygon {
int closed;
} XrPolygon;
typedef struct _XrSpline {
XPointFixed a, b, c, d;
int num_pts;
int pts_size;
XPointFixed *pts;
} XrSpline;
typedef struct _XrSlopeFixed
{
XFixed dx;
XFixed dy;
} XrSlopeFixed;
typedef struct _XrSpline {
XPointFixed a, b, c, d;
XrSlopeFixed initial_slope;
XrSlopeFixed final_slope;
int num_pts;
int pts_size;
XPointFixed *pts;
} XrSpline;
typedef enum _XrPenVertexTag {
XrPenVertexTagNone,
XrPenVertexTagForward,
@ -580,5 +583,10 @@ XrTrapsTessellateRectangle (XrTraps *traps, XPointFixed q[4]);
XrError
XrTrapsTessellatePolygon (XrTraps *traps, XrPolygon *poly, int winding);
/* xrmisc.c */
void
ComputeSlope(XPointFixed *a, XPointFixed *b, XrSlopeFixed *slope);
#endif

34
xrmisc.c Normal file
View file

@ -0,0 +1,34 @@
/*
* $XFree86: $
*
* Copyright © 2002 Carl D. Worth
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of Carl
* D. Worth not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior
* permission. Carl D. Worth makes no representations about the
* suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* CARL D. WORTH DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL CARL D. WORTH BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "xrint.h"
void
ComputeSlope(XPointFixed *a, XPointFixed *b, XrSlopeFixed *slope)
{
slope->dx = b->x - a->x;
slope->dy = b->y - a->y;
}

View file

@ -221,7 +221,6 @@ _XrPathOpBufCreate(void)
static void
_XrPathOpBufDestroy(XrPathOpBuf *op)
{
op->num_ops = 0;
free(op);
}
@ -249,7 +248,6 @@ _XrPathArgBufCreate(void)
static void
_XrPathArgBufDestroy(XrPathArgBuf *arg)
{
arg->num_pts = 0;
free(arg);
}

22
xrpen.c
View file

@ -31,9 +31,6 @@ _XrPenVerticesNeeded(double radius, double tolerance, XrTransform *matrix);
static void
_XrPenComputeSlopes(XrPen *pen);
static void
_FindSlope(XPointFixed *a, XPointFixed *b, XrSlopeFixed *slope);
static int
_SlopeClockwise(XrSlopeFixed *a, XrSlopeFixed *b);
@ -231,18 +228,11 @@ _XrPenComputeSlopes(XrPen *pen)
v = &pen->vertex[i];
next = &pen->vertex[(i + 1) % pen->num_vertices];
_FindSlope(&prev->pt, &v->pt, &v->slope_cw);
_FindSlope(&v->pt, &next->pt, &v->slope_ccw);
ComputeSlope(&prev->pt, &v->pt, &v->slope_cw);
ComputeSlope(&v->pt, &next->pt, &v->slope_ccw);
}
}
static void
_FindSlope(XPointFixed *a, XPointFixed *b, XrSlopeFixed *slope)
{
slope->dx = b->x - a->x;
slope->dy = b->y - a->y;
}
static int
_SlopeClockwise(XrSlopeFixed *a, XrSlopeFixed *b)
{
@ -283,12 +273,14 @@ _XrPenStrokeSplineHalf(XrPen *pen, XrSpline *spline, XrPenVertexTag dir, XrPolyg
start = 0;
stop = num_pts;
step = 1;
_FindSlope(&spline->c, &spline->d, &final_slope);
final_slope = spline->final_slope;
} else {
start = num_pts - 1;
stop = -1;
step = -1;
_FindSlope(&spline->b, &spline->a, &final_slope);
final_slope = spline->initial_slope;
final_slope.dx = -final_slope.dx;
final_slope.dy = -final_slope.dy;
}
i = start;
@ -302,7 +294,7 @@ _XrPenStrokeSplineHalf(XrPen *pen, XrSpline *spline, XrPenVertexTag dir, XrPolyg
if (i + step == stop)
slope = final_slope;
else
_FindSlope(&pt[i], &pt[i+step], &slope);
ComputeSlope(&pt[i], &pt[i+step], &slope);
if (_SlopeCounterClockwise(&slope, &pen->vertex[active].slope_ccw)) {
if (++active == pen->num_vertices)
active = 0;

View file

@ -56,6 +56,7 @@ XrPolygonDeinit(XrPolygon *polygon)
{
if (polygon->edges_size) {
free(polygon->edges);
polygon->edges = NULL;
polygon->edges_size = 0;
polygon->num_edges = 0;
}

View file

@ -38,7 +38,7 @@ static void
_DeCastlejau(XrSpline *spline, XrSpline *s1, XrSpline *s2);
static double
_XrSplineErrorSquared(XrSpline *spline, XPointFixed *p);
_XrSplineErrorSquared(XrSpline *spline);
static XrError
_XrSplineDecomposeInto(XrSpline *spline, double tolerance_squared, XrSpline *result);
@ -53,6 +53,30 @@ XrSplineInit(XrSpline *spline, XPointFixed *a, XPointFixed *b, XPointFixed *c,
spline->c = *c;
spline->d = *d;
if (a->x != b->x || a->y != b->y) {
ComputeSlope(&spline->a, &spline->b, &spline->initial_slope);
} else if (a->x != c->x || a->y != c->y) {
ComputeSlope(&spline->a, &spline->c, &spline->initial_slope);
} else if (a->x != d->x || a->y != d->y) {
ComputeSlope(&spline->a, &spline->d, &spline->initial_slope);
} else {
/* XXX: Completely degenerate spline (single point). I'm still
not sure what the fallout from this is. */
spline->initial_slope.dx = 0;
spline->initial_slope.dy = 0;
}
if (c->x != d->x || c->y != d->y) {
ComputeSlope(&spline->c, &spline->d, &spline->final_slope);
} else if (b->x != d->x || b->y != d->y) {
ComputeSlope(&spline->b, &spline->b, &spline->final_slope);
} else if (a->x != d->x || a->y != d->y) {
ComputeSlope(&spline->a, &spline->d, &spline->final_slope);
} else {
spline->final_slope.dx = 0;
spline->final_slope.dy = 0;
}
spline->num_pts = 0;
spline->pts_size = 0;
spline->pts = NULL;
@ -64,6 +88,7 @@ XrSplineDeinit(XrSpline *spline)
spline->num_pts = 0;
spline->pts_size = 0;
free(spline->pts);
spline->pts = NULL;
}
static XrError
@ -139,44 +164,89 @@ _DeCastlejau(XrSpline *spline, XrSpline *s1, XrSpline *s2)
}
static double
_XrSplineErrorSquared(XrSpline *spline, XPointFixed *p)
_PointDistanceSquaredToPoint(XPointFixed *a, XPointFixed *b)
{
XPointFixed mid;
double dx, dy;
_LerpHalf(&spline->a, &spline->d, &mid);
dx = XFixedToDouble(mid.x - p->x);
dy = XFixedToDouble(mid.y - p->y);
double dx = XFixedToDouble(b->x - a->x);
double dy = XFixedToDouble(b->y - a->y);
return dx*dx + dy*dy;
}
static double
_PointDistanceSquaredToSegment(XPointFixed *p, XPointFixed *p1, XPointFixed *p2)
{
double u;
double dx, dy;
double pdx, pdy;
XPointFixed px;
/* intersection point (px):
px = p1 + u(p2 - p1)
(p - px) . (p2 - p1) = 0
Thus:
u = ((p - p1) . (p2 - p1)) / (||(p2 - p1)|| ^ 2);
*/
dx = XFixedToDouble(p2->x - p1->x);
dy = XFixedToDouble(p2->y - p1->y);
if (dx == 0 && dy == 0)
return _PointDistanceSquaredToPoint(p, p1);
pdx = XFixedToDouble(p->x - p1->x);
pdy = XFixedToDouble(p->y - p1->y);
u = (pdx * dx + pdy * dy) / (dx*dx + dy*dy);
if (u <= 0)
return _PointDistanceSquaredToPoint(p, p1);
else if (u >= 1)
return _PointDistanceSquaredToPoint(p, p2);
px.x = p1->x + u * (p2->x - p1->x);
px.y = p1->y + u * (p2->y - p1->y);
return _PointDistanceSquaredToPoint(p, &px);
}
/* Return an upper bound on the error (squared) that could result from approximating
a spline as a line segment connecting the two endpoints */
static double
_XrSplineErrorSquared(XrSpline *spline)
{
double berr, cerr;
berr = _PointDistanceSquaredToSegment(&spline->b, &spline->a, &spline->d);
cerr = _PointDistanceSquaredToSegment(&spline->c, &spline->a, &spline->d);
if (berr > cerr)
return berr;
else
return cerr;
}
static XrError
_XrSplineDecomposeInto(XrSpline *spline, double tolerance_squared, XrSpline *result)
{
XrError err;
XrSpline s1, s2;
if (_XrSplineErrorSquared(spline) < tolerance_squared) {
return _XrSplineAddPoint(result, &spline->a);
}
_DeCastlejau(spline, &s1, &s2);
if (_XrSplineErrorSquared(spline, &s1.d) < tolerance_squared) {
err = _XrSplineAddPoint(result, &s1.a);
if (err)
return err;
err = _XrSplineAddPoint(result, &s1.d);
if (err)
return err;
} else {
err = _XrSplineDecomposeInto(&s1, tolerance_squared, result);
if (err)
return err;
err = _XrSplineDecomposeInto(&s2, tolerance_squared, result);
if (err)
return err;
}
err = _XrSplineDecomposeInto(&s1, tolerance_squared, result);
if (err)
return err;
err = _XrSplineDecomposeInto(&s2, tolerance_squared, result);
if (err)
return err;
return XrErrorSuccess;
}

View file

@ -74,6 +74,7 @@ XrTrapsDeinit(XrTraps *traps)
{
if (traps->xtraps_size) {
free(traps->xtraps);
traps->xtraps = NULL;
traps->xtraps_size = 0;
traps->num_xtraps = 0;
}