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