[dia] path: fix Difference sometimes not closing the path
- From: Hans Breuer <hans src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [dia] path: fix Difference sometimes not closing the path
- Date: Sun, 7 Sep 2014 16:55:33 +0000 (UTC)
commit 2edd83fbcd2f5afe4464de2ec40d7d8c0cbe32a1
Author: Hans Breuer <hans breuer org>
Date: Sun Sep 7 16:09:43 2014 +0200
path: fix Difference sometimes not closing the path
distance_bez_shape_point() was miscalculating the distance of
non-closed path making selection harder. Now handled because
such path can be created by other means, too.
_curve_from_segment() did not correctly flip a line-to, the
remaining changes should simply increase readability (typos,
documentation updates)
unrelated: stdpath_get_object_menu() make "Show Control Lines"
a toggle menu.
lib/geometry.c | 15 ++++++++++++++-
lib/path-math.c | 50 +++++++++++++++++++++++++++++++++-----------------
lib/standard-path.c | 15 ++++++++++-----
3 files changed, 57 insertions(+), 23 deletions(-)
---
diff --git a/lib/geometry.c b/lib/geometry.c
index 1e95562..ebbe07a 100644
--- a/lib/geometry.c
+++ b/lib/geometry.c
@@ -341,6 +341,7 @@ distance_bez_shape_point(const BezPoint *b, guint npoints,
real line_width, const Point *point)
{
Point last;
+ const Point *close_to; /* path must be closed to calculate distance */
guint i;
real line_dist = G_MAXFLOAT;
guint crossings = 0;
@@ -348,6 +349,7 @@ distance_bez_shape_point(const BezPoint *b, guint npoints,
g_return_val_if_fail(b[0].type == BEZ_MOVE_TO, -1);
last = b[0].p1;
+ close_to = &b[0].p1;
for (i = 1; i < npoints; i++) {
real dist;
@@ -356,12 +358,15 @@ distance_bez_shape_point(const BezPoint *b, guint npoints,
case BEZ_MOVE_TO:
/* no complains, there are renderers capable to handle this */
last = b[i].p1;
+ close_to = &b[i].p1;
break;
case BEZ_LINE_TO:
dist = distance_line_point(&last, &b[i].p1, line_width, point);
crossings += line_crosses_ray(&last, &b[i].p1, point);
line_dist = MIN(line_dist, dist);
last = b[i].p1;
+ if (close_to && close_to->x == last.x && close_to->y == last.y)
+ close_to = NULL;
break;
case BEZ_CURVE_TO:
dist = bez_point_distance_and_ray_crosses(&last, &b[i].p1, &b[i].p2,
@@ -369,11 +374,19 @@ distance_bez_shape_point(const BezPoint *b, guint npoints,
&crossings);
line_dist = MIN(line_dist, dist);
last = b[i].p3;
+ if (close_to && close_to->x == last.x && close_to->y == last.y)
+ close_to = NULL;
break;
}
}
+ if (close_to) {
+ /* final, implicit line-to */
+ real dist = distance_line_point(&last, close_to, line_width, point);
+ crossings += line_crosses_ray(&last, close_to, point);
+ line_dist = MIN(line_dist, dist);
+ }
/* If there is an odd number of ray crossings, we are inside the polygon.
- * Otherwise, return the minium distance from a line segment */
+ * Otherwise, return the minimum distance from a line segment */
if (crossings % 2 == 1)
return 0.0;
else
diff --git a/lib/path-math.c b/lib/path-math.c
index 90b1e58..7adf6f6 100644
--- a/lib/path-math.c
+++ b/lib/path-math.c
@@ -150,6 +150,15 @@ _segment_is_moveto (const BezierSegment *bs)
return TRUE;
return FALSE;
}
+static gboolean
+_segment_is_lineto (const BezierSegment *bs)
+{
+ if ( memcmp (&bs->p0, &bs->p1, sizeof (Point)) != 0 /* not move-to */
+ && memcmp (&bs->p1, &bs->p2, sizeof (Point)) == 0
+ && memcmp (&bs->p1, &bs->p3, sizeof (Point)) == 0)
+ return TRUE;
+ return FALSE;
+}
/* search precision */
static const real EPSILON = 0.0001;
@@ -201,9 +210,11 @@ bezier_bezier_intersection (GArray *crossing,
/* if the boxes are small enough we can calculate the point */
if (small_a && small_b) {
/* intersecting and both small, should not matter which one is used */
- Point pt = { (bbox.right + bbox.left) / 2, (bbox.bottom + bbox.top) / 2 };
+ Point pt = { (abox.right + abox.left + bbox.right + bbox.left) / 4,
+ (abox.bottom + abox.top + bbox.bottom + bbox.top) / 4 };
Intersection is;
int i;
+
for (i = 0; i < crossing->len; ++i) {
/* if it's already included we are done */
if (distance_point_point (&g_array_index (crossing, Intersection, i).pt, &pt) < 1.4142*EPSILON)
@@ -262,7 +273,7 @@ _curve_from_segment (BezPoint *bp, const BezierSegment *a, gboolean flip)
{
if (_segment_is_moveto (a))
bp->type = BEZ_MOVE_TO;
- else if (memcmp (&a->p1, &a->p2, sizeof(Point)) == 0)
+ else if (_segment_is_lineto (a))
bp->type = BEZ_LINE_TO;
else
bp->type = BEZ_CURVE_TO;
@@ -271,9 +282,13 @@ _curve_from_segment (BezPoint *bp, const BezierSegment *a, gboolean flip)
bp->p2 = a->p2;
bp->p3 = a->p3;
} else {
- bp->p1 = a->p2;
- bp->p2 = a->p1;
- bp->p3 = a->p0;
+ if (bp->type != BEZ_CURVE_TO) {
+ bp->p1 = bp->p2 = bp->p3 = a->p0;
+ } else {
+ bp->p1 = a->p2;
+ bp->p2 = a->p1;
+ bp->p3 = a->p0;
+ }
}
}
@@ -323,7 +338,14 @@ _path_to_segments (const GArray *path)
g_array_append_val (segs, bs);
}
/* if the path is not closed do an explicit line-to */
- if (memcmp (&last_move->p1, &bs.p3, sizeof(Point)) != 0) {
+ if (distance_point_point (&last_move->p1, &bs.p3) < EPSILON) {
+ /* if the error is small enough just modify the last point */
+ BezierSegment *e = &g_array_index (segs, BezierSegment, segs->len - 1);
+ if (_segment_is_lineto (e))
+ e->p1 = e->p2 = e->p3 = last_move->p1;
+ else
+ e->p3 = last_move->p1;
+ } else {
bs.p0 = bs.p3;
bs.p1 = bs.p2 = bs.p3 = last_move->p1;
g_array_append_val (segs, bs);
@@ -354,7 +376,7 @@ _compare_split (gconstpointer as, gconstpointer bs)
* all segment splits and create unique segment index.
*
* Split.seg is the index to the segment to split before this function.
- * After the split it indexes the last segment in the row of splits.
+ * After the splits are applied every split.seq is unique.
*/
static void
_split_segments (GArray *segs, GArray *splits, const GArray *other)
@@ -401,16 +423,14 @@ _split_segments (GArray *segs, GArray *splits, const GArray *other)
Split *sp = &g_array_index (splits, Split, i);
BezierSegment *bs = &g_array_index (segs, BezierSegment, sp->seg);
BezierSegment left, right;
- Point pt;
int to, j;
if (i == 0 && sp->seg > 0)
g_array_append_vals (pending, &g_array_index (segs, BezierSegment, 0), sp->seg);
bezier_split (bs, &left, &right);
- pt = right.p0;
sp->outside = distance_bez_shape_point (&g_array_index (other, BezPoint, 0), other->len,
- 0 /* line width */, &pt) > 0.0;
+ 0 /* line width */, &right.p0) > 0.0;
/* also remember the sub-path */
to = g_array_index (splits, Split, (i+1)%splits->len).seg;
sp->path = g_array_new (FALSE, FALSE, sizeof(BezierSegment));
@@ -711,9 +731,9 @@ _make_path (GArray *one, /*!< array<BezierSegment> from first path */
* \brief Combine two path into a single one with the given operation
*
* This should (but does not) consider
+ * - holes within the path more explicitely
* - self intersections in a path
- * - winding rule
- * - typical path operations (Union, Difference, Intersection, Exclusion, ...)
+ * - winding rule?
*/
GArray *
path_combine (const GArray *p1,
@@ -733,10 +753,6 @@ path_combine (const GArray *p1,
one = _path_to_segments (p1);
two = _path_to_segments (p2);
crossing = _find_intersections (one, two);
-
- if (crossing)
- g_print ("Total path intersections: %d\n", crossing->len);
-
if (crossing) {
/* Now crossing includes points in arbitrary order. Every point has four lines
* going in or out - two from p1, two from p2. Split one and two into segments
@@ -747,7 +763,7 @@ path_combine (const GArray *p1,
_split_segments (one, one_splits, p2);
_split_segments (two, two_splits, p1);
- /* XXX: convert segments back to a single path */
+ /* convert segments back to a single path */
if (one_splits->len < 2) { /* XXX: just joining again */
result = _make_path0 (one, one_splits, two, two_splits);
} else if (debug) {
diff --git a/lib/standard-path.c b/lib/standard-path.c
index 3f2f224..df77007 100644
--- a/lib/standard-path.c
+++ b/lib/standard-path.c
@@ -29,7 +29,7 @@
* with a single move-to, too.
* Instead of breaking forward compatibility this is implementing a new bezier
* based object, which can render holes, even if the _DiaRenderer can not. It is
- * using a newer file format representaion through BezPointarrayProperty.
+ * using a newer file format representation through BezPointarrayProperty.
*/
#include <config.h>
@@ -143,7 +143,7 @@ static PropDescription stdpath_props[] = {
PROP_STD_LINE_JOIN_OPTIONAL,
PROP_STD_LINE_CAPS_OPTIONAL,
PROP_STD_FILL_COLOUR_OPTIONAL,
- /* just to simplify transfering properties between objects */
+ /* just to simplify transferring properties between objects */
{ "show_background", PROP_TYPE_BOOL, PROP_FLAG_DONT_SAVE, N_("Draw background"), NULL, NULL },
{ "show_control_lines", PROP_TYPE_BOOL, PROP_FLAG_OPTIONAL, N_("Draw Control Lines") },
{ "pattern", PROP_TYPE_PATTERN, PROP_FLAG_VISIBLE|PROP_FLAG_OPTIONAL, N_("Pattern"), NULL },
@@ -551,7 +551,7 @@ _show_control_lines (DiaObject *obj, Point *clicked, gpointer data)
static DiaMenuItem _stdpath_menu_items[] = {
{ N_("Convert to Bezier"), _convert_to_beziers_callback, NULL, DIAMENU_ACTIVE },
{ N_("Invert Path"), _invert_path_callback, NULL, DIAMENU_ACTIVE },
- { N_("Show Control Lines"), _show_control_lines, NULL, DIAMENU_ACTIVE }
+ { N_("Show Control Lines"), _show_control_lines, NULL, DIAMENU_ACTIVE | DIAMENU_TOGGLE }
};
static DiaMenu _stdpath_menu = {
"Path",
@@ -570,6 +570,11 @@ static DiaMenu _stdpath_menu = {
static DiaMenu *
stdpath_get_object_menu(StdPath *stdpath, Point *clickedpoint)
{
+ const int i = G_N_ELEMENTS(_stdpath_menu_items) - 1;
+ if (stdpath->show_control_lines)
+ _stdpath_menu_items[i].active |= DIAMENU_TOGGLE_ON;
+ else
+ _stdpath_menu_items[i].active &= ~DIAMENU_TOGGLE_ON;
return &_stdpath_menu;
}
/*!
@@ -605,8 +610,8 @@ stdpath_set_props (StdPath *stdpath, GPtrArray *props)
else
stdpath->stroke_or_fill &= ~PDO_FILL;
}
- /* now when transfering properties from text we'll loose stroke and fill
- * Instead of drawing nothing maket it just fill.
+ /* now when transferring properties from text we'll loose stroke and fill
+ * Instead of drawing nothing make it just fill.
*/
if (!stdpath->stroke_or_fill)
stdpath->stroke_or_fill = PDO_FILL;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]