[librsvg/rustification] marker.rs: Port rsvg_rust_render_markers() to Rust
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg/rustification] marker.rs: Port rsvg_rust_render_markers() to Rust
- Date: Tue, 1 Nov 2016 02:37:52 +0000 (UTC)
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]