diff --git a/src/xrint.h b/src/xrint.h index 01756afab..f41058f60 100644 --- a/src/xrint.h +++ b/src/xrint.h @@ -564,6 +564,9 @@ XrTransformPointWithoutTranslate(XrTransform *transform, XPointDouble *pt); void XrTransformPoint(XrTransform *transform, XPointDouble *pt); +void +XrTransformEigenValues(XrTransform *transform, double *lambda1, double *lambda2); + /* xrtraps.c */ void XrTrapsInit(XrTraps *traps); diff --git a/src/xrpen.c b/src/xrpen.c index 266eec1a8..95f0f0f98 100644 --- a/src/xrpen.c +++ b/src/xrpen.c @@ -26,7 +26,7 @@ #include "xrint.h" static int -_XrPenVerticesNeeded(double radius, double tolerance); +_XrPenVerticesNeeded(double radius, double tolerance, XrTransform *matrix); static void _XrPenComputeSlopes(XrPen *pen); @@ -77,7 +77,7 @@ XrPenInit(XrPen *pen, double radius, XrGState *gstate) pen->radius = radius; pen->tolerance = gstate->tolerance; - pen->num_vertices = _XrPenVerticesNeeded(radius, gstate->tolerance); + pen->num_vertices = _XrPenVerticesNeeded(radius, gstate->tolerance, &gstate->ctm); pen->vertex = malloc(pen->num_vertices * sizeof(XrPenVertex)); if (pen->vertex == NULL) { @@ -199,17 +199,23 @@ XrPenAddPoints(XrPen *pen, XrPenTaggedPoint *pt, int num_pts) } static int -_XrPenVerticesNeeded(double radius, double tolerance) +_XrPenVerticesNeeded(double radius, double tolerance, XrTransform *matrix) { - /* XXX: BUG: This calculation needs to be corrected to account for - the fact that the radius is specified in user space, while the - tolerance is specified in device space. */ + double e1, e2, emax, theta; + if (tolerance > radius) { return 4; - } else { - double theta = acos(1 - tolerance/radius); - return ceil(M_PI / theta); - } + } + + XrTransformEigenValues(matrix, &e1, &e2); + + if (fabs(e1) > fabs(e2)) + emax = fabs(e1); + else + emax = fabs(e2); + + theta = acos(1 - tolerance/(emax * radius)); + return ceil(M_PI / theta); } static void diff --git a/src/xrtransform.c b/src/xrtransform.c index 97cb083b8..7d41e9b3a 100644 --- a/src/xrtransform.c +++ b/src/xrtransform.c @@ -151,3 +151,35 @@ XrTransformPoint(XrTransform *transform, XPointDouble *pt) pt->x += transform->m[2][0]; pt->y += transform->m[2][1]; } + +void +XrTransformEigenValues(XrTransform *transform, double *lambda1, double *lambda2) +{ + /* The eigenvalues of an NxN matrix M are found by solving the polynomial: + + det(M - lI) = 0 + + which for our 2x2 matrix: + + M = a b + c d + + gives: + + l^2 - (a+d)l + (ad - bc) = 0 + + l = (a+d +/- sqrt(a^2 + 2ad + d^2 - 4(ad-bc))) / 2; + */ + + double a, b, c, d, rad; + + a = transform->m[0][0]; + b = transform->m[0][1]; + c = transform->m[1][0]; + d = transform->m[1][1]; + + rad = sqrt(a*a + 2*a*d + d*d - 4*(a*d - b*c)); + *lambda1 = (a + d + rad) / 2.0; + *lambda2 = (a + d - rad) / 2.0; +} + diff --git a/xrint.h b/xrint.h index 01756afab..f41058f60 100644 --- a/xrint.h +++ b/xrint.h @@ -564,6 +564,9 @@ XrTransformPointWithoutTranslate(XrTransform *transform, XPointDouble *pt); void XrTransformPoint(XrTransform *transform, XPointDouble *pt); +void +XrTransformEigenValues(XrTransform *transform, double *lambda1, double *lambda2); + /* xrtraps.c */ void XrTrapsInit(XrTraps *traps); diff --git a/xrpen.c b/xrpen.c index 266eec1a8..95f0f0f98 100644 --- a/xrpen.c +++ b/xrpen.c @@ -26,7 +26,7 @@ #include "xrint.h" static int -_XrPenVerticesNeeded(double radius, double tolerance); +_XrPenVerticesNeeded(double radius, double tolerance, XrTransform *matrix); static void _XrPenComputeSlopes(XrPen *pen); @@ -77,7 +77,7 @@ XrPenInit(XrPen *pen, double radius, XrGState *gstate) pen->radius = radius; pen->tolerance = gstate->tolerance; - pen->num_vertices = _XrPenVerticesNeeded(radius, gstate->tolerance); + pen->num_vertices = _XrPenVerticesNeeded(radius, gstate->tolerance, &gstate->ctm); pen->vertex = malloc(pen->num_vertices * sizeof(XrPenVertex)); if (pen->vertex == NULL) { @@ -199,17 +199,23 @@ XrPenAddPoints(XrPen *pen, XrPenTaggedPoint *pt, int num_pts) } static int -_XrPenVerticesNeeded(double radius, double tolerance) +_XrPenVerticesNeeded(double radius, double tolerance, XrTransform *matrix) { - /* XXX: BUG: This calculation needs to be corrected to account for - the fact that the radius is specified in user space, while the - tolerance is specified in device space. */ + double e1, e2, emax, theta; + if (tolerance > radius) { return 4; - } else { - double theta = acos(1 - tolerance/radius); - return ceil(M_PI / theta); - } + } + + XrTransformEigenValues(matrix, &e1, &e2); + + if (fabs(e1) > fabs(e2)) + emax = fabs(e1); + else + emax = fabs(e2); + + theta = acos(1 - tolerance/(emax * radius)); + return ceil(M_PI / theta); } static void diff --git a/xrtransform.c b/xrtransform.c index 97cb083b8..7d41e9b3a 100644 --- a/xrtransform.c +++ b/xrtransform.c @@ -151,3 +151,35 @@ XrTransformPoint(XrTransform *transform, XPointDouble *pt) pt->x += transform->m[2][0]; pt->y += transform->m[2][1]; } + +void +XrTransformEigenValues(XrTransform *transform, double *lambda1, double *lambda2) +{ + /* The eigenvalues of an NxN matrix M are found by solving the polynomial: + + det(M - lI) = 0 + + which for our 2x2 matrix: + + M = a b + c d + + gives: + + l^2 - (a+d)l + (ad - bc) = 0 + + l = (a+d +/- sqrt(a^2 + 2ad + d^2 - 4(ad-bc))) / 2; + */ + + double a, b, c, d, rad; + + a = transform->m[0][0]; + b = transform->m[0][1]; + c = transform->m[1][0]; + d = transform->m[1][1]; + + rad = sqrt(a*a + 2*a*d + d*d - 4*(a*d - b*c)); + *lambda1 = (a + d + rad) / 2.0; + *lambda2 = (a + d - rad) / 2.0; +} +