Shouldn't Cairo use/offer degrees rather than radians?

Submitted by David Kastrup on July 1, 2017, 6:51 a.m.

Details

Message ID 87efu0hcq3.fsf@fencepost.gnu.org
State New
Series "Shouldn't Cairo use/offer degrees rather than radians?"
Headers show

Commit Message

David Kastrup July 1, 2017, 6:51 a.m.
Bill Spitzak <spitzak@gmail.com> writes:

> On Fri, Jun 30, 2017 at 1:44 PM, David Kastrup <dak@gnu.org> wrote:
>
>> I am not calling any atan2d.
>
> Your code copied and pasted from the previous email. If this is not
> your code please explain where the code you want to use is:

Appended again.  Sorry for the atan2d thing: I should have checked for
completeness, and quoting vector->angle code with respect to Cairo was
rather pointless in the first place.

The missing code unsurprisingly was

static inline Real
atan2d (Real y, Real x)
{
  return atan2 (y, x) * (180.0 / M_PI);
}

but with regard to Cairo, the direction vector->angle is hardly
relevant, with angle->vector being the seminal API in question.

I'll append the proposed patch again (at the bottom)

David Kastrup

Patch hide | download patch | download mbox

From a057b17b5018000435cc011d7a2d20072457badb Mon Sep 17 00:00:00 2001
From: David Kastrup <dak@gnu.org>
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