[libshumate] vector: Avoid sharp angles in line symbols
- From: Marcus Lundblad <mlundblad src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libshumate] vector: Avoid sharp angles in line symbols
- Date: Thu, 23 Jun 2022 21:16:33 +0000 (UTC)
commit 95e36975cd062a7150f03f5faeb0c1263832d1e4
Author: James Westman <james jwestman net>
Date: Tue May 17 17:07:10 2022 -0500
vector: Avoid sharp angles in line symbols
.../vector/shumate-vector-render-scope-private.h | 3 +-
shumate/vector/shumate-vector-render-scope.c | 63 +++++++++++------
shumate/vector/shumate-vector-symbol-layer.c | 82 ++++++++++++++--------
shumate/vector/shumate-vector-utils-private.h | 2 +-
shumate/vector/shumate-vector-utils.c | 43 +++++++++++-
5 files changed, 136 insertions(+), 57 deletions(-)
---
diff --git a/shumate/vector/shumate-vector-render-scope-private.h
b/shumate/vector/shumate-vector-render-scope-private.h
index d53f3cb..eb121b3 100644
--- a/shumate/vector/shumate-vector-render-scope-private.h
+++ b/shumate/vector/shumate-vector-render-scope-private.h
@@ -46,7 +46,6 @@ void shumate_vector_render_scope_get_bounds (ShumateVectorRenderScope *self,
double *max_x,
double *max_y);
-void shumate_vector_render_scope_get_geometry (ShumateVectorRenderScope *self,
- ShumateVectorLineString *linestring);
+GPtrArray *shumate_vector_render_scope_get_geometry (ShumateVectorRenderScope *self);
void shumate_vector_render_scope_get_variable (ShumateVectorRenderScope *self, const char *variable,
ShumateVectorValue *value);
diff --git a/shumate/vector/shumate-vector-render-scope.c b/shumate/vector/shumate-vector-render-scope.c
index 8b9106b..ca905f2 100644
--- a/shumate/vector/shumate-vector-render-scope.c
+++ b/shumate/vector/shumate-vector-render-scope.c
@@ -17,6 +17,12 @@
#include "shumate-vector-render-scope-private.h"
+enum {
+ MOVE_TO = 1,
+ LINE_TO = 2,
+ CLOSE_PATH = 7,
+};
+
/* Sets the current layer by name. */
gboolean
shumate_vector_render_scope_find_layer (ShumateVectorRenderScope *self, const char *layer_name)
@@ -62,19 +68,19 @@ shumate_vector_render_scope_exec_geometry (ShumateVectorRenderScope *self)
for (int j = 0; j < repeat; j ++)
{
switch (op) {
- case 1:
+ case MOVE_TO:
g_return_if_fail (i + 2 < self->feature->n_geometry);
dx = zigzag (self->feature->geometry[++i]);
dy = zigzag (self->feature->geometry[++i]);
cairo_rel_move_to (self->cr, dx, dy);
break;
- case 2:
+ case LINE_TO:
g_return_if_fail (i + 2 < self->feature->n_geometry);
dx = zigzag (self->feature->geometry[++i]);
dy = zigzag (self->feature->geometry[++i]);
cairo_rel_line_to (self->cr, dx, dy);
break;
- case 7:
+ case CLOSE_PATH:
cairo_get_current_point (self->cr, &dx, &dy);
cairo_close_path (self->cr);
cairo_move_to (self->cr, dx, dy);
@@ -87,15 +93,14 @@ shumate_vector_render_scope_exec_geometry (ShumateVectorRenderScope *self)
}
-void
-shumate_vector_render_scope_get_geometry (ShumateVectorRenderScope *self,
- ShumateVectorLineString *linestring)
+GPtrArray *
+shumate_vector_render_scope_get_geometry (ShumateVectorRenderScope *self)
{
- ShumateVectorPoint *points = NULL;
- gsize point_index = 0;
+ GPtrArray *lines = g_ptr_array_new ();
+ ShumateVectorLineString *current_line = NULL;
double x = 0, y = 0;
- g_return_if_fail (self->feature != NULL);
+ g_return_val_if_fail (self->feature != NULL, NULL);
for (int i = 0; i < self->feature->n_geometry; i ++)
{
@@ -105,34 +110,47 @@ shumate_vector_render_scope_get_geometry (ShumateVectorRenderScope *self,
int op = cmd & 0x7;
int repeat = cmd >> 3;
- points = g_realloc (points, (repeat + point_index) * sizeof (ShumateVectorPoint));
- g_return_if_fail (points != NULL);
+ if (current_line != NULL)
+ current_line->points = g_realloc (current_line->points, (repeat + current_line->n_points) * sizeof
(ShumateVectorPoint));
for (int j = 0; j < repeat; j ++)
{
switch (op) {
- case 1:
- g_return_if_fail (i + 2 < self->feature->n_geometry);
+ case MOVE_TO:
+ g_return_val_if_fail (i + 2 < self->feature->n_geometry, NULL);
+
+ if (current_line != NULL)
+ g_ptr_array_add (lines, current_line);
+
+ current_line = g_new (ShumateVectorLineString, 1);
+ current_line->points = g_new (ShumateVectorPoint, 1);
+ current_line->n_points = 1;
+
x += zigzag (self->feature->geometry[++i]);
y += zigzag (self->feature->geometry[++i]);
start_x = x;
start_y = y;
- points[point_index++] = (ShumateVectorPoint) {
+
+ current_line->points[0] = (ShumateVectorPoint) {
.x = x / self->layer->extent,
.y = y / self->layer->extent,
};
break;
- case 2:
- g_return_if_fail (i + 2 < self->feature->n_geometry);
+ case LINE_TO:
+ g_return_val_if_fail (i + 2 < self->feature->n_geometry, NULL);
+ g_return_val_if_fail (current_line != NULL, NULL);
+
x += zigzag (self->feature->geometry[++i]);
y += zigzag (self->feature->geometry[++i]);
- points[point_index++] = (ShumateVectorPoint) {
+ current_line->points[current_line->n_points++] = (ShumateVectorPoint) {
.x = x / self->layer->extent,
.y = y / self->layer->extent,
};
break;
- case 7:
- points[point_index++] = (ShumateVectorPoint) {
+ case CLOSE_PATH:
+ g_return_val_if_fail (current_line != NULL, NULL);
+
+ current_line->points[current_line->n_points++] = (ShumateVectorPoint) {
.x = start_x / self->layer->extent,
.y = start_y / self->layer->extent,
};
@@ -143,8 +161,10 @@ shumate_vector_render_scope_get_geometry (ShumateVectorRenderScope *self,
}
}
- linestring->n_points = point_index;
- linestring->points = points;
+ if (current_line != NULL)
+ g_ptr_array_add (lines, current_line);
+
+ return lines;
}
@@ -265,3 +285,4 @@ shumate_vector_render_scope_get_variable (ShumateVectorRenderScope *self, const
}
}
}
+
diff --git a/shumate/vector/shumate-vector-symbol-layer.c b/shumate/vector/shumate-vector-symbol-layer.c
index 82a28d5..af5a253 100644
--- a/shumate/vector/shumate-vector-symbol-layer.c
+++ b/shumate/vector/shumate-vector-symbol-layer.c
@@ -126,47 +126,69 @@ shumate_vector_symbol_layer_render (ShumateVectorLayer *layer, ShumateVectorRend
if (strlen (text_field) == 0)
return;
- symbol_info = shumate_vector_symbol_info_new (text_field,
- &text_color,
- text_size,
- self->text_fonts,
- self->line_placement,
- x,
- y);
-
if (self->line_placement)
{
- ShumateVectorLineString linestring;
- shumate_vector_render_scope_get_geometry (scope, &linestring);
- shumate_vector_line_string_simplify (&linestring);
- shumate_vector_symbol_info_set_line_points (symbol_info, &linestring);
+ g_autoptr(GPtrArray) lines = shumate_vector_render_scope_get_geometry (scope);
+ guint i, j;
+
+ for (i = 0; i < lines->len; i ++)
+ {
+ g_autoptr(GPtrArray) split_lines = shumate_vector_line_string_simplify ((ShumateVectorLineString
*)lines->pdata[i]);
+
+ for (j = 0; j < split_lines->len; j ++)
+ {
+ ShumateVectorLineString *linestring = (ShumateVectorLineString *)split_lines->pdata[j];
+ shumate_vector_line_string_simplify (linestring);
#if 0
- /* visualize line simplification */
+ /* visualize line simplification */
- line_points = shumate_vector_render_scope_get_geometry (scope, &num_points);
- shumate_vector_line_simplify (line_points, &num_points);
+ if (linestring->n_points > 0)
+ {
+ guint k;
+ float scale = scope->scale * scope->target_size;
- if (num_points > 0)
- {
- float scale = scope->scale * scope->target_size;
+ cairo_set_source_rgb (scope->cr,
+ rand () % 255 / 255.0,
+ rand () % 255 / 255.0,
+ rand () % 255 / 255.0);
+ cairo_set_line_width (scope->cr, scope->scale);
+
+ cairo_move_to (scope->cr, linestring->points[0].x * scale, linestring->points[0].y *
scale);
+ for (k = 1; k < linestring->n_points; k ++)
+ cairo_line_to (scope->cr, linestring->points[k].x * scale, linestring->points[k].y *
scale);
- cairo_set_source_rgb (scope->cr,
- rand () % 255 / 255.0,
- rand () % 255 / 255.0,
- rand () % 255 / 255.0);
- cairo_set_line_width (scope->cr, scope->scale);
+ cairo_stroke (scope->cr);
+ }
+#endif
+
+ symbol_info = shumate_vector_symbol_info_new (text_field,
+ &text_color,
+ text_size,
+ self->text_fonts,
+ TRUE,
+ x,
+ y);
- cairo_move_to (scope->cr, line_points[0].x * scale, line_points[0].y * scale);
- for (gsize i = 1; i < num_points; i ++)
- cairo_line_to (scope->cr, line_points[i].x * scale, line_points[i].y * scale);
+ shumate_vector_symbol_info_set_line_points (symbol_info,
+ linestring);
- cairo_stroke (scope->cr);
+ g_ptr_array_add (scope->symbols, symbol_info);
+ }
}
-#endif
}
-
- g_ptr_array_add (scope->symbols, symbol_info);
+ else
+ {
+ symbol_info = shumate_vector_symbol_info_new (text_field,
+ &text_color,
+ text_size,
+ self->text_fonts,
+ FALSE,
+ x,
+ y);
+
+ g_ptr_array_add (scope->symbols, symbol_info);
+ }
}
diff --git a/shumate/vector/shumate-vector-utils-private.h b/shumate/vector/shumate-vector-utils-private.h
index df26269..712992c 100644
--- a/shumate/vector/shumate-vector-utils-private.h
+++ b/shumate/vector/shumate-vector-utils-private.h
@@ -96,4 +96,4 @@ double shumate_vector_line_string_length (ShumateVectorLineString *
void shumate_vector_line_string_bounds (ShumateVectorLineString *linestring,
ShumateVectorPoint *radius_out,
ShumateVectorPoint *center_out);
-void shumate_vector_line_string_simplify (ShumateVectorLineString *linestring);
+GPtrArray *shumate_vector_line_string_simplify (ShumateVectorLineString *linestring);
diff --git a/shumate/vector/shumate-vector-utils.c b/shumate/vector/shumate-vector-utils.c
index f76e314..09596b9 100644
--- a/shumate/vector/shumate-vector-utils.c
+++ b/shumate/vector/shumate-vector-utils.c
@@ -369,13 +369,16 @@ shumate_vector_line_string_bounds (ShumateVectorLineString *linestring,
}
-void
+GPtrArray *
shumate_vector_line_string_simplify (ShumateVectorLineString *linestring)
{
- gint i;
+ gsize i;
+ GPtrArray *result = g_ptr_array_new ();
+
+ g_ptr_array_add (result, linestring);
if (linestring->n_points <= 2)
- return;
+ return result;
/* The glyph layout algorithm for line symbols does not handle high detail
* very well. Lots of short segments with different angles cause it to place
@@ -428,4 +431,38 @@ shumate_vector_line_string_simplify (ShumateVectorLineString *linestring)
for (i = min_idx + 1; i < linestring->n_points; i ++)
linestring->points[i] = linestring->points[i + 1];
}
+
+ /* Line labels also don't look good if there's sharp angles. To fix that, we
+ * split the line wherever one occurs. */
+
+ for (i = linestring->n_points - 2; i >= 1; i --)
+ {
+ /* Angle between three points. See https://math.stackexchange.com/a/3427603 */
+
+ /* Dot product of points[i] to points[i - 1], and points[i] to points[i + 1] */
+ float dot = (linestring->points[i].x - linestring->points[i + 1].x)
+ * (linestring->points[i].x - linestring->points[i - 1].x)
+ + ((linestring->points[i].y - linestring->points[i + 1].y)
+ * (linestring->points[i].y - linestring->points[i - 1].y));
+
+ float len = sqrt (point_distance_sq (&linestring->points[i], &linestring->points[i + 1])
+ * point_distance_sq (&linestring->points[i], &linestring->points[i - 1]));
+
+ float angle = fabs (acos (dot / len));
+
+ if (angle < 120 * G_PI / 180)
+ {
+ ShumateVectorLineString *new_line = g_new (ShumateVectorLineString, 1);
+
+ /* Copy from the current point until the end of the line */
+ new_line->n_points = linestring->n_points - i;
+ new_line->points = g_memdup2 (&linestring->points[i], new_line->n_points * sizeof
(ShumateVectorPoint));
+
+ linestring->n_points = i + 1;
+
+ g_ptr_array_add (result, new_line);
+ }
+ }
+
+ return result;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]