1 /* GnomeCanvas Bezier polyline paths & segments
2 *
3 * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
4 * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
5 *
6 * Copyright (C) 1998,1999 The Free Software Foundation
7 *
8 * Authors: Federico Mena <federico@nuclecu.unam.mx>
9 * Lauris Kaplinski <lauris@ariman.ee>
10 * Raph Levien <raph@acm.org>
11 * Jasper van de Gronde <th.v.d.gronde@hccnet.nl>
12 */
14 #include <cstring>
15 #include <string>
16 #include <cstdlib>
17 #include <cstdio>
18 #include <glib/gmem.h>
19 #include <glib/gmessages.h>
20 #include <algorithm>
22 #include "libnr/n-art-bpath.h"
23 #include "libnr/nr-point-ops.h"
24 #include "gnome-canvas-bpath-util.h"
25 #include "prefs-utils.h"
27 static inline NR::Point distTo(GnomeCanvasBpathDef *bpd, size_t idx1, size_t idx2, unsigned int coord1=3, unsigned int coord2=3) {
28 NR::Point diff(bpd->bpath[idx1].c(coord1) - bpd->bpath[idx2].c(coord2));
29 return NR::Point(std::abs(diff[NR::X]), std::abs(diff[NR::Y]));
30 }
32 static bool isApproximatelyClosed(GnomeCanvasBpathDef *bpd) {
33 int const np = prefs_get_int_attribute("options.svgoutput", "numericprecision", 8);
34 double const precision = pow(10.0, -(np+1)); // This roughly corresponds to a difference below the last significant digit
35 int const initial = bpd->moveto_idx;
36 int const current = bpd->n_bpath - 1;
37 NR::Point distToInit(distTo(bpd, current, initial));
38 // TODO check the double to int conversion in the abs() call:
39 return
40 distToInit[NR::X] <= abs(bpd->bpath[current].c(3)[NR::X])*precision &&
41 distToInit[NR::Y] <= abs(bpd->bpath[current].c(3)[NR::Y])*precision;
42 }
44 GnomeCanvasBpathDef *
45 gnome_canvas_bpath_def_new (void)
46 {
47 GnomeCanvasBpathDef *bpd;
49 bpd = g_new (GnomeCanvasBpathDef, 1);
50 bpd->n_bpath = 0;
51 bpd->n_bpath_max = 16;
52 bpd->moveto_idx = -1;
53 bpd->bpath = g_new (NArtBpath, bpd->n_bpath_max);
54 bpd->ref_count = 1;
56 return bpd;
57 }
59 GnomeCanvasBpathDef *
60 gnome_canvas_bpath_def_new_from (NArtBpath *path)
61 {
62 GnomeCanvasBpathDef *bpd;
63 int i;
65 g_return_val_if_fail (path != NULL, NULL);
67 bpd = g_new (GnomeCanvasBpathDef, 1);
69 for (i = 0; path [i].code != NR_END; i++)
70 ;
71 bpd->n_bpath = i;
72 bpd->n_bpath_max = i;
73 bpd->moveto_idx = -1;
74 bpd->ref_count = 1;
75 bpd->bpath = g_new (NArtBpath, i);
77 memcpy (bpd->bpath, path, i * sizeof (NArtBpath));
78 return bpd;
79 }
81 GnomeCanvasBpathDef *
82 gnome_canvas_bpath_def_ref (GnomeCanvasBpathDef *bpd)
83 {
84 g_return_val_if_fail (bpd != NULL, NULL);
86 bpd->ref_count += 1;
87 return bpd;
88 }
90 void
91 gnome_canvas_bpath_def_free (GnomeCanvasBpathDef *bpd)
92 {
93 g_return_if_fail (bpd != NULL);
95 bpd->ref_count -= 1;
96 if (bpd->ref_count == 0) {
97 g_free (bpd->bpath);
98 g_free (bpd);
99 }
100 }
102 void
103 gnome_canvas_bpath_def_moveto (GnomeCanvasBpathDef *bpd, double x, double y)
104 {
105 NArtBpath *bpath;
106 int n_bpath;
108 g_return_if_fail (bpd != NULL);
110 n_bpath = bpd->n_bpath++;
112 if (n_bpath == bpd->n_bpath_max)
113 bpd->bpath = (NArtBpath*)g_realloc (bpd->bpath,
114 (bpd->n_bpath_max <<= 1) * sizeof (NArtBpath));
115 bpath = bpd->bpath;
116 bpath[n_bpath].code = NR_MOVETO_OPEN;
117 bpath[n_bpath].x3 = x;
118 bpath[n_bpath].y3 = y;
119 bpd->moveto_idx = n_bpath;
120 }
122 void
123 gnome_canvas_bpath_def_lineto (GnomeCanvasBpathDef *bpd, double x, double y)
124 {
125 NArtBpath *bpath;
126 int n_bpath;
128 g_return_if_fail (bpd != NULL);
129 g_return_if_fail (bpd->moveto_idx >= 0);
131 n_bpath = bpd->n_bpath++;
133 if (n_bpath == bpd->n_bpath_max)
134 bpd->bpath = (NArtBpath*)g_realloc (bpd->bpath,
135 (bpd->n_bpath_max <<= 1) * sizeof (NArtBpath));
136 bpath = bpd->bpath;
137 bpath[n_bpath].code = NR_LINETO;
138 bpath[n_bpath].x3 = x;
139 bpath[n_bpath].y3 = y;
140 }
142 void
143 gnome_canvas_bpath_def_curveto (GnomeCanvasBpathDef *bpd, double x1, double y1, double x2, double y2, double x3, double y3)
144 {
145 NArtBpath *bpath;
146 int n_bpath;
148 g_return_if_fail (bpd != NULL);
149 g_return_if_fail (bpd->moveto_idx >= 0);
151 n_bpath = bpd->n_bpath++;
153 if (n_bpath == bpd->n_bpath_max)
154 bpd->bpath = (NArtBpath*)g_realloc (bpd->bpath,
155 (bpd->n_bpath_max <<= 1) * sizeof (NArtBpath));
156 bpath = bpd->bpath;
157 bpath[n_bpath].code = NR_CURVETO;
158 bpath[n_bpath].x1 = x1;
159 bpath[n_bpath].y1 = y1;
160 bpath[n_bpath].x2 = x2;
161 bpath[n_bpath].y2 = y2;
162 bpath[n_bpath].x3 = x3;
163 bpath[n_bpath].y3 = y3;
164 }
166 void
167 gnome_canvas_bpath_def_closepath (GnomeCanvasBpathDef *bpd)
168 {
169 NArtBpath *bpath;
170 int n_bpath;
172 g_return_if_fail (bpd != NULL);
173 g_return_if_fail (bpd->moveto_idx >= 0);
174 g_return_if_fail (bpd->n_bpath > 0);
176 bpath = bpd->bpath;
177 n_bpath = bpd->n_bpath;
179 /* Add closing vector if we need it. */
180 if (!isApproximatelyClosed(bpd)) {
181 gnome_canvas_bpath_def_lineto (bpd, bpath[bpd->moveto_idx].x3,
182 bpath[bpd->moveto_idx].y3);
183 bpath = bpd->bpath;
184 } else {
185 // If it is approximately closed we close it here to prevent internal logic to fail.
186 // In addition it is probably better to continue working with this end point, as it
187 // is probably more precise than the original.
188 bpath[n_bpath-1].x3 = bpath[bpd->moveto_idx].x3;
189 bpath[n_bpath-1].y3 = bpath[bpd->moveto_idx].y3;
190 }
191 bpath[bpd->moveto_idx].code = NR_MOVETO;
192 bpd->moveto_idx = -1;
193 }
195 void
196 gnome_canvas_bpath_def_art_finish (GnomeCanvasBpathDef *bpd)
197 {
198 int n_bpath;
200 g_return_if_fail (bpd != NULL);
202 n_bpath = bpd->n_bpath++;
204 if (n_bpath == bpd->n_bpath_max)
205 bpd->bpath = (NArtBpath*)g_realloc (bpd->bpath,
206 (bpd->n_bpath_max <<= 1) * sizeof (NArtBpath));
207 bpd->bpath [n_bpath].code = NR_END;
208 }