Re: Shouldn't Cairo use/offer degrees rather than radians?
David Kastrup <[email protected]>
| Newsgroups | gmane.comp.lib.cairo |
|---|---|
| Organization | Organization?!? |
| Message-ID | <[email protected]> |
Lawrence D'Oliveiro <[email protected]> writes: > On Thu, 29 Jun 2017 08:08:04 +0200, David Kastrup wrote: > >> Lawrence D'Oliveiro <[email protected]> writes: >> >>> You realize that, by introducing this distortion, you are reducing >>> the accuracy of the computation? >> >> It replaces a sin(x)/cos(x) pair by a sin(x)/sin(pi/2-x) pair. The >> largest cumulative effect is at pi/4. > > So you introduce a kink at π/4. How is this any better, again? There is no kink. None at all. The handover between different functions happens at sin(pi/2) where there is a large stretch of 1.0. At pi/4, there is no discontinuity but the sin/cos pair is taken as (sin(pi/4), sin(pi/4)) instead of (sin(pi/4), cos(pi/4)). Since pi/4 has no actual numeric representation, getting an exact angle of 45 degree in the transformation matrix may come at the cost of a larger magnitude jitter of the determinant _iff_ you have a very, very accurate sin/cos implementation. Mind you: that jitter is still less than 2ulp so you need a very good sin/cos implementation to even measure it. Square the transform matrix for 45 degrees 20 times or so with the degree function and the radian function and compare the results and see which you like better. I'll append the patch to Cairo to make it easier for you. >>> I wonder what the term is, for deliberately producing inaccurate >>> answers just to look good... >> >> A very well-considered tradeoff and solid engineering? > > “Engineering” is not the term that comes to mind. Try “marketing” or > “management”. Please compare the results. > You do realize that your claim about Cairo’s “inability to reliably > draw a half circle that seamlessly connects with another half circle > under any rasterization” is complete nonsense, right? Sigh. If you cannot specify an angle for pi for which sin(angle)==0, you will have rasterizations of half circle arcs that won't line up perfectly because you then cannot make the half circles butt up perfectly, and "too small to be noticeable" becomes a statistic rather than a static feature under rasterization. -- David Kastrup -- cairo mailing list [email protected] https://lists.cairographics.org/mailman/listinfo/cairo
0001-Implement-cairo_matrix_init_rotate_deg-and-cairo_mat.patch
(text/x-diff, 5.8 KB)
From a057b17b5018000435cc011d7a2d20072457badb Mon Sep 17 00:00:00 2001 From: David Kastrup <[email protected]> Date: Fri, 30 Jun 2017 07:01:26 +0200 Subject: [PATCH] Implement cairo_matrix_init_rotate_deg and cairo_matrix_rotate_deg Those offer variants of cairo_matrix_init_rotate and cairo_matrix_rotate that are numerically perfect at right angles without introducing discontinuities. --- doc/public/cairo-sections.txt | 2 + src/cairo-matrix.c | 92 +++++++++++++++++++++++++++++++++++++++++++ src/cairo.h | 7 ++++ src/cairoint.h | 1 + 4 files changed, 102 insertions(+) diff --git a/doc/public/cairo-sections.txt b/doc/public/cairo-sections.txt index 7b04ae7b3..ac5e566a0 100644 --- a/doc/public/cairo-sections.txt +++ b/doc/public/cairo-sections.txt @@ -430,9 +430,11 @@ cairo_matrix_init_identity cairo_matrix_init_translate cairo_matrix_init_scale cairo_matrix_init_rotate +cairo_matrix_init_rotate_deg cairo_matrix_translate cairo_matrix_scale cairo_matrix_rotate +cairo_matrix_rotate_deg cairo_matrix_invert cairo_matrix_multiply cairo_matrix_transform_distance diff --git a/src/cairo-matrix.c b/src/cairo-matrix.c index ae498f515..8728bcdee 100644 --- a/src/cairo-matrix.c +++ b/src/cairo-matrix.c @@ -312,6 +312,98 @@ cairo_matrix_rotate (cairo_matrix_t *matrix, double radians) } /** + * cairo_matrix_init_rotate_deg: + * @matrix: a #cairo_matrix_t + * @degrees: angle of rotation, in degrees. The direction of rotation + * is defined such that positive angles rotate in the direction from + * the positive X axis toward the positive Y axis. With the default + * axis orientation of cairo, positive angles rotate in a clockwise + * direction. + * For angles that are multiples of 90 degrees, the transformation + * matrix is numerically exact. + * + * Initializes @matrix to a + * transformation that rotates by @degrees. + * + * Since: 1.16 + **/ +void +cairo_matrix_init_rotate_deg (cairo_matrix_t *matrix, + double degrees) +{ + double s; + double c; + + if (unlikely (degrees <= -540.0 || degrees >= 540.0)) + degrees = fmod (degrees, 360.0); + /* Now |degrees| < 540.0, and the absolute size is not larger than + before, so we haven't lost precision. */ + if (unlikely (degrees <= -180.0)) + degrees += 360.0; + else if (unlikely (degrees > 180.0)) + degrees -= 360.0; + /* Now -180.0 < degrees <= 180.0 and we still haven't lost + precision. We don't work with angles greater than 90 degrees + absolute in order to minimize how rounding errors of M_PI/180 + affect the result. The "handover" between one sine expression + to the next happens at angles of +-90 degrees where + sin(pi/2+eps) is about (1-eps^2/2). Since the difference to + pi/2 should be quite small, the sine will be numerically 1 here. + + Sign of the sine is chosen to avoid -0.0 in results. This + version delivers exactly equal magnitude on x/y for odd + multiples of 45 degrees. */ + + if (degrees >= 0) { + c = sin ((90 - degrees) * (M_PI/180.0)); + if (degrees > 90) + s = sin ((180 - degrees) * (M_PI/180.0)); + else + s = sin (degrees * (M_PI/180.0)); + } else { + c = sin ((90 + degrees) * (M_PI/180.0)); + if (degrees < -90) + s = sin ((-180 - degrees) * (M_PI/180.0)); + else + s = sin (degrees * (M_PI/180.0)); + } + + cairo_matrix_init (matrix, + c, s, + -s, c, + 0, 0); +} +slim_hidden_def(cairo_matrix_init_rotate_deg); + +/** + * cairo_matrix_rotate_deg: + * @matrix: a #cairo_matrix_t + * @degrees: angle of rotation, in degrees. The direction of rotation + * is defined such that positive angles rotate in the direction from + * the positive X axis toward the positive Y axis. With the default + * axis orientation of cairo, positive angles rotate in a clockwise + * direction. + * For angles that are multiples of 90 degrees, the change to the + * transformation matrix is numerically exact. + * + * Applies rotation by @degrees to the transformation in + * @matrix. The effect of the new transformation is to first rotate the + * coordinates by @degrees, then apply the original transformation + * to the coordinates. + * + * Since: 1.16 + **/ +void +cairo_matrix_rotate_deg (cairo_matrix_t *matrix, double degrees) +{ + cairo_matrix_t tmp; + + cairo_matrix_init_rotate_deg (&tmp, degrees); + + cairo_matrix_multiply (matrix, &tmp, matrix); +} + +/** * cairo_matrix_multiply: * @result: a #cairo_matrix_t in which to store the result * @a: a #cairo_matrix_t diff --git a/src/cairo.h b/src/cairo.h index 32fc88b17..310f4c6d5 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -3026,6 +3026,10 @@ cairo_matrix_init_rotate (cairo_matrix_t *matrix, double radians); cairo_public void +cairo_matrix_init_rotate_deg (cairo_matrix_t *matrix, + double degrees); + +cairo_public void cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty); cairo_public void @@ -3034,6 +3038,9 @@ cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy); cairo_public void cairo_matrix_rotate (cairo_matrix_t *matrix, double radians); +cairo_public void +cairo_matrix_rotate_deg (cairo_matrix_t *matrix, double degrees); + cairo_public cairo_status_t cairo_matrix_invert (cairo_matrix_t *matrix); diff --git a/src/cairoint.h b/src/cairoint.h index 4fedf861d..f04e81131 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -1941,6 +1941,7 @@ slim_hidden_proto (cairo_mask); slim_hidden_proto (cairo_matrix_init); slim_hidden_proto (cairo_matrix_init_identity); slim_hidden_proto (cairo_matrix_init_rotate); +slim_hidden_proto (cairo_matrix_init_rotate_deg); slim_hidden_proto (cairo_matrix_init_scale); slim_hidden_proto (cairo_matrix_init_translate); slim_hidden_proto (cairo_matrix_invert); -- 2.11.0