[librsvg/rustification] path_builder.rs: Port rsvg_path_builder_arc() to Rust.
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg/rustification] path_builder.rs: Port rsvg_path_builder_arc() to Rust.
- Date: Thu, 3 Nov 2016 15:20:13 +0000 (UTC)
commit 5c7a8bae58286bc3b1d6cefd43a9d3db4bc44c67
Author: Federico Mena Quintero <federico gnome org>
Date: Wed Nov 2 21:34:57 2016 -0600
path_builder.rs: Port rsvg_path_builder_arc() to Rust.
This is no longer in the C code. We need tests for this!
rsvg-path.c | 160 -----------------------------------
rust/src/lib.rs | 1 +
rust/src/path_builder.rs | 210 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 211 insertions(+), 160 deletions(-)
---
diff --git a/rsvg-path.c b/rsvg-path.c
index 7150a83..d37a5a2 100644
--- a/rsvg-path.c
+++ b/rsvg-path.c
@@ -58,166 +58,6 @@ typedef struct {
double params[7]; /* parameters that have been parsed */
} RSVGParsePathCtx;
-static void
-rsvg_path_arc_segment (RsvgPathBuilder *builder,
- double xc, double yc,
- double th0, double th1, double rx, double ry,
- double x_axis_rotation)
-{
- double x1, y1, x2, y2, x3, y3;
- double t;
- double th_half;
- double f, sinf, cosf;
-
- f = x_axis_rotation * M_PI / 180.0;
- sinf = sin(f);
- cosf = cos(f);
-
- th_half = 0.5 * (th1 - th0);
- t = (8.0 / 3.0) * sin (th_half * 0.5) * sin (th_half * 0.5) / sin (th_half);
- x1 = rx*(cos (th0) - t * sin (th0));
- y1 = ry*(sin (th0) + t * cos (th0));
- x3 = rx*cos (th1);
- y3 = ry*sin (th1);
- x2 = x3 + rx*(t * sin (th1));
- y2 = y3 + ry*(-t * cos (th1));
-
- rsvg_path_builder_curve_to (builder,
- xc + cosf*x1 - sinf*y1,
- yc + sinf*x1 + cosf*y1,
- xc + cosf*x2 - sinf*y2,
- yc + sinf*x2 + cosf*y2,
- xc + cosf*x3 - sinf*y3,
- yc + sinf*x3 + cosf*y3);
-}
-
-/**
- * rsvg_path_builder_arc:
- * @builder: Path builder.
- * @x1: Starting x coordinate
- * @y1: Starting y coordinate
- * @rx: Radius in x direction (before rotation).
- * @ry: Radius in y direction (before rotation).
- * @x_axis_rotation: Rotation angle for axes.
- * @large_arc_flag: 0 for arc length <= 180, 1 for arc >= 180.
- * @sweep_flag: 0 for "negative angle", 1 for "positive angle".
- * @x2: Ending x coordinate
- * @y2: Ending y coordinate
- *
- * Add an RSVG arc to the path context.
- **/
-void
-rsvg_path_builder_arc (RsvgPathBuilder *builder,
- double x1, double y1,
- double rx, double ry,
- double x_axis_rotation,
- gboolean large_arc_flag, gboolean sweep_flag,
- double x2, double y2)
-{
-
- /* See Appendix F.6 Elliptical arc implementation notes
- http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes */
-
- double f, sinf, cosf;
- double x1_, y1_;
- double cx_, cy_, cx, cy;
- double gamma;
- double theta1, delta_theta;
- double k1, k2, k3, k4, k5;
-
- int i, n_segs;
-
- if (x1 == x2 && y1 == y2)
- return;
-
- /* X-axis */
- f = x_axis_rotation * M_PI / 180.0;
- sinf = sin (f);
- cosf = cos (f);
-
- rx = fabs (rx);
- ry = fabs (ry);
-
- /* Check the radius against floading point underflow.
- See http://bugs.debian.org/508443 */
- if ((rx < DBL_EPSILON) || (ry < DBL_EPSILON)) {
- rsvg_path_builder_line_to (builder, x2, y2);
- return;
- }
-
- k1 = (x1 - x2) / 2;
- k2 = (y1 - y2) / 2;
-
- x1_ = cosf * k1 + sinf * k2;
- y1_ = -sinf * k1 + cosf * k2;
-
- gamma = (x1_ * x1_) / (rx * rx) + (y1_ * y1_) / (ry * ry);
- if (gamma > 1) {
- rx *= sqrt (gamma);
- ry *= sqrt (gamma);
- }
-
- /* Compute the center */
-
- k1 = rx * rx * y1_ * y1_ + ry * ry * x1_ * x1_;
- if (k1 == 0)
- return;
-
- k1 = sqrt (fabs ((rx * rx * ry * ry) / k1 - 1));
- if (sweep_flag == large_arc_flag)
- k1 = -k1;
-
- cx_ = k1 * rx * y1_ / ry;
- cy_ = -k1 * ry * x1_ / rx;
-
- cx = cosf * cx_ - sinf * cy_ + (x1 + x2) / 2;
- cy = sinf * cx_ + cosf * cy_ + (y1 + y2) / 2;
-
- /* Compute start angle */
-
- k1 = (x1_ - cx_) / rx;
- k2 = (y1_ - cy_) / ry;
- k3 = (-x1_ - cx_) / rx;
- k4 = (-y1_ - cy_) / ry;
-
- k5 = sqrt (fabs (k1 * k1 + k2 * k2));
- if (k5 == 0)
- return;
-
- k5 = k1 / k5;
- k5 = CLAMP (k5, -1, 1);
- theta1 = acos (k5);
- if (k2 < 0)
- theta1 = -theta1;
-
- /* Compute delta_theta */
-
- k5 = sqrt (fabs ((k1 * k1 + k2 * k2) * (k3 * k3 + k4 * k4)));
- if (k5 == 0)
- return;
-
- k5 = (k1 * k3 + k2 * k4) / k5;
- k5 = CLAMP (k5, -1, 1);
- delta_theta = acos (k5);
- if (k1 * k4 - k3 * k2 < 0)
- delta_theta = -delta_theta;
-
- if (sweep_flag && delta_theta < 0)
- delta_theta += M_PI * 2;
- else if (!sweep_flag && delta_theta > 0)
- delta_theta -= M_PI * 2;
-
- /* Now draw the arc */
-
- n_segs = ceil (fabs (delta_theta / (M_PI * 0.5 + 0.001)));
-
- for (i = 0; i < n_segs; i++)
- rsvg_path_arc_segment (builder, cx, cy,
- theta1 + i * delta_theta / n_segs,
- theta1 + (i + 1) * delta_theta / n_segs,
- rx, ry, x_axis_rotation);
-}
-
/* supply defaults for missing parameters, assuming relative coordinates
are to be interpreted as x,y */
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index b27892e..ab7c4aa 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -5,6 +5,7 @@ pub use path_builder::{
rsvg_path_builder_line_to,
rsvg_path_builder_curve_to,
rsvg_path_builder_close_path,
+ rsvg_path_builder_arc,
rsvg_path_builder_add_to_cairo_context
};
diff --git a/rust/src/path_builder.rs b/rust/src/path_builder.rs
index 2f1ebcb..db19331 100644
--- a/rust/src/path_builder.rs
+++ b/rust/src/path_builder.rs
@@ -1,3 +1,5 @@
+use std::f64;
+
extern crate cairo;
extern crate cairo_sys;
@@ -34,6 +36,193 @@ impl RsvgPathBuilder {
pub fn get_path_segments (&self) -> &Vec<cairo::PathSegment> {
&self.path_segments
}
+
+ /**
+ * x1/y1: starting coordinates
+ * rx/ry: radiuses before rotation
+ * x_axis_rotation: Rotation angle for axes, in degrees
+ * is_large_arc: false for arc length <= 180, true for arc >= 180
+ * is_sweep: false for negative angle, true for positive angle
+ * x2/y2: ending coordinates
+ */
+ pub fn arc (&mut self,
+ x1: f64, y1: f64,
+ mut rx: f64, mut ry: f64,
+ x_axis_rotation: f64,
+ is_large_arc: bool,
+ is_sweep: bool,
+ x2: f64, y2: f64) {
+ /* See Appendix F.6 Elliptical arc implementation notes
+ http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes */
+
+ let f: f64;
+ let sinf: f64;
+ let cosf: f64;
+ let x1_: f64;
+ let y1_: f64;
+ let cx_: f64;
+ let cy_: f64;
+ let cx: f64;
+ let cy: f64;
+ let gamma: f64;
+ let mut theta1: f64;
+ let mut delta_theta: f64;
+ let mut k1: f64;
+ let mut k2: f64;
+ let k3: f64;
+ let k4: f64;
+ let mut k5: f64;
+ let n_segs: i32;
+
+ if x1 == x2 && y1 == y2 {
+ return;
+ }
+
+ /* X-axis */
+ f = x_axis_rotation * f64::consts::PI / 180.0;
+ sinf = f.sin ();
+ cosf = f.cos ();
+
+ rx = rx.abs ();
+ ry = ry.abs ();
+
+ /* Check the radius against floading point underflow.
+ See http://bugs.debian.org/508443 */
+ if (rx < f64::EPSILON) || (ry < f64::EPSILON) {
+ self.line_to (x2, y2);
+ return;
+ }
+
+ k1 = (x1 - x2) / 2.0;
+ k2 = (y1 - y2) / 2.0;
+
+ x1_ = cosf * k1 + sinf * k2;
+ y1_ = -sinf * k1 + cosf * k2;
+
+ gamma = (x1_ * x1_) / (rx * rx) + (y1_ * y1_) / (ry * ry);
+ if gamma > 1.0 {
+ rx *= gamma.sqrt ();
+ ry *= gamma.sqrt ();
+ }
+
+ /* Compute the center */
+
+ k1 = rx * rx * y1_ * y1_ + ry * ry * x1_ * x1_;
+ if k1 == 0.0 {
+ return;
+ }
+
+ k1 = ((rx * rx * ry * ry) / k1 - 1.0).abs ().sqrt ();
+ if is_sweep == is_large_arc {
+ k1 = -k1;
+ }
+
+ cx_ = k1 * rx * y1_ / ry;
+ cy_ = -k1 * ry * x1_ / rx;
+
+ cx = cosf * cx_ - sinf * cy_ + (x1 + x2) / 2.0;
+ cy = sinf * cx_ + cosf * cy_ + (y1 + y2) / 2.0;
+
+ /* Compute start angle */
+
+ k1 = (x1_ - cx_) / rx;
+ k2 = (y1_ - cy_) / ry;
+ k3 = (-x1_ - cx_) / rx;
+ k4 = (-y1_ - cy_) / ry;
+
+ k5 = (k1 * k1 + k2 * k2).abs ().sqrt ();
+ if k5 == 0.0 {
+ return;
+ }
+
+ k5 = k1 / k5;
+ k5 = clamp (k5, -1.0, 1.0);
+ theta1 = k5.acos ();
+ if k2 < 0.0 {
+ theta1 = -theta1;
+ }
+
+ /* Compute delta_theta */
+
+ k5 = ((k1 * k1 + k2 * k2) * (k3 * k3 + k4 * k4)).abs ().sqrt ();
+ if k5 == 0.0 {
+ return;
+ }
+
+ k5 = (k1 * k3 + k2 * k4) / k5;
+ k5 = clamp (k5, -1.0, 1.0);
+ delta_theta = k5.acos ();
+ if k1 * k4 - k3 * k2 < 0.0 {
+ delta_theta = -delta_theta;
+ }
+
+ if is_sweep && delta_theta < 0.0 {
+ delta_theta += f64::consts::PI * 2.0;
+ } else if !is_sweep && delta_theta > 0.0 {
+ delta_theta -= f64::consts::PI * 2.0;
+ }
+
+ /* Now draw the arc */
+
+ n_segs = (delta_theta / (f64::consts::PI * 0.5 + 0.001)).abs ().ceil () as i32;
+ let n_segs_dbl = n_segs as f64;
+
+ for i in 0 .. n_segs {
+ self.arc_segment (cx, cy,
+ theta1 + i as f64 * delta_theta / n_segs_dbl,
+ theta1 + (i + 1) as f64 * delta_theta / n_segs_dbl,
+ rx, ry,
+ x_axis_rotation);
+ }
+ }
+
+ fn arc_segment (&mut self,
+ xc: f64, yc: f64,
+ th0: f64, th1: f64,
+ rx: f64, ry: f64,
+ x_axis_rotation: f64) {
+ let x1: f64;
+ let y1: f64;
+ let x2: f64;
+ let y2: f64;
+ let x3: f64;
+ let y3: f64;
+ let t: f64;
+ let th_half: f64;
+ let f: f64;
+ let sinf: f64;
+ let cosf: f64;
+
+ f = x_axis_rotation * f64::consts::PI / 180.0;
+ sinf = f.sin ();
+ cosf = f.cos ();
+
+ th_half = 0.5 * (th1 - th0);
+ t = (8.0 / 3.0) * (th_half * 0.5).sin () * (th_half * 0.5).sin () / th_half.sin ();
+ x1 = rx * (th0.cos () - t * th0.sin ());
+ y1 = ry * (th0.sin () + t * th0.cos ());
+ x3 = rx * th1.cos ();
+ y3 = ry * th1.sin ();
+ x2 = x3 + rx * (t * th1.sin ());
+ y2 = y3 + ry * (-t * th1.cos ());
+
+ self.curve_to (xc + cosf * x1 - sinf * y1,
+ yc + sinf * x1 + cosf * y1,
+ xc + cosf * x2 - sinf * y2,
+ yc + sinf * x2 + cosf * y2,
+ xc + cosf * x3 - sinf * y3,
+ yc + sinf * x3 + cosf * y3);
+ }
+}
+
+fn clamp (val: f64, low: f64, high: f64) -> f64 {
+ if val < low {
+ low
+ } else if val > high {
+ high
+ } else {
+ val
+ }
}
#[no_mangle]
@@ -87,6 +276,27 @@ pub extern fn rsvg_path_builder_curve_to (raw_builder: *mut RsvgPathBuilder,
}
#[no_mangle]
+pub extern fn rsvg_path_builder_arc (raw_builder: *mut RsvgPathBuilder,
+ x1: f64, y1: f64,
+ rx: f64, ry: f64,
+ x_axis_rotation: f64,
+ large_arc_flag: bool,
+ sweep_flag: bool,
+ x2: f64, y2: f64) {
+ assert! (!raw_builder.is_null ());
+
+ let builder: &mut RsvgPathBuilder = unsafe { &mut (*raw_builder) };
+
+ builder.arc (x1, y1,
+ rx, ry,
+ x_axis_rotation,
+ large_arc_flag,
+ sweep_flag,
+ x2, y2);
+}
+
+
+#[no_mangle]
pub extern fn rsvg_path_builder_close_path (raw_builder: *mut RsvgPathBuilder) {
assert! (!raw_builder.is_null ());
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]