[librsvg/rustification] marker.rs: Port rsvg_rust_render_markers() to Rust



commit 035d6c2451a3daaae1301512c692994121d4f936
Author: Federico Mena Quintero <federico gnome org>
Date:   Mon Oct 31 20:34:59 2016 -0600

    marker.rs: Port rsvg_rust_render_markers() to Rust
    
    This is the interesting part of the old rsvg_render_markers() from C.
    
    That function in C now remains only as a wrapper that extracts a few
    values from the RsvgDrawingCtx and passes them on to
    rsvg_rust_render_markers().  I'll probably add accessor functions later
    so that this kind of wrapper is not necessary.
    
    Also, change the Cargo.toml to build a staticlib.  Re-enable the Rust
    build in the master Makefile.am.
    
    And it works!  The tests pass!
    
    The Makefile.am is fishy; it uses rust/target/debug/*.a instead of
    automatically switching between "debug" and "release".  Also, this is
    not yet integrated into configure.ac at all.
    
    But it works!

 Makefile.am        |   14 +-
 rsvg-marker.c      |  455 ++--------------------------------------------------
 rsvg-marker.h      |    3 +
 rust/Cargo.toml    |    5 +-
 rust/src/lib.rs    |    3 +-
 rust/src/marker.rs |  196 ++++++++++++++++++++++-
 6 files changed, 221 insertions(+), 455 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index febce78..19dcad4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -72,12 +72,12 @@ librsvg_@RSVG_API_MAJOR_VERSION@_la_SOURCES = \
        librsvg-enum-types.h    \
        $(NULL)
 
-#RUST_LIB = rust/target/debug/librsvg_internals.so
+RUST_LIB = rust/target/debug/librsvg_internals.a
 
-#.PHONY: rust/target/debug/librsvg_internals.so
-#rust/target/release/librsvg_internals.so:
-#      cd rust && \
-#      cargo build --verbose
+.PHONY: rust/target/debug/librsvg_internals.a
+rust/target/debug/librsvg_internals.a:
+       cd rust && \
+       cargo build --verbose
 
 librsvg_@RSVG_API_MAJOR_VERSION@_la_CPPFLAGS = \
        -I$(top_srcdir)                         \
@@ -102,8 +102,8 @@ librsvg_@RSVG_API_MAJOR_VERSION@_la_LDFLAGS = \
 
 librsvg_@RSVG_API_MAJOR_VERSION@_la_LIBADD = \
        $(LIBRSVG_LIBS)         \
-       $(LIBM)
-#      rust/target/release/librsvg_internals.so
+       $(LIBM)                 \
+       $(RUST_LIB)
 
 librsvgincdir = $(includedir)/librsvg-$(RSVG_API_VERSION)/librsvg
 librsvginc_HEADERS =   \
diff --git a/rsvg-marker.c b/rsvg-marker.c
index 6b688bb..0ff3317 100644
--- a/rsvg-marker.c
+++ b/rsvg-marker.c
@@ -111,7 +111,7 @@ rsvg_new_marker (void)
     return &marker->super;
 }
 
-static void
+void
 rsvg_marker_render (const char * marker_name, gdouble xpos, gdouble ypos, gdouble orient, gdouble linewidth,
                     RsvgDrawingCtx * ctx)
 {
@@ -203,342 +203,12 @@ rsvg_marker_render (const char * marker_name, gdouble xpos, gdouble ypos, gdoubl
     rsvg_release_node (ctx, (RsvgNode *) self);
 }
 
-typedef struct {
-    gboolean is_degenerate; /* If true, only (p1x, p1y) are valid.  If false, all are valid */
-    double p1x, p1y;
-    double p2x, p2y;
-    double p3x, p3y;
-    double p4x, p4y;
-} Segment;
-
-typedef enum {
-    SEGMENT_START,
-    SEGMENT_END,
-} SegmentState;
-
-/* This converts a cairo_path_t into a list of curveto-like segments.  Each segment can be:
- *
- * 1. segment->is_degenerate = TRUE => the segment is actually a single point (segment->p1x, segment->p1y)
- *
- * 2. segment->is_degenerate = FALSE => either a lineto or a curveto (or the effective lineto that results 
from a closepath).
- *    We have the following points:
- *       P1 = (p1x, p1y)
- *       P2 = (p2x, p2y)
- *       P3 = (p3x, p3y)
- *       P4 = (p4x, p4y)
- *
- *    The start and end points are P1 and P4, respectively.
- *    The tangent at the start point is given by the vector (P2 - P1).
- *    The tangent at the end point is given by the vector (P4 - P3).
- *    The tangents also work if the segment refers to a lineto (they will both just point in the same 
direction).
- */
-
-#define EPSILON 1e-10
-#define DOUBLE_EQUALS(a, b) (fabs ((a) - (b)) < EPSILON)
-
-static void
-path_to_segments (const cairo_path_t *path,
-                  Segment **out_segments,
-                  int *num_segments)
-{
-    int i;
-    double last_x, last_y;
-    double cur_x, cur_y;
-    double subpath_start_x, subpath_start_y;
-    int max_segments;
-    int segment_num;
-    Segment *segments;
-    SegmentState state;
-
-    max_segments = path->num_data; /* We'll generate maximum this many segments */
-    segments = g_new (Segment, max_segments);
-    *out_segments = segments;
-
-    last_x = last_y = cur_x = cur_y = subpath_start_x = subpath_start_y = 0.0;
-
-    segment_num = -1;
-    state = SEGMENT_END;
-
-    for (i = 0; i < path->num_data; i += path->data[i].header.length) {
-        last_x = cur_x;
-        last_y = cur_y;
-
-        switch (path->data[i].header.type) {
-        case CAIRO_PATH_MOVE_TO:
-            segment_num++;
-            g_assert (segment_num < max_segments);
-
-            g_assert (i + 1 < path->num_data);
-            cur_x = path->data[i + 1].point.x;
-            cur_y = path->data[i + 1].point.y;
-
-            subpath_start_x = cur_x;
-            subpath_start_y = cur_y;
-
-            segments[segment_num].is_degenerate = TRUE;
-
-            segments[segment_num].p1x = cur_x;
-            segments[segment_num].p1y = cur_y;
-
-            state = SEGMENT_START;
-
-            break;
-
-        case CAIRO_PATH_LINE_TO:
-            g_assert (i + 1 < path->num_data);
-            cur_x = path->data[i + 1].point.x;
-            cur_y = path->data[i + 1].point.y;
-
-            if (state == SEGMENT_START) {
-                segments[segment_num].is_degenerate = FALSE;
-                state = SEGMENT_END;
-            } else {
-                segment_num++;
-                g_assert (segment_num < max_segments);
-
-                segments[segment_num].is_degenerate = FALSE;
-
-                segments[segment_num].p1x = last_x;
-                segments[segment_num].p1y = last_y;
-            }
-
-            segments[segment_num].p2x = cur_x;
-            segments[segment_num].p2y = cur_y;
-
-            segments[segment_num].p3x = last_x;
-            segments[segment_num].p3y = last_y;
-
-            segments[segment_num].p4x = cur_x;
-            segments[segment_num].p4y = cur_y;
-
-            break;
-
-        case CAIRO_PATH_CURVE_TO:
-            g_assert (i + 3 < path->num_data);
-            cur_x = path->data[i + 3].point.x;
-            cur_y = path->data[i + 3].point.y;
-
-            if (state == SEGMENT_START) {
-                segments[segment_num].is_degenerate = FALSE;
-                state = SEGMENT_END;
-            } else {
-                segment_num++;
-                g_assert (segment_num < max_segments);
-
-                segments[segment_num].is_degenerate = FALSE;
-
-                segments[segment_num].p1x = last_x;
-                segments[segment_num].p1y = last_y;
-            }
-
-            segments[segment_num].p2x = path->data[i + 1].point.x;
-            segments[segment_num].p2y = path->data[i + 1].point.y;
-
-            segments[segment_num].p3x = path->data[i + 2].point.x;
-            segments[segment_num].p3y = path->data[i + 2].point.y;
-
-            segments[segment_num].p4x = cur_x;
-            segments[segment_num].p4y = cur_y;
-
-            /* Fix the tangents for when the middle control points coincide with their respective endpoints 
*/
-
-            if (DOUBLE_EQUALS (segments[segment_num].p2x, segments[segment_num].p1x)
-                && DOUBLE_EQUALS (segments[segment_num].p2y, segments[segment_num].p1y)) {
-                segments[segment_num].p2x = segments[segment_num].p3x;
-                segments[segment_num].p2y = segments[segment_num].p3y;
-            }
-
-            if (DOUBLE_EQUALS (segments[segment_num].p3x, segments[segment_num].p4x)
-                && DOUBLE_EQUALS (segments[segment_num].p3y, segments[segment_num].p4y)) {
-                segments[segment_num].p3x = segments[segment_num].p2x;
-                segments[segment_num].p3y = segments[segment_num].p2y;
-            }
-
-            break;
-
-        case CAIRO_PATH_CLOSE_PATH:
-            cur_x = subpath_start_x;
-            cur_y = subpath_start_y;
-
-            if (state == SEGMENT_START) {
-                segments[segment_num].is_degenerate = FALSE;
-
-                segments[segment_num].p2x = cur_x;
-                segments[segment_num].p2y = cur_y;
-
-                segments[segment_num].p3x = last_x;
-                segments[segment_num].p3y = last_y;
-
-                segments[segment_num].p4x = cur_x;
-                segments[segment_num].p4y = cur_y;
-
-                state = SEGMENT_END;
-            } else {
-                /* nothing; closepath after moveto (or a single lone closepath) does nothing */
-            }
-
-            break;
-
-        default:
-            g_assert_not_reached ();
-        }
-    }
-
-    *num_segments = segment_num + 1;
-    g_assert (*num_segments <= max_segments);
-}
-
-static gboolean
-points_equal (double x1, double y1, double x2, double y2)
-{
-    return DOUBLE_EQUALS (x1, x2) && DOUBLE_EQUALS (y1, y2);
-}
-
-/* A segment is zero length if it is degenerate, or if all four control points
- * coincide (the first and last control points may coincide, but the others may
- * define a loop - thus nonzero length)
- */
-static gboolean
-is_zero_length_segment (Segment *segment)
-{
-    double p1x, p1y;
-    double p2x, p2y;
-    double p3x, p3y;
-    double p4x, p4y;
-
-    if (segment->is_degenerate)
-        return TRUE;
-
-    p1x = segment->p1x;
-    p1y = segment->p1y;
-
-    p2x = segment->p2x;
-    p2y = segment->p2y;
-
-    p3x = segment->p3x;
-    p3y = segment->p3y;
-
-    p4x = segment->p4x;
-    p4y = segment->p4y;
-
-    return (points_equal (p1x, p1y, p2x, p2y)
-            && points_equal (p1x, p1y, p3x, p3y)
-            && points_equal (p1x, p1y, p4x, p4y));
-}
-
-/* The SVG spec 1.1 says http://www.w3.org/TR/SVG/implnote.html#PathElementImplementationNotes
- *
- * Certain line-capping and line-joining situations and markers
- * require that a path segment have directionality at its start and
- * end points. Zero-length path segments have no directionality. In
- * these cases, the following algorithm is used to establish
- * directionality:  to determine the directionality of the start
- * point of a zero-length path segment, go backwards in the path
- * data specification within the current subpath until you find a
- * segment which has directionality at its end point (e.g., a path
- * segment with non-zero length) and use its ending direction;
- * otherwise, temporarily consider the start point to lack
- * directionality. Similarly, to determine the directionality of the
- * end point of a zero-length path segment, go forwards in the path
- * data specification within the current subpath until you find a
- * segment which has directionality at its start point (e.g., a path
- * segment with non-zero length) and use its starting direction;
- * otherwise, temporarily consider the end point to lack
- * directionality. If the start point has directionality but the end
- * point doesn't, then the end point uses the start point's
- * directionality. If the end point has directionality but the start
- * point doesn't, then the start point uses the end point's
- * directionality. Otherwise, set the directionality for the path
- * segment's start and end points to align with the positive x-axis
- * in user space.
- */
-static gboolean
-find_incoming_directionality_backwards (Segment *segments, int num_segments, int start_index, double *vx, 
double *vy)
-{
-    int j;
-    gboolean found;
-
-    /* "go backwards ... within the current subpath until ... segment which has directionality at its end 
point" */
-
-    found = FALSE;
-
-    for (j = start_index; j >= 0; j--) {
-        if (segments[j].is_degenerate)
-            break; /* reached the beginning of the subpath as we ran into a standalone point */
-        else {
-            if (is_zero_length_segment (&segments[j]))
-                continue;
-            else {
-                found = TRUE;
-                break;
-            }
-        }
-    }
-
-    if (found) {
-        g_assert (j >= 0);
-        *vx = segments[j].p4x - segments[j].p3x;
-        *vy = segments[j].p4y - segments[j].p3y;
-        return TRUE;
-    } else {
-        *vx = 0.0;
-        *vy = 0.0;
-        return FALSE;
-    }
-}
-
-static gboolean
-find_outgoing_directionality_forwards (Segment *segments, int num_segments, int start_index, double *vx, 
double *vy)
-{
-    int j;
-    gboolean found;
-
-    /* "go forwards ... within the current subpath until ... segment which has directionality at its start 
point" */
-
-    found = FALSE;
-
-    for (j = start_index; j < num_segments; j++) {
-        if (segments[j].is_degenerate)
-            break; /* reached the end of a subpath as we ran into a standalone point */
-        else {
-            if (is_zero_length_segment (&segments[j]))
-                continue;
-            else {
-                found = TRUE;
-                break;
-            }
-        }
-    }
-
-    if (found) {
-        g_assert (j < num_segments);
-        *vx = segments[j].p2x - segments[j].p1x;
-        *vy = segments[j].p2y - segments[j].p1y;
-        return TRUE;
-    } else {
-        *vx = 0.0;
-        *vy = 0.0;
-        return FALSE;
-    }
-}
-
-static double
-angle_from_vector (double vx, double vy)
-{
-    double angle;
-
-    angle = atan2 (vy, vx);
-
-    if (isnan (angle))
-        return 0.0;
-    else
-        return angle;
-}
-
-typedef enum {
-    NO_SUBPATH,
-    IN_SUBPATH,
-} SubpathState;
+extern void rsvg_rust_render_markers (RsvgDrawingCtx *ctx,
+                                      RsvgPathBuilder *builder,
+                                      double linewidth,
+                                      const char *startmarker,
+                                      const char *middlemarker,
+                                      const char *endmarker);
 
 void
 rsvg_render_markers (RsvgDrawingCtx *ctx,
@@ -546,114 +216,15 @@ rsvg_render_markers (RsvgDrawingCtx *ctx,
 {
     RsvgState *state;
     double linewidth;
-    const char *startmarker;
-    const char *middlemarker;
-    const char *endmarker;
-    cairo_path_t *path;
-
-    int i;
-    double incoming_vx, incoming_vy;
-    double outgoing_vx, outgoing_vy;
-
-    Segment *segments;
-    int num_segments;
-
-    SubpathState subpath_state;
-
 
     state = rsvg_current_state (ctx);
 
     linewidth = _rsvg_css_normalize_length (&state->stroke_width, ctx, 'o');
-    startmarker = state->startMarker;
-    middlemarker = state->middleMarker;
-    endmarker = state->endMarker;
-
-    if (linewidth == 0)
-        return;
-
-    if (!startmarker && !middlemarker && !endmarker)
-        return;
-
-    path = rsvg_path_builder_copy_path (builder);
-
-    if (path->num_data <= 0) {
-        rsvg_cairo_path_destroy (path);
-        return;
-    }
-
-    /* Convert the path to a list of segments and bare points (i.e. degenerate segments) */
-    path_to_segments (path, &segments, &num_segments);
-
-    subpath_state = NO_SUBPATH;
-
-    for (i = 0; i < num_segments; i++) {
-        incoming_vx = incoming_vy = outgoing_vx = outgoing_vy = 0.0;
-
-        if (segments[i].is_degenerate) {
-            if (subpath_state == IN_SUBPATH) {
-                g_assert (i > 0);
-
-                /* Got a lone point after a subpath; render the subpath's end marker first */
-
-                find_incoming_directionality_backwards (segments, num_segments, i - 1, &incoming_vx, 
&incoming_vy);
-                rsvg_marker_render (endmarker, segments[i - 1].p4x, segments[i - 1].p4y, angle_from_vector 
(incoming_vx, incoming_vy), linewidth, ctx);
-            }
-
-            /* Render marker for the lone point; no directionality */
-            rsvg_marker_render (middlemarker, segments[i].p1x, segments[i].p1y, 0.0, linewidth, ctx);
-
-            subpath_state = NO_SUBPATH;
-        } else {
-            /* Not a degenerate segment */
-
-            if (subpath_state == NO_SUBPATH) {
-                find_outgoing_directionality_forwards (segments, num_segments, i, &outgoing_vx, 
&outgoing_vy);
-                rsvg_marker_render (startmarker, segments[i].p1x, segments[i].p1y, angle_from_vector 
(outgoing_vx, outgoing_vy), linewidth, ctx);
-
-                subpath_state = IN_SUBPATH;
-            } else {
-                /* subpath_state == IN_SUBPATH */
-
-                gboolean has_incoming, has_outgoing;
-                double incoming, outgoing;
-                double angle;
-
-                g_assert (i > 0);
-
-                has_incoming = find_incoming_directionality_backwards (segments, num_segments, i - 1, 
&incoming_vx, &incoming_vy);
-                has_outgoing = find_outgoing_directionality_forwards (segments, num_segments, i, 
&outgoing_vx, &outgoing_vy);
-
-                if (has_incoming)
-                    incoming = angle_from_vector (incoming_vx, incoming_vy);
-
-                if (has_outgoing)
-                    outgoing = angle_from_vector (outgoing_vx, outgoing_vy);
-
-                if (has_incoming && has_outgoing)
-                    angle = (incoming + outgoing) / 2;
-                else if (has_incoming)
-                    angle = incoming;
-                else if (has_outgoing)
-                    angle = outgoing;
-                else
-                    angle = 0.0;
-
-                rsvg_marker_render (middlemarker, segments[i].p1x, segments[i].p1y, angle, linewidth, ctx);
-            }
-        }
-    }
-
-    /* Finally, render the last point */
-
-    if (num_segments > 0) {
-        if (!segments[num_segments - 1].is_degenerate) {
-            find_incoming_directionality_backwards (segments, num_segments, num_segments - 1, &incoming_vx, 
&incoming_vy);
-
-            rsvg_marker_render (endmarker, segments[num_segments - 1].p4x, segments[num_segments - 1].p4y, 
angle_from_vector (incoming_vx, incoming_vy), linewidth, ctx);
-        }
-    }
-
-    g_free (segments);
 
-    rsvg_cairo_path_destroy (path);
+    rsvg_rust_render_markers (ctx,
+                              builder,
+                              linewidth,
+                              state->startMarker,
+                              state->middleMarker,
+                              state->endMarker);
 }
diff --git a/rsvg-marker.h b/rsvg-marker.h
index 71f62f5..44d4799 100644
--- a/rsvg-marker.h
+++ b/rsvg-marker.h
@@ -35,6 +35,9 @@ RsvgNode    *rsvg_new_marker      (void);
 G_GNUC_INTERNAL
 void        rsvg_render_markers    (RsvgDrawingCtx *ctx, RsvgPathBuilder *builder);
 
+G_GNUC_INTERNAL
+void rsvg_marker_render (const char * marker_name, gdouble xpos, gdouble ypos, gdouble orient, gdouble 
linewidth, RsvgDrawingCtx * ctx);
+
 G_END_DECLS
 
 #endif                          /* RSVG_MARKER_H */
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 3d427e5..68e3130 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -4,8 +4,9 @@ version = "0.0.1"
 authors = ["Federico Mena Quintero <federico gnome org>"]
 
 [dependencies]
-cairo-rs = "^0"
+libc = "0.2"
+cairo-rs = { git = "file:///home/federico/src/gtk-rs/cairo" }
 
 [lib]
 name = "rsvg_internals"
-crate-type = ["cdylib"]
+crate-type = ["staticlib"]
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index e5f3664..963ad74 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -1,6 +1,5 @@
 pub use marker::{
-    Segment,
-    path_to_segments,
+    rsvg_rust_render_markers,
 };
 
 mod marker;
diff --git a/rust/src/marker.rs b/rust/src/marker.rs
index 48455e9..0dd4bd6 100644
--- a/rust/src/marker.rs
+++ b/rust/src/marker.rs
@@ -1,3 +1,4 @@
+extern crate libc;
 extern crate cairo;
 
 #[derive(Debug)]
@@ -236,7 +237,7 @@ fn get_segment_directionalities (segment: &Segment) -> Option <(f64, f64, f64, f
  * segment's start and end points to align with the positive x-axis
  * in user space.
  */
-fn find_incoming_directionality_backwards (segments: Vec<Segment>, start_index: usize) -> (bool, f64, f64) {
+fn find_incoming_directionality_backwards (segments: &Vec<Segment>, start_index: usize) -> (bool, f64, f64) {
     /* "go backwards ... within the current subpath until ... segment which has directionality at its end 
point" */
 
     for j in (0 .. start_index + 1).rev () {
@@ -257,7 +258,7 @@ fn find_incoming_directionality_backwards (segments: Vec<Segment>, start_index:
     (false, 0.0, 0.0)
 }
 
-fn find_outgoing_directionality_forwards (segments: Vec<Segment>, start_index: usize) -> (bool, f64, f64) {
+fn find_outgoing_directionality_forwards (segments: &Vec<Segment>, start_index: usize) -> (bool, f64, f64) {
     /* "go forwards ... within the current subpath until ... segment which has directionality at its start 
point" */
 
     for j in start_index .. segments.len () {
@@ -278,6 +279,197 @@ fn find_outgoing_directionality_forwards (segments: Vec<Segment>, start_index: u
     (false, 0.0, 0.0)
 }
 
+fn angle_from_vector (vx: f64, vy: f64) -> f64 {
+    let angle = vy.atan2 (vx);
+
+    if angle.is_nan () {
+        0.0
+    } else {
+        angle
+    }
+}
+
+pub enum RsvgDrawingCtx {}
+pub enum RsvgPathBuilder {}
+
+extern "C" {
+    fn rsvg_path_builder_copy_path (builder: *mut RsvgPathBuilder) -> *mut cairo::cairo_path_t;
+    fn rsvg_marker_render (marker_name: *const libc::c_char,
+                           xpos: f64,
+                           ypos: f64,
+                           orient: f64,
+                           linewidth: f64,
+                           ctx: *mut RsvgDrawingCtx);
+}
+
+enum SubpathState {
+    NoSubpath,
+    InSubpath
+}
+
+fn render_marker_at_start_of_segment (segment: &Segment,
+                                      marker_name: *const libc::c_char,
+                                      orient: f64,
+                                      linewidth: f64,
+                                      ctx: *mut RsvgDrawingCtx) {
+    let xpos: f64;
+    let ypos: f64;
+
+    match *segment {
+        Segment::Degenerate { x, y } => {
+            xpos = x;
+            ypos = y;
+        },
+
+        Segment::LineOrCurve { x1, y1, .. } => {
+            xpos = x1;
+            ypos = y1;
+        }
+    }
+
+    unsafe { rsvg_marker_render (marker_name, xpos, ypos, orient, linewidth, ctx); }
+}
+
+fn render_marker_at_end_of_segment (segment: &Segment,
+                                    marker_name: *const libc::c_char,
+                                    orient: f64,
+                                    linewidth: f64,
+                                    ctx: *mut RsvgDrawingCtx) {
+    let xpos: f64;
+    let ypos: f64;
+
+    match *segment {
+        Segment::Degenerate { x, y } => {
+            xpos = x;
+            ypos = y;
+        },
+
+        Segment::LineOrCurve { x4, y4, .. } => {
+            xpos = x4;
+            ypos = y4;
+        }
+    }
+
+    unsafe { rsvg_marker_render (marker_name, xpos, ypos, orient, linewidth, ctx); }
+}
+
+#[no_mangle]
+pub extern fn rsvg_rust_render_markers (ctx: *mut RsvgDrawingCtx,
+                                        builder: *mut RsvgPathBuilder,
+                                        linewidth: f64,
+                                        startmarker: *const libc::c_char,
+                                        middlemarker: *const libc::c_char,
+                                        endmarker: *const libc::c_char) {
+    if linewidth == 0.0 {
+        return;
+    }
+
+    if startmarker.is_null () && middlemarker.is_null () && endmarker.is_null () {
+        return;
+    }
+
+    let cairopath: *mut cairo::cairo_path_t;
+
+    unsafe { cairopath = rsvg_path_builder_copy_path (builder); }
+    let path = cairo::Path::wrap (cairopath);
+
+    /* Convert the path to a list of segments and bare points */
+    let segments = path_to_segments (path);
+
+    let mut subpath_state = SubpathState::NoSubpath;
+
+    for (i, segment) in segments.iter ().enumerate () {
+        match *segment {
+            Segment::Degenerate { .. } => {
+                match subpath_state {
+                    SubpathState::InSubpath => {
+                        assert! (i > 0);
+
+                        /* Got a lone point after a subpath; render the subpath's end marker first */
+
+                        let (_, incoming_vx, incoming_vy) = find_incoming_directionality_backwards 
(&segments, i - 1);
+                        render_marker_at_end_of_segment (&segments[i - 1], endmarker, angle_from_vector 
(incoming_vx, incoming_vy), linewidth, ctx);
+                    },
+
+                    _ => { }
+                }
+
+                /* Render marker for the lone point; no directionality */
+                render_marker_at_start_of_segment (segment, middlemarker, 0.0, linewidth, ctx);
+
+                subpath_state = SubpathState::NoSubpath;
+            },
+
+            Segment::LineOrCurve { .. } => {
+                /* Not a degenerate segment */
+
+                match subpath_state {
+                    SubpathState::NoSubpath => {
+                        let (_, outgoing_vx, outgoing_vy) = find_outgoing_directionality_forwards 
(&segments, i);
+                        render_marker_at_start_of_segment (segment, startmarker, angle_from_vector 
(outgoing_vx, outgoing_vy), linewidth, ctx);
+
+                        subpath_state = SubpathState::InSubpath;
+                    },
+
+                    SubpathState::InSubpath => {
+                        assert! (i > 0);
+
+                        let (has_incoming, incoming_vx, incoming_vy) = 
find_incoming_directionality_backwards (&segments, i - 1);
+                        let (has_outgoing, outgoing_vx, outgoing_vy) = find_outgoing_directionality_forwards 
(&segments, i);
+
+                        let incoming: f64;
+                        let outgoing: f64;
+
+                        if has_incoming {
+                            incoming = angle_from_vector (incoming_vx, incoming_vy);
+                        } else {
+                            incoming = 0.0;
+                        }
+
+                        if has_outgoing {
+                            outgoing = angle_from_vector (outgoing_vx, outgoing_vy);
+                        } else {
+                            outgoing = 0.0;
+                        }
+
+                        let angle: f64;
+
+                        if has_incoming && has_outgoing {
+                            angle = (incoming + outgoing) / 2.0;
+                        } else if has_incoming {
+                            angle = incoming;
+                        } else if has_outgoing {
+                            angle = outgoing;
+                        } else {
+                            angle = 0.0;
+                        }
+
+                        render_marker_at_start_of_segment (segment, middlemarker, angle, linewidth, ctx);
+                    }
+                }
+            }
+        }
+    }
+
+    /* Finally, render the last point */
+
+    if segments.len() > 0 {
+        let segment = &segments[segments.len() - 1];
+        match *segment {
+            Segment::LineOrCurve { .. } => {
+                let (_, incoming_vx, incoming_vy) = find_incoming_directionality_backwards (&segments, 
segments.len () - 1);
+
+                render_marker_at_end_of_segment (&segment, endmarker, angle_from_vector (incoming_vx, 
incoming_vy), linewidth, ctx);
+            },
+
+            _ => { }
+        }
+    }
+}
+
+
+/******************** Tests ********************/
+
 #[cfg(test)]
 mod tests {
     use super::*;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]