1 #include <2geom/d2.h>
2 #include <2geom/intersection-graph.h>
3 #include <2geom/path.h>
4 #include <2geom/sbasis.h>
5 #include <2geom/svg-path-parser.h>
6 #include <2geom/transforms.h>
7
8 #include <toys/path-cairo.h>
9 #include <toys/toy-framework-2.h>
10
11 #include <algorithm>
12 #include <cstdlib>
13
14 using namespace Geom;
15
16 class BoolOps : public Toy {
17 PathVector as, bs;
18 Line ah, bh;
19 PointHandle path_handles[4];
20 std::vector<Toggle> togs;
21 bool path_handles_inited;
22
23 public:
BoolOps()24 BoolOps()
25 : path_handles_inited(false)
26 {}
27
draw(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream * timer_stream)28 void draw(cairo_t *cr, std::ostringstream *notify, int width, int height, bool save, std::ostringstream *timer_stream) override {
29 if (!path_handles_inited) {
30 Rect vp(Point(10,10), Point(width-10, height-10));
31 setup_path_handles(vp);
32 }
33
34 Line aht(path_handles[0].pos, path_handles[1].pos);
35 Line bht(path_handles[2].pos, path_handles[3].pos);
36
37 PathVector ast = as * ah.transformTo(aht);
38 PathVector bst = bs * bh.transformTo(bht);
39
40 Timer tm;
41 tm.start();
42
43 PathIntersectionGraph pig(ast, bst);
44 std::vector<Point> dix, ix, wpoints;
45 ix = pig.intersectionPoints();
46 dix = pig.intersectionPoints(true);
47 wpoints = pig.windingPoints();
48 PathVector result, f_in, f_out;
49
50 if (pig.valid()) {
51 if (togs[0].on && !togs[1].on && !togs[2].on) {
52 result = pig.getAminusB();
53 }
54 if (!togs[0].on && togs[1].on && !togs[2].on) {
55 result = pig.getIntersection();
56 }
57 if (!togs[0].on && !togs[1].on && togs[2].on) {
58 result = pig.getBminusA();
59 }
60 if (togs[0].on && togs[1].on && !togs[2].on) {
61 result = ast;
62 }
63 if (togs[0].on && !togs[1].on && togs[2].on) {
64 result = pig.getXOR();
65 }
66 if (!togs[0].on && togs[1].on && togs[2].on) {
67 result = bst;
68 }
69 if (togs[0].on && togs[1].on && togs[2].on) {
70 result = pig.getUnion();
71 }
72 }
73
74 if (togs[5].on || togs[6].on) {
75 pig.fragments(f_in, f_out);
76 }
77 Timer::Time boolop_time = tm.lap();
78
79 cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
80 cairo_set_line_join(cr, CAIRO_LINE_JOIN_BEVEL);
81 cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
82
83 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
84 cairo_path(cr, result);
85 cairo_fill(cr);
86
87 cairo_set_line_width(cr, 1);
88 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
89 cairo_path(cr, ast);
90
91 cairo_stroke(cr);
92 cairo_set_source_rgb(cr, 0, 0, 0);
93 cairo_path(cr, bst);
94 cairo_stroke(cr);
95
96 if (togs[5].on) {
97 cairo_set_source_rgb(cr, 1, 0, 0);
98 cairo_path(cr, f_in);
99 cairo_stroke(cr);
100 }
101 if (togs[6].on) {
102 cairo_set_source_rgb(cr, 0, 0, 1);
103 cairo_path(cr, f_out);
104 cairo_stroke(cr);
105 }
106
107 //cairo_set_line_width(cr, 1);
108
109 if (togs[7].on) {
110 cairo_set_source_rgb(cr, 0, 1, 1);
111 for (auto & wpoint : wpoints) {
112 draw_handle(cr, wpoint);
113 }
114 cairo_stroke(cr);
115 }
116
117 if (togs[3].on) {
118 cairo_set_source_rgb(cr, 0, 1, 0);
119 for (auto & i : ix) {
120 draw_handle(cr, i);
121 }
122 cairo_stroke(cr);
123 }
124
125 if (togs[4].on) {
126 cairo_set_source_rgb(cr, 1, 0, 0);
127 for (auto & i : dix) {
128 draw_handle(cr, i);
129 }
130 cairo_stroke(cr);
131 }
132
133
134 double x = width - 90, y = height - 40, y2 = height - 80;
135 Point p(x, y), p2(x, y2), dpoint(25,25), xo(25,0);
136 togs[0].bounds = Rect(p, p + dpoint);
137 togs[1].bounds = Rect(p + xo, p + xo + dpoint);
138 togs[2].bounds = Rect(p + 2*xo, p + 2*xo + dpoint);
139
140 togs[3].bounds = Rect(p2 - 2*xo, p2 - 2*xo + dpoint);
141 togs[4].bounds = Rect(p2 - xo, p2 - xo + dpoint);
142 togs[5].bounds = Rect(p2, p2 + dpoint);
143 togs[6].bounds = Rect(p2 + xo, p2 + xo + dpoint);
144 togs[7].bounds = Rect(p2 + 2*xo, p2 + 2*xo + dpoint);
145 draw_toggles(cr, togs);
146
147 *notify << ix.size() << " intersections";
148 if (dix.size() != 0) {
149 *notify << " + " << dix.size() << " defective";
150 }
151 if (pig.valid()) {
152 *notify << "\nboolop time: " << boolop_time << std::endl;
153 } else {
154 *notify << "\nboolop failed, time: " << boolop_time << std::endl;
155 }
156 Toy::draw(cr, notify, width, height, save,timer_stream);
157 }
158
mouse_pressed(GdkEventButton * e)159 void mouse_pressed(GdkEventButton* e) override {
160 toggle_events(togs, e);
161 Toy::mouse_pressed(e);
162 }
163
first_time(int argc,char ** argv)164 void first_time(int argc, char** argv) override {
165 const char *path_a_name="svgd/winding.svgd";
166 const char *path_b_name="svgd/star.svgd";
167 if(argc > 1)
168 path_a_name = argv[1];
169 if(argc > 2)
170 path_b_name = argv[2];
171
172 as = read_svgd(path_a_name);
173 bs = read_svgd(path_b_name);
174
175 OptRect abox = as.boundsExact();
176 OptRect bbox = bs.boundsExact();
177
178 if (!abox) {
179 std::clog << "Error: path A is empty" << std::endl;
180 }
181 if (!bbox) {
182 std::clog << "Error: path B is empty" << std::endl;
183 }
184 if (!abox || !bbox) {
185 std::exit(1);
186 }
187
188 std::vector<Point> anodes = as.nodes();
189 std::vector<Point> bnodes = bs.nodes();
190
191 typedef std::vector<Point>::iterator Iter;
192 std::pair<Iter, Iter> apts =
193 std::minmax_element(anodes.begin(), anodes.end(), Point::LexLess<Y>());
194 std::pair<Iter, Iter> bpts =
195 std::minmax_element(bnodes.begin(), bnodes.end(), Point::LexLess<Y>());
196
197 ah = Line(*apts.first, *apts.second);
198 bh = Line(*bpts.first, *bpts.second);
199
200 togs.emplace_back("R", true);
201 togs.emplace_back("&", false);
202 togs.emplace_back("B", false);
203
204 togs.emplace_back("X", true);
205 togs.emplace_back("D", true);
206 togs.emplace_back("I", false);
207 togs.emplace_back("O", false);
208 togs.emplace_back("W", false);
209 }
210
setup_path_handles(Rect const & viewport)211 void setup_path_handles(Rect const &viewport) {
212 Line aht = ah * as.boundsExact()->transformTo(viewport, Aspect(ALIGN_XMID_YMID));
213 Line bht = bh * bs.boundsExact()->transformTo(viewport, Aspect(ALIGN_XMID_YMID));
214
215 path_handles[0] = PointHandle(aht.initialPoint());
216 path_handles[1] = PointHandle(aht.finalPoint());
217 path_handles[2] = PointHandle(bht.initialPoint());
218 path_handles[3] = PointHandle(bht.finalPoint());
219
220 for (auto & path_handle : path_handles) {
221 handles.push_back(&path_handle);
222 }
223 path_handles_inited = true;
224 }
225 //virtual bool should_draw_numbers() {return false;}
226 };
227
main(int argc,char ** argv)228 int main(int argc, char **argv) {
229 init(argc, argv, new BoolOps());
230 return 0;
231 }
232
233 /*
234 Local Variables:
235 mode:c++
236 c-file-style:"stroustrup"
237 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
238 indent-tabs-mode:nil
239 fill-column:99
240 End:
241 */
242 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=4:softtabstop=4:fileencoding=utf-8:textwidth=99 :
243