2002-08-15 17:30:03 +00:00
|
|
|
|
/*
|
|
|
|
|
|
* $XFree86: $
|
|
|
|
|
|
*
|
|
|
|
|
|
* Copyright <EFBFBD> 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"
|
|
|
|
|
|
|
2002-09-03 08:42:25 +00:00
|
|
|
|
static int
|
2002-09-05 10:06:44 +00:00
|
|
|
|
_XrPenVerticesNeeded(double radius, double tolerance, XrTransform *matrix);
|
2002-09-03 08:42:25 +00:00
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
_XrPenComputeSlopes(XrPen *pen);
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
|
_SlopeClockwise(XrSlopeFixed *a, XrSlopeFixed *b);
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
|
_SlopeCounterClockwise(XrSlopeFixed *a, XrSlopeFixed *b);
|
|
|
|
|
|
|
|
|
|
|
|
static XrError
|
2002-09-13 12:55:37 +00:00
|
|
|
|
_XrPenStrokeSplineHalf(XrPen *pen, XrSpline *spline, XrPenVertexFlag dir, XrPolygon *polygon);
|
2002-09-03 08:42:25 +00:00
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
|
_XrPenVertexCompareByTheta(const void *a, const void *b);
|
|
|
|
|
|
|
2002-08-15 17:30:03 +00:00
|
|
|
|
XrError
|
2002-09-03 08:42:25 +00:00
|
|
|
|
XrPenInitEmpty(XrPen *pen)
|
2002-08-15 17:30:03 +00:00
|
|
|
|
{
|
2002-09-03 08:42:25 +00:00
|
|
|
|
pen->radius = 0;
|
|
|
|
|
|
pen->tolerance = 0;
|
|
|
|
|
|
pen->vertex = NULL;
|
|
|
|
|
|
pen->num_vertices = 0;
|
|
|
|
|
|
|
2002-08-15 17:33:00 +00:00
|
|
|
|
return XrErrorSuccess;
|
2002-08-15 17:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
XrError
|
2002-09-04 07:28:56 +00:00
|
|
|
|
XrPenInit(XrPen *pen, double radius, XrGState *gstate)
|
2002-08-15 17:30:03 +00:00
|
|
|
|
{
|
2002-09-03 08:42:25 +00:00
|
|
|
|
int i;
|
|
|
|
|
|
XrPenVertex *v;
|
2002-09-04 07:28:56 +00:00
|
|
|
|
XPointDouble pt;
|
2002-09-03 08:42:25 +00:00
|
|
|
|
|
|
|
|
|
|
if (pen->num_vertices) {
|
2002-09-04 07:28:56 +00:00
|
|
|
|
/* XXX: It would be nice to notice that the pen is already properly constructed.
|
|
|
|
|
|
However, this test would also have to account for possible changes in the transformation
|
|
|
|
|
|
matrix.
|
2002-09-03 08:42:25 +00:00
|
|
|
|
if (pen->radius == radius && pen->tolerance == tolerance)
|
|
|
|
|
|
return XrErrorSuccess;
|
2002-09-04 07:28:56 +00:00
|
|
|
|
*/
|
2002-09-03 08:42:25 +00:00
|
|
|
|
XrPenDeinit(pen);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pen->radius = radius;
|
2002-09-04 07:28:56 +00:00
|
|
|
|
pen->tolerance = gstate->tolerance;
|
2002-09-03 08:42:25 +00:00
|
|
|
|
|
2002-09-05 10:06:44 +00:00
|
|
|
|
pen->num_vertices = _XrPenVerticesNeeded(radius, gstate->tolerance, &gstate->ctm);
|
2002-09-13 12:55:37 +00:00
|
|
|
|
/* number of vertices must be even */
|
|
|
|
|
|
if (pen->num_vertices % 2)
|
|
|
|
|
|
pen->num_vertices++;
|
2002-09-03 08:42:25 +00:00
|
|
|
|
|
|
|
|
|
|
pen->vertex = malloc(pen->num_vertices * sizeof(XrPenVertex));
|
|
|
|
|
|
if (pen->vertex == NULL) {
|
|
|
|
|
|
return XrErrorNoMemory;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (i=0; i < pen->num_vertices; i++) {
|
|
|
|
|
|
v = &pen->vertex[i];
|
|
|
|
|
|
v->theta = 2 * M_PI * i / (double) pen->num_vertices;
|
2002-09-04 07:28:56 +00:00
|
|
|
|
pt.x = radius * cos(v->theta);
|
|
|
|
|
|
pt.y = radius * sin(v->theta);
|
|
|
|
|
|
XrTransformPointWithoutTranslate(&gstate->ctm, &pt);
|
|
|
|
|
|
v->pt.x = XDoubleToFixed(pt.x);
|
|
|
|
|
|
v->pt.y = XDoubleToFixed(pt.y);
|
2002-09-13 12:55:37 +00:00
|
|
|
|
v->flag = XrPenVertexFlagNone;
|
2002-09-03 08:42:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_XrPenComputeSlopes(pen);
|
|
|
|
|
|
|
2002-08-15 17:33:00 +00:00
|
|
|
|
return XrErrorSuccess;
|
2002-08-15 17:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
XrPenDeinit(XrPen *pen)
|
|
|
|
|
|
{
|
2002-09-03 08:42:25 +00:00
|
|
|
|
free(pen->vertex);
|
|
|
|
|
|
XrPenInitEmpty(pen);
|
2002-08-15 17:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2002-09-03 08:42:25 +00:00
|
|
|
|
XrError
|
|
|
|
|
|
XrPenInitCopy(XrPen *pen, XrPen *other)
|
2002-08-15 17:30:03 +00:00
|
|
|
|
{
|
2002-09-03 08:42:25 +00:00
|
|
|
|
*pen = *other;
|
|
|
|
|
|
|
2002-09-13 13:27:51 +00:00
|
|
|
|
if (pen->num_vertices) {
|
|
|
|
|
|
pen->vertex = malloc(pen->num_vertices * sizeof(XrPenVertex));
|
|
|
|
|
|
if (pen->vertex == NULL) {
|
|
|
|
|
|
return XrErrorNoMemory;
|
|
|
|
|
|
}
|
|
|
|
|
|
memcpy(pen->vertex, other->vertex, pen->num_vertices * sizeof(XrPenVertex));
|
2002-09-03 08:42:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return XrErrorSuccess;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
|
_XrPenVertexCompareByTheta(const void *a, const void *b)
|
|
|
|
|
|
{
|
2002-09-04 16:11:35 +00:00
|
|
|
|
double diff;
|
2002-09-03 08:42:25 +00:00
|
|
|
|
const XrPenVertex *va = a;
|
|
|
|
|
|
const XrPenVertex *vb = b;
|
|
|
|
|
|
|
2002-09-04 16:11:35 +00:00
|
|
|
|
diff = va->theta - vb->theta;
|
|
|
|
|
|
if (diff < 0)
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
else if (diff > 0)
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
else
|
|
|
|
|
|
return 0;
|
2002-08-15 17:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
XrError
|
2002-09-13 12:55:37 +00:00
|
|
|
|
XrPenAddPoints(XrPen *pen, XrPenFlaggedPoint *pt, int num_pts)
|
2002-08-15 17:30:03 +00:00
|
|
|
|
{
|
2002-09-04 07:28:56 +00:00
|
|
|
|
int i, j;
|
2002-09-13 12:55:37 +00:00
|
|
|
|
XrPenVertex *v, *v_next, *new_vertex;
|
2002-09-03 08:42:25 +00:00
|
|
|
|
|
|
|
|
|
|
pen->num_vertices += num_pts;
|
|
|
|
|
|
new_vertex = realloc(pen->vertex, pen->num_vertices * sizeof(XrPenVertex));
|
|
|
|
|
|
if (new_vertex == NULL) {
|
|
|
|
|
|
pen->num_vertices -= num_pts;
|
|
|
|
|
|
return XrErrorNoMemory;
|
|
|
|
|
|
}
|
|
|
|
|
|
pen->vertex = new_vertex;
|
|
|
|
|
|
|
2002-09-13 12:55:37 +00:00
|
|
|
|
/* initialize new vertices */
|
2002-09-03 08:42:25 +00:00
|
|
|
|
for (i=0; i < num_pts; i++) {
|
2002-09-13 12:55:37 +00:00
|
|
|
|
v = &pen->vertex[pen->num_vertices-(i+1)];
|
|
|
|
|
|
v->pt = pt[i].pt;
|
|
|
|
|
|
v->flag = pt[i].flag;
|
|
|
|
|
|
v->theta = atan2(v->pt.y, v->pt.x);
|
|
|
|
|
|
if (v->theta < 0)
|
|
|
|
|
|
v->theta += 2 * M_PI;
|
2002-09-03 08:42:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2002-09-13 12:55:37 +00:00
|
|
|
|
qsort(pen->vertex, pen->num_vertices, sizeof(XrPenVertex), _XrPenVertexCompareByTheta);
|
2002-09-03 08:42:25 +00:00
|
|
|
|
|
2002-09-13 12:55:37 +00:00
|
|
|
|
/* eliminate any duplicate vertices */
|
|
|
|
|
|
for (i=0; i < pen->num_vertices - 1; i++ ) {
|
|
|
|
|
|
v = &pen->vertex[i];
|
|
|
|
|
|
v_next = &pen->vertex[i+1];
|
|
|
|
|
|
if (v->pt.x == v_next->pt.x && v->pt.y == v_next->pt.y) {
|
|
|
|
|
|
v->flag |= v_next->flag;
|
|
|
|
|
|
for (j=i+1; j < pen->num_vertices - 1; j++)
|
2002-09-04 07:28:56 +00:00
|
|
|
|
pen->vertex[j] = pen->vertex[j+1];
|
|
|
|
|
|
pen->num_vertices--;
|
|
|
|
|
|
}
|
2002-09-03 08:42:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_XrPenComputeSlopes(pen);
|
|
|
|
|
|
|
2002-08-15 17:33:00 +00:00
|
|
|
|
return XrErrorSuccess;
|
2002-08-15 17:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2002-09-03 08:42:25 +00:00
|
|
|
|
static int
|
2002-09-05 10:06:44 +00:00
|
|
|
|
_XrPenVerticesNeeded(double radius, double tolerance, XrTransform *matrix)
|
2002-09-03 08:42:25 +00:00
|
|
|
|
{
|
2002-09-05 10:06:44 +00:00
|
|
|
|
double e1, e2, emax, theta;
|
|
|
|
|
|
|
|
|
|
|
|
XrTransformEigenValues(matrix, &e1, &e2);
|
|
|
|
|
|
|
|
|
|
|
|
if (fabs(e1) > fabs(e2))
|
|
|
|
|
|
emax = fabs(e1);
|
|
|
|
|
|
else
|
|
|
|
|
|
emax = fabs(e2);
|
|
|
|
|
|
|
2002-09-13 13:27:51 +00:00
|
|
|
|
if (tolerance > emax*radius) {
|
|
|
|
|
|
return 4;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2002-09-05 10:06:44 +00:00
|
|
|
|
theta = acos(1 - tolerance/(emax * radius));
|
|
|
|
|
|
return ceil(M_PI / theta);
|
2002-09-03 08:42:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
_XrPenComputeSlopes(XrPen *pen)
|
|
|
|
|
|
{
|
|
|
|
|
|
int i, i_prev;
|
|
|
|
|
|
XrPenVertex *prev, *v, *next;
|
|
|
|
|
|
|
|
|
|
|
|
for (i=0, i_prev = pen->num_vertices - 1;
|
|
|
|
|
|
i < pen->num_vertices;
|
|
|
|
|
|
i_prev = i++) {
|
|
|
|
|
|
prev = &pen->vertex[i_prev];
|
|
|
|
|
|
v = &pen->vertex[i];
|
|
|
|
|
|
next = &pen->vertex[(i + 1) % pen->num_vertices];
|
|
|
|
|
|
|
2002-09-10 08:01:00 +00:00
|
|
|
|
ComputeSlope(&prev->pt, &v->pt, &v->slope_cw);
|
|
|
|
|
|
ComputeSlope(&v->pt, &next->pt, &v->slope_ccw);
|
2002-09-03 08:42:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
|
_SlopeClockwise(XrSlopeFixed *a, XrSlopeFixed *b)
|
|
|
|
|
|
{
|
|
|
|
|
|
double a_dx = XFixedToDouble(a->dx);
|
|
|
|
|
|
double a_dy = XFixedToDouble(a->dy);
|
|
|
|
|
|
double b_dx = XFixedToDouble(b->dx);
|
|
|
|
|
|
double b_dy = XFixedToDouble(b->dy);
|
|
|
|
|
|
|
|
|
|
|
|
return b_dy * a_dx > a_dy * b_dx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
|
_SlopeCounterClockwise(XrSlopeFixed *a, XrSlopeFixed *b)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ! _SlopeClockwise(a, b);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static XrError
|
2002-09-13 12:55:37 +00:00
|
|
|
|
_XrPenStrokeSplineHalf(XrPen *pen, XrSpline *spline, XrPenVertexFlag dir, XrPolygon *polygon)
|
2002-09-03 08:42:25 +00:00
|
|
|
|
{
|
|
|
|
|
|
int i;
|
|
|
|
|
|
XrError err;
|
|
|
|
|
|
int start, stop, step;
|
|
|
|
|
|
int active = 0;
|
|
|
|
|
|
XPointFixed hull_pt;
|
2002-09-05 13:12:23 +00:00
|
|
|
|
XrSlopeFixed slope, final_slope;
|
|
|
|
|
|
XPointFixed *pt = spline->pts;
|
|
|
|
|
|
int num_pts = spline->num_pts;
|
2002-09-03 08:42:25 +00:00
|
|
|
|
|
|
|
|
|
|
for (i=0; i < pen->num_vertices; i++) {
|
2002-09-13 12:55:37 +00:00
|
|
|
|
if (pen->vertex[i].flag & dir) {
|
|
|
|
|
|
active = i;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2002-09-03 08:42:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2002-09-13 12:55:37 +00:00
|
|
|
|
if (dir == XrPenVertexFlagForward) {
|
2002-09-03 08:42:25 +00:00
|
|
|
|
start = 0;
|
2002-09-05 13:12:23 +00:00
|
|
|
|
stop = num_pts;
|
2002-09-03 08:42:25 +00:00
|
|
|
|
step = 1;
|
2002-09-10 08:01:00 +00:00
|
|
|
|
final_slope = spline->final_slope;
|
2002-09-03 08:42:25 +00:00
|
|
|
|
} else {
|
|
|
|
|
|
start = num_pts - 1;
|
2002-09-05 13:12:23 +00:00
|
|
|
|
stop = -1;
|
2002-09-03 08:42:25 +00:00
|
|
|
|
step = -1;
|
2002-09-10 08:01:00 +00:00
|
|
|
|
final_slope = spline->initial_slope;
|
|
|
|
|
|
final_slope.dx = -final_slope.dx;
|
|
|
|
|
|
final_slope.dy = -final_slope.dy;
|
2002-09-03 08:42:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
i = start;
|
|
|
|
|
|
while (i != stop) {
|
|
|
|
|
|
hull_pt.x = pt[i].x + pen->vertex[active].pt.x;
|
|
|
|
|
|
hull_pt.y = pt[i].y + pen->vertex[active].pt.y;
|
|
|
|
|
|
err = XrPolygonAddPoint(polygon, &hull_pt);
|
|
|
|
|
|
if (err)
|
|
|
|
|
|
return err;
|
|
|
|
|
|
|
2002-09-05 13:12:23 +00:00
|
|
|
|
if (i + step == stop)
|
|
|
|
|
|
slope = final_slope;
|
|
|
|
|
|
else
|
2002-09-10 08:01:00 +00:00
|
|
|
|
ComputeSlope(&pt[i], &pt[i+step], &slope);
|
2002-09-05 13:12:23 +00:00
|
|
|
|
if (_SlopeCounterClockwise(&slope, &pen->vertex[active].slope_ccw)) {
|
|
|
|
|
|
if (++active == pen->num_vertices)
|
2002-09-03 08:42:25 +00:00
|
|
|
|
active = 0;
|
2002-09-05 13:12:23 +00:00
|
|
|
|
} else if (_SlopeClockwise(&slope, &pen->vertex[active].slope_cw)) {
|
|
|
|
|
|
if (--active == -1)
|
2002-09-03 08:42:25 +00:00
|
|
|
|
active = pen->num_vertices - 1;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
i += step;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2002-09-05 13:12:23 +00:00
|
|
|
|
return XrErrorSuccess;
|
2002-09-03 08:42:25 +00:00
|
|
|
|
}
|
2002-09-13 12:55:37 +00:00
|
|
|
|
|
2002-09-03 08:42:25 +00:00
|
|
|
|
|
2002-09-13 12:55:37 +00:00
|
|
|
|
/* Compute outline of a given spline using the pen.
|
|
|
|
|
|
The trapezoids needed to fill that outline will be added to traps
|
|
|
|
|
|
*/
|
2002-09-03 08:42:25 +00:00
|
|
|
|
XrError
|
2002-09-13 12:55:37 +00:00
|
|
|
|
XrPenStrokeSpline(XrPen *pen, XrSpline *spline, double tolerance, XrTraps *traps)
|
2002-09-03 08:42:25 +00:00
|
|
|
|
{
|
|
|
|
|
|
XrError err;
|
2002-09-13 12:55:37 +00:00
|
|
|
|
XrPolygon polygon;
|
2002-09-03 08:42:25 +00:00
|
|
|
|
|
2002-09-13 12:55:37 +00:00
|
|
|
|
XrPolygonInit(&polygon);
|
|
|
|
|
|
|
2002-09-05 13:12:23 +00:00
|
|
|
|
err = XrSplineDecompose(spline, tolerance);
|
|
|
|
|
|
if (err)
|
|
|
|
|
|
return err;
|
2002-09-13 12:55:37 +00:00
|
|
|
|
|
|
|
|
|
|
err = _XrPenStrokeSplineHalf(pen, spline, XrPenVertexFlagForward, &polygon);
|
2002-09-03 08:42:25 +00:00
|
|
|
|
if (err)
|
2002-09-13 12:55:37 +00:00
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
|
|
err = _XrPenStrokeSplineHalf(pen, spline, XrPenVertexFlagReverse, &polygon);
|
2002-09-03 08:42:25 +00:00
|
|
|
|
if (err)
|
2002-09-13 12:55:37 +00:00
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
|
|
XrPolygonClose(&polygon);
|
|
|
|
|
|
XrTrapsTessellatePolygon(traps, &polygon, 1);
|
|
|
|
|
|
XrPolygonDeinit(&polygon);
|
2002-09-03 08:42:25 +00:00
|
|
|
|
|
2002-09-13 12:55:37 +00:00
|
|
|
|
return XrErrorSuccess;
|
2002-09-03 08:42:25 +00:00
|
|
|
|
}
|
2002-09-13 12:55:37 +00:00
|
|
|
|
|