Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
auto-cross.cpp
Go to the documentation of this file.
1/* @brief
2 * A toy for playing around with Path::intersectSelf().
3 *
4 * Authors:
5 * Rafał Siejakowski <rs@rs-math.net>
6 *
7 * Copyright 2022 the Authors.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it either under the terms of the GNU Lesser General Public
11 * License version 2.1 as published by the Free Software Foundation
12 * (the "LGPL") or, at your option, under the terms of the Mozilla
13 * Public License Version 1.1 (the "MPL"). If you do not alter this
14 * notice, a recipient may use your version of this file under either
15 * the MPL or the LGPL.
16 *
17 * You should have received a copy of the LGPL along with this library
18 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * You should have received a copy of the MPL along with this library
21 * in the file COPYING-MPL-1.1
22 *
23 * The contents of this file are subject to the Mozilla Public License
24 * Version 1.1 (the "License"); you may not use this file except in
25 * compliance with the License. You may obtain a copy of the License at
26 * http://www.mozilla.org/MPL/
27 *
28 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
29 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
30 * the specific language governing rights and limitations.
31 */
32
34#include <2geom/path.h>
37
38using namespace Geom;
39using Color = uint32_t;
40
41Color const RED = 0x80000000;
42Color const GREEN = 0x00800000;
43Color const BROWN = 0x90500000;
44Color const BLUE = 0x0000ff00;
45Color const BLACK = 0x00000000;
46
48{
49 cairo_set_source_rgba(c, (double)((rgb & 0xFF000000) >> 24) / 255.0,
50 (double)((rgb & 0x00FF0000) >> 16) / 255.0,
51 (double)((rgb & 0x0000FF00) >> 8) / 255.0,
52 1.0);
53}
54
55static void write_text(cairo_t *c, const char *text, Point const &position, Color color)
56{
57 cairo_move_to(c, position);
58 cairo_set_font_size(c, 12);
59 set_cairo_rgb(c, color);
60 cairo_show_text(c, text);
61}
62
63static std::string format_point(Point const &pt)
64{
65 std::ostringstream ss;
66 ss.precision(4);
67 ss << pt;
68 return ss.str();
69}
70
72{
73 double const dist = distance(from, to);
74 auto angle = atan2(to - from);
75 bool sweep = std::abs(angle) > M_PI_2;
76 angle *= 2;
77 angle = std::fmod(angle, 2.0 * M_PI);
78 return EllipticalArc(from, Point(0.5 * dist, 2.0 * dist), angle, false, sweep, to);
79}
80
81class Item
82{
83private:
84 Path _path;
85 Color _color;
86 std::string _d;
87
88public:
89 Item(Color color)
90 : _color{color}
91 {}
92
93 void setPath(Path &&new_path)
94 {
95 _path = std::forward<Path>(new_path);
96 std::ostringstream oss;
97 oss << _path;
98 _d = oss.str();
99 }
100
101 void draw(cairo_t *cr) const
102 {
103 cairo_set_line_width(cr, 2);
104 set_cairo_rgb(cr, _color);
105 cairo_path(cr, _path);
106 cairo_stroke(cr);
107 _drawBezierTangents(cr);
108 _drawSelfIntersections(cr);
109 }
110
111 void write(cairo_t *cr, Point const &pos) const
112 {
113 write_text(cr, _d.c_str(), pos, _color);
114 }
115
116 std::string const& getSVGD() const { return _d; }
117
118private:
119 void _drawBezierTangents(cairo_t *c) const
120 {
121 cairo_set_line_width(c, 1);
122 set_cairo_rgb(c, 0x0000b000);
123 // Draw tangents for Beziers:
124 for (auto const &curve : _path) {
125 if (auto const *bezier = dynamic_cast<BezierCurve const *>(&curve)) {
126 if (bezier->order() > 1) {
127 auto points = bezier->controlPoints();
128 cairo_move_to(c, points[0]);
129 cairo_line_to(c, points[1]);
130 cairo_stroke(c);
131 cairo_move_to(c, points.back());
132 cairo_line_to(c, points[points.size() - 2]);
133 cairo_stroke(c);
134 }
135 }
136 }
137 }
138
139 void _drawSelfIntersections(cairo_t *cr) const
140 {
141 set_cairo_rgb(cr, BLACK);
142 for (auto const &xing : _path.intersectSelf()) {
143 draw_cross(cr, xing.point());
144 auto const coords = format_point(xing.point());
145 write_text(cr, coords.c_str(), xing.point() + Point(8, -8), BLACK);
146 }
147 }
148};
149
150class AutoCross : public Toy
151{
152public:
153 AutoCross()
154 : items{Item(RED), Item(GREEN), Item(BROWN)}
155 {
156 bezier_handles.pts = { {200, 400}, {100, 300}, {300, 400}, {300, 300}, {450, 300}, {500, 500}, {400, 400} };
157 elliptical_handles.pts = { {500, 200}, {700, 400}, {600, 500} };
158 mixed_handles.pts = { {100, 600}, {120, 690}, {300, 650}, {330, 600}, {500, 800} };
159 handles.push_back(&bezier_handles);
160 handles.push_back(&elliptical_handles);
161 handles.push_back(&mixed_handles);
162 }
163
164 void draw(cairo_t *cr, std::ostringstream *notify, int width, int height, bool save,
165 std::ostringstream *timer_stream) override
166 {
167 if (crashed) {
168 draw_error(cr, width, height);
169 } else {
170 try {
171 draw_impl(cr, width, height);
172 } catch (Exception &e) {
173 error = e.what();
174 handles.clear();
175 crashed = true;
176 }
177 }
178 Toy::draw(cr, notify, width, height, save, timer_stream);
179 }
180
181 void key_hit(unsigned keyval, unsigned modifiers) override
182 {
183 if (keyval == GDK_KEY_space) {
184 print_path_d();
185 } else if ((keyval == GDK_KEY_V || keyval == GDK_KEY_v) && (modifiers & GDK_CONTROL_MASK)) {
186 paste_d();
187 }
188 }
189
190private:
191 std::string error;
192 std::vector<Item> items;
193 PointSetHandle bezier_handles, elliptical_handles, mixed_handles;
194 bool crashed = false;
195
196 void paste_d()
197 {
198 get_clipboard_text([this] (char const *text) {
199 if (!text) {
200 return;
201 }
202
203 PathVector pv;
204 try {
205 pv = parse_svg_path(text);
206 } catch (SVGPathParseError const &error) {
207 std::cerr << "Error pasting path d: " << error.what() << std::endl;
208 return;
209 }
210 if (pv.empty()) {
211 return;
212 }
213 Item paste_item{RED}; // TODO: cycle through a color palette.
214 paste_item.setPath(std::move(pv[0]));
215 items.push_back(paste_item);
216 redraw();
217 });
218 }
219
220 void print_path_d()
221 {
222 std::cout << "Path snapshots:\n";
223 for (auto it = items.rbegin(); it != items.rend(); ++it) {
224 std::cout << it->getSVGD() << '\n';
225 }
226 }
227
228 void refresh_geometry()
229 {
230 // Construct the 2-segment Bézier path
231 auto const &cp = bezier_handles.pts;
232 Path bezier;
233 bezier.append(BezierCurveN<3>(cp[0].round(), cp[1].round(), cp[2].round(), cp[3].round()));
234 bezier.append(BezierCurveN<3>(cp[3].round(), cp[4].round(), cp[5].round(), cp[6].round()));
235 items[0].setPath(std::move(bezier));
236
237 // Construct the elliptical arcs
238 auto const &ae = elliptical_handles.pts;
239 Path elliptical;
240 elliptical.append(random_arc(ae[0], ae[1]));
241 elliptical.append(random_arc(ae[1], ae[2]));
242 items[1].setPath(std::move(elliptical));
243
244 // Construct a mixed path
245 auto const &mh = mixed_handles.pts;
246 Path mixed;
247 mixed.append(BezierCurveN<3>(mh[0], mh[1], mh[2], mh[3]));
248 mixed.append(random_arc(mh[3], mh[4]));
249 mixed.close();
250 items[2].setPath(std::move(mixed));
251 }
252
253 void draw_impl(cairo_t *cr, int width, int height)
254 {
255 refresh_geometry();
256 write_title(cr);
257
258 auto text_pos = Point(20, height - 20);
259 for (auto const &item : items) {
260 item.draw(cr);
261 item.write(cr, text_pos);
262 text_pos -= Point(0, 20);
263 }
264 }
265
266 void write_title(cairo_t *c)
267 {
268 cairo_move_to(c, 10, 40);
269 cairo_set_font_size(c, 30);
270 set_cairo_rgb(c, 0x0);
271 cairo_show_text(c, "Self-intersection of paths in lib2geom!");
272 cairo_set_font_size(c, 14);
273 cairo_move_to(c, 10, 60);
274 cairo_show_text(c, "[Space]: Print SVG 'd' attributes to stdout");
275 cairo_move_to(c, 10, 80);
276 cairo_show_text(c, "[Ctrl-V]: Paste a 'd' attribute from clipboard");
277 }
278
279 void draw_error(cairo_t *cr, int width, int height)
280 {
281 auto center = Point(0.5 * (double)width, 0.5 * (double)height);
282 cairo_move_to(cr, center + Point(-90, -100));
283 cairo_line_to(cr, center + Point(100, 90));
284 cairo_line_to(cr, center + Point(90, 100));
285 cairo_line_to(cr, center + Point(-100, -90));
286 cairo_close_path(cr);
287 cairo_set_source_rgb(cr, 1, 0, 0);
288 cairo_fill(cr);
289
290 cairo_move_to(cr, center + Point(90, -100));
291 cairo_line_to(cr, center + Point(100, -90));
292 cairo_line_to(cr, center + Point(-90, 100));
293 cairo_line_to(cr, center + Point(-100, 90));
294 cairo_close_path(cr);
295 cairo_set_source_rgb(cr, 1, 0, 0);
296 cairo_fill(cr);
297
298 cairo_move_to(cr, center + Point(-90, 120));
299 cairo_show_text(cr, "Sorry, your toy has just broken :-/");
300 cairo_move_to(cr, Point(10, center[Y] + 150));
301 cairo_show_text(cr, error.c_str());
302 }
303};
304
305int main(int argc, char **argv)
306{
307 auto toy = AutoCross();
308 init(argc, argv, &toy, 800, 800);
309 return 0;
310}
311
312/*
313 Local Variables:
314 mode:c++
315 c-file-style:"stroustrup"
316 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
317 indent-tabs-mode:nil
318 fill-column:99
319 End:
320 */
321// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Path - a sequence of contiguous curves.
Color const BROWN
static void write_text(cairo_t *c, const char *text, Point const &position, Color color)
Color const GREEN
Color const BLACK
Color const RED
static std::string format_point(Point const &pt)
static void set_cairo_rgb(cairo_t *c, Color rgb)
Color const BLUE
uint32_t Color
static EllipticalArc random_arc(Point from, Point to)
int main()
Bezier curve with compile-time specified order.
Two-dimensional Bezier curve of arbitrary order.
Elliptical arc curve.
Base exception class, all 2geom exceptions should be derived from this one.
Definition exception.h:53
const char * what() const noexcept override
Definition exception.h:63
Sequence of subpaths.
Definition pathvector.h:122
bool empty() const
Check whether the vector contains any paths.
Definition pathvector.h:145
Sequence of contiguous curves, aka spline.
Definition path.h:353
void close(bool closed=true)
Set whether the path is closed.
Definition path.cpp:322
void append(Curve *curve)
Add a new curve to the end of the path.
Definition path.h:750
Two-dimensional point that doubles as a vector.
Definition point.h:66
std::vector< Geom::Point > pts
Inkscape::XML::Node * write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, unsigned int flags) override
Definition sp-item.cpp:850
vector< Handle * > handles
virtual void save(FILE *f)
virtual void key_hit(unsigned keyval, unsigned modifiers)
virtual void draw(cairo_t *cr, std::ostringstream *notify, int w, int h, bool save, std::ostringstream *timing_stream)
void draw(cairo_t *cr, xAx C, Rect bnd)
Definition conic-5.cpp:63
double c[8][4]
Elliptical arc curve.
void parse_svg_path(char const *str, PathSink &sink)
Feed SVG path data to the specified sink.
@ Y
Definition coord.h:48
SPItem * item
Various utility functions.
Definition affine.h:22
Angle distance(Angle const &a, Angle const &b)
Definition angle.h:163
double atan2(Point const &p)
void cairo_line_to(cairo_t *cr, Geom::Point p1)
void draw_cross(cairo_t *cr, Geom::Point h)
struct _cairo cairo_t
Definition path-cairo.h:16
void cairo_path(cairo_t *cr, Geom::Path const &p)
void cairo_move_to(cairo_t *cr, Geom::Point p1)
RGB rgb
Definition quantize.cpp:36
GList * items
char const * what() const noexcept override
Definition exception.h:139
Definition curve.h:24
parse SVG path specifications
double height
double width
void cairo_set_source_rgba(cairo_t *cr, colour c)
void get_clipboard_text(std::function< void(char const *)> &&on_completion)
void redraw()
void init(int argc, char **argv, Toy *t, int width=600, int height=600)