1 /*
2  * Diffeomorphism-based intersector: given two curves
3  *  M(t)=(x(t),y(t)) and N(u)=(X(u),Y(u))
4  * and supposing M is a graph over the x-axis, we compute y(x) and solve
5  *  Y(u) - y(X(u)) = 0
6  * to get the intersections of the two curves...
7  *
8  * Notice the result can be far from intuitive because of the choice we have
9  * to make to consider a curve as a graph over x or y. For instance the two
10  * branches of xy=eps are never close from this point of view (!)...
11  *
12  * Authors:
13  * 		J.-F. Barraud    <jfbarraud at gmail.com>
14  * Copyright 2010  authors
15  *
16  * This library is free software; you can redistribute it and/or
17  * modify it either under the terms of the GNU Lesser General Public
18  * License version 2.1 as published by the Free Software Foundation
19  * (the "LGPL") or, at your option, under the terms of the Mozilla
20  * Public License Version 1.1 (the "MPL"). If you do not alter this
21  * notice, a recipient may use your version of this file under either
22  * the MPL or the LGPL.
23  *
24  * You should have received a copy of the LGPL along with this library
25  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27  * You should have received a copy of the MPL along with this library
28  * in the file COPYING-MPL-1.1
29  *
30  * The contents of this file are subject to the Mozilla Public License
31  * Version 1.1 (the "License"); you may not use this file except in
32  * compliance with the License. You may obtain a copy of the License at
33  * http://www.mozilla.org/MPL/
34  *
35  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
36  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
37  * the specific language governing rights and limitations.
38  */
39 
40 
41 #include <2geom/d2.h>
42 #include <2geom/sbasis.h>
43 #include <2geom/path.h>
44 #include <2geom/bezier-to-sbasis.h>
45 #include <2geom/sbasis-geometric.h>
46 #include <toys/path-cairo.h>
47 #include <toys/toy-framework-2.h>
48 
49 #include <cstdlib>
50 #include <cstdio>
51 #include <set>
52 #include <vector>
53 #include <algorithm>
54 
55 #include <2geom/orphan-code/intersection-by-smashing.h>
56 #include "../2geom/orphan-code/intersection-by-smashing.cpp"
57 
58 using namespace Geom;
59 
60 #define VERBOSE 0
61 
exp_rescale(double x)62 static double exp_rescale(double x){ return ::pow(10, x);}
exp_formatter(double x)63 std::string exp_formatter(double x){ return default_formatter(exp_rescale(x));}
64 
65 
66 
67 #if 0
68 //useless here;
69 Piecewise<D2<SBasis> > linearizeCusps( D2<SBasis> f, double tol){
70 	D2<SBasis> df = derivative( f );
71 	std::vector<Interval> xdoms = level_set( df[X], 0., tol);
72 	std::vector<Interval> ydoms = level_set( df[Y], 0., tol);
73 	std::vector<Interval> doms;
74 	//TODO: use order!!
75 	for ( unsigned i=0; i<xdoms.size(); i++ ){
76 		OptInterval inter = xdoms[i];
77 		for ( unsigned j=0; j<ydoms.size(); j++ ){
78 			inter &= ydoms[j];
79 		}
80 		if (inter) {
81 			doms.push_back( *inter );
82 		}
83 	}
84 	Piecewise<D2<SBasis> > result;
85 	if (doms.size() == 0 ) return Piecewise<D2<SBasis> >(f);
86 	if (doms[0].min() > 0 ){
87 		result.cuts.push_back( 0 );
88 		result.cuts.push_back( doms[0].min() );
89 		result.segs.push_back( portion( f, Interval( 0, doms[0].min() ) ) );
90 	}
91 	for ( unsigned i=0; i<doms.size(); i++ ){
92 		Point a = result.segs.back().at1();
93 		Point b = f.valueAt( doms[i].middle() );
94 		Point c = f.valueAt( doms[i].max() );
95 		result.cuts.push_back( doms[i].middle() );
96 		result.segs.push_back( D2<SBasis>( Linear( a[X], b[X] ), Linear( a[Y], b[Y] ) ) );
97 		result.cuts.push_back( doms[i].max() );
98 		result.segs.push_back( D2<SBasis>( Linear( b[X], c[X] ), Linear( b[Y], c[Y] ) ) );
99 		double t = ( i+1 == doms.size() )? 1 : doms[i+1].min();
100 		result.cuts.push_back( t );
101 		result.segs.push_back( portion( f, Interval( doms[i].max(), t ) ) );
102 	}
103 	return result;
104 }
105 #endif
106 
107 #if 0
108 /* Computes the intersection of two sets given as (ordered) union intervals.
109  */
110 std::vector<Interval> intersect( std::vector<Interval> const &a, std::vector<Interval> const &b){
111 	std::vector<Interval> result;
112 	//TODO: use order!
113 	for (unsigned i=0; i < a.size(); i++){
114 		for (unsigned j=0; j < b.size(); j++){
115 			OptInterval c( a[i] );
116 			c &= b[j];
117 			if ( c ) {
118 				result.push_back( *c );
119 			}
120 		}
121 	}
122 	return result;
123 }
124 
125 /* Computes the top and bottom boundaries of the L_\infty neighborhood
126  * of a curve. The curve is supposed to be a graph over the x-axis.
127  */
128 void computeLinfinityNeighborhood( D2<SBasis > const &f, double tol, D2<Piecewise<SBasis> > &topside, D2<Piecewise<SBasis> > &botside ){
129 	double signx = ( f[X].at0() > f[X].at1() )? -1 : 1;
130 	double signy = ( f[Y].at0() > f[Y].at1() )? -1 : 1;
131 
132 	Piecewise<D2<SBasis> > top, bot;
133 	top = Piecewise<D2<SBasis> > (f);
134 	top.cuts.insert( top.cuts.end(), 2);
135 	top.segs.insert( top.segs.end(), D2<SBasis>(Linear( f[X].at1(), f[X].at1()+2*tol*signx),
136 			                                    Linear( f[Y].at1() )) );
137 	bot = Piecewise<D2<SBasis> >(f);
138 	bot.cuts.insert( bot.cuts.begin(), - 1 );
139 	bot.segs.insert( bot.segs.begin(), D2<SBasis>(Linear( f[X].at0()-2*tol*signx, f[X].at0()),
140 												  Linear( f[Y].at0() )) );
141 	top += Point(-tol*signx,  tol);
142 	bot += Point( tol*signx, -tol);
143 
144 	if ( signy < 0 ){
145 		swap( top, bot );
146 		top += Point( 0,  2*tol);
147 		bot += Point( 0, -2*tol);
148 	}
149 	topside = make_cuts_independent(top);
150 	botside = make_cuts_independent(bot);
151 }
152 
153 
154 /*Compute top and bottom boundaries of the L^infty nbhd of the graph of a *monotonic* function f.
155  * if f is increasing, it is given by [f(t-tol)-tol, f(t+tol)+tol].
156  * if not, it is [f(t+tol)-tol, f(t-tol)+tol].
157  */
158 void computeLinfinityNeighborhood( Piecewise<SBasis> const &f, double tol, Piecewise<SBasis> &top, Piecewise<SBasis> &bot){
159 	top = f + tol;
160 	top.offsetDomain( - tol );
161 	top.cuts.insert( top.cuts.end(), f.domain().max() + tol);
162 	top.segs.insert( top.segs.end(), SBasis(Linear( f.lastValue() + tol )) );
163 
164 	bot = f - tol;
165 	bot.offsetDomain( tol );
166 	bot.cuts.insert( bot.cuts.begin(), f.domain().min() - tol);
167 	bot.segs.insert( bot.segs.begin(), SBasis(Linear( f.firstValue() - tol )) );
168 
169 	if ( f.firstValue() > f.lastValue() ){
170 	swap( top, bot );
171 	top += 2*tol;
172 	bot -= 2*tol;
173 	}
174 }
175 
176 std::vector<Interval> level_set( D2<SBasis> const &f, Rect region){
177 	std::vector<Interval> x_in_reg = level_set( f[X], region[X] );
178 	std::vector<Interval> y_in_reg = level_set( f[Y], region[Y] );
179 	std::vector<Interval> result = intersect ( x_in_reg, y_in_reg );
180 	return result;
181 }
182 
183 void prolongateByConstants( Piecewise<SBasis> &f, double paddle_width ){
184 	if ( f.size() == 0 ) return; //do we have a covention about the domain of empty pwsb?
185 	f.cuts.insert( f.cuts.begin(), f.cuts.front() - paddle_width );
186 	f.segs.insert( f.segs.begin(), SBasis( f.segs.front().at0() ) );
187 	f.cuts.insert( f.cuts.end(), f.cuts.back() + paddle_width );
188 	f.segs.insert( f.segs.end(), SBasis( f.segs.back().at1() ) );
189 }
190 
191 
192 
193 /* Returns the intervals over which the curve keeps its slope
194  * in one of the 8 sectors delimited by x=0, y=0, y=x, y=-x.
195  * WARNING: both curves are supposed to be a graphs over x or y axis,
196  *     and the smaller the slopes the better (typically <=45°).
197  */
198 std::vector<std::pair<Interval, Interval> > smash_intersect( D2<SBasis> const &a, D2<SBasis> const &b,
199 			double tol, cairo_t *cr , bool draw_more_stuff=false ){
200 
201 	std::vector<std::pair<Interval, Interval> > res;
202 
203 	// a and b or X and Y may have to be exchanged, so make local copies.
204 	D2<SBasis> aa = a;
205 	D2<SBasis> bb = b;
206 	bool swapresult = false;
207 	bool swapcoord = false;//debug only!
208 
209 	if ( draw_more_stuff ){
210 		cairo_set_line_width (cr, 3);
211 		cairo_set_source_rgba(cr, .5, .9, .7, 1 );
212 		cairo_d2_sb(cr, aa);
213 		cairo_d2_sb(cr, bb);
214 		cairo_stroke(cr);
215 	}
216 
217 #if 1
218 	//if the (enlarged) bounding boxes don't intersect, stop.
219 	if ( !draw_more_stuff ){
220 		OptRect abounds = bounds_fast( a );
221 		OptRect bbounds = bounds_fast( b );
222 		if ( !abounds || !bbounds ) return res;
223 		abounds->expandBy(tol);
224 		if ( !(abounds->intersects(*bbounds))){
225 			return res;
226 		}
227 	}
228 #endif
229 
230 	//Choose the best curve to be re-parametrized by x or y values.
231 	OptRect dabounds = bounds_exact(derivative(a));
232 	OptRect dbbounds = bounds_exact(derivative(b));
233 	if	( dbbounds->min().length() > dabounds->min().length() ){
234 		aa=b;
235 		bb=a;
236 		swap( dabounds, dbbounds );
237 		swapresult = true;
238 	}
239 
240 	//Choose the best coordinate to use as new parameter
241 	double dxmin = std::min( abs((*dabounds)[X].max()), abs((*dabounds)[X].min()) );
242 	double dymin = std::min( abs((*dabounds)[Y].max()), abs((*dabounds)[Y].min()) );
243 	if ( (*dabounds)[X].max()*(*dabounds)[X].min() < 0 ) dxmin=0;
244 	if ( (*dabounds)[Y].max()*(*dabounds)[Y].min() < 0 ) dymin=0;
245 	assert (dxmin>=0 && dymin>=0);
246 
247 	if (dxmin < dymin) {
248 		aa = D2<SBasis>( aa[Y], aa[X] );
249 		bb = D2<SBasis>( bb[Y], bb[X] );
250 		swapcoord = true;
251 	}
252 
253 	//re-parametrize aa by the value of x.
254 	Interval x_range_strict( aa[X].at0(), aa[X].at1() );
255 	Piecewise<SBasis> y_of_x = pw_compose_inverse(aa[Y],aa[X], 2, 1e-5);
256 
257 	//Compute top and bottom boundaries of the L^infty nbhd of aa.
258 	Piecewise<SBasis> top_ay, bot_ay;
259 	computeLinfinityNeighborhood( y_of_x, tol, top_ay, bot_ay);
260 
261 	Interval ax_range = top_ay.domain();//i.e. aa[X] domain ewpanded by tol.
262 
263 	if ( draw_more_stuff ){
264 		Piecewise<SBasis> dbg_x( SBasis( Linear( top_ay.domain().min(), top_ay.domain().max() ) ) );
265 		dbg_x.setDomain( top_ay.domain() );
266 		D2<Piecewise<SBasis> > dbg_side ( Piecewise<SBasis>( SBasis( Linear(   0    ) ) ),
267 				                          Piecewise<SBasis>( SBasis( Linear( 0, 2*tol) ) ) );
268 
269 		D2<Piecewise<SBasis> > dbg_rgn;
270 		unsigned h = ( swapcoord ) ? Y : X;
271 		dbg_rgn[h].concat ( dbg_x );
272 		dbg_rgn[h].concat ( dbg_side[X] + dbg_x.lastValue() );
273 		dbg_rgn[h].concat ( reverse(dbg_x) );
274 		dbg_rgn[h].concat ( dbg_side[X] + dbg_x.firstValue() );
275 
276 		dbg_rgn[1-h].concat ( bot_ay );
277 		dbg_rgn[1-h].concat ( dbg_side[Y] + bot_ay.lastValue() );
278 		dbg_rgn[1-h].concat ( reverse(top_ay) );
279 		dbg_rgn[1-h].concat ( reverse( dbg_side[Y] ) + bot_ay.firstValue() );
280 
281 		cairo_set_line_width (cr, 1.);
282 		cairo_set_source_rgba(cr, 0., 1., 0., .75 );
283 		cairo_d2_pw_sb(cr, dbg_rgn );
284 		cairo_stroke(cr);
285 
286 		D2<SBasis> bbb = bb;
287 		if ( swapcoord ) swap( bbb[X], bbb[Y] );
288 		//Piecewise<D2<SBasis> > dbg_rgnB = neighborhood( bbb, tol );
289 		D2<Piecewise<SBasis> > dbg_topB, dbg_botB;
290 		computeLinfinityNeighborhood( bbb, tol, dbg_topB, dbg_botB );
291 		cairo_set_line_width (cr, 1.);
292 		cairo_set_source_rgba(cr, .2, 8., .2, .4 );
293 //		cairo_pw_d2_sb(cr, dbg_rgnB );
294 		cairo_d2_pw_sb(cr, dbg_topB );
295 		cairo_d2_pw_sb(cr, dbg_botB );
296 		cairo_stroke(cr);
297 	}
298 
299 	std::vector<Interval> bx_in_ax_range = level_set(bb[X], ax_range );
300 
301 	// find times when bb is in the neighborhood of aa.
302 	std::vector<Interval> tbs;
303 	for (unsigned i=0; i<bx_in_ax_range.size(); i++){
304 		D2<Piecewise<SBasis> > bb_in;
305 		bb_in[X] = Piecewise<SBasis> ( portion( bb[X], bx_in_ax_range[i] ) );
306 		bb_in[Y] = Piecewise<SBasis> ( portion( bb[Y], bx_in_ax_range[i]) );
307 		bb_in[X].setDomain( bx_in_ax_range[i] );
308 		bb_in[Y].setDomain( bx_in_ax_range[i] );
309 
310 		Piecewise<SBasis> h;
311 		Interval level;
312 		h = bb_in[Y] - compose( top_ay, bb_in[X] );
313 		level = Interval( -infinity(), 0 );
314 		std::vector<Interval> rts_lo = level_set( h, level);
315 		h = bb_in[Y] - compose( bot_ay, bb_in[X] );
316 		level = Interval( 0, infinity());
317 		std::vector<Interval> rts_hi = level_set( h, level);
318 
319 		std::vector<Interval> rts = intersect( rts_lo, rts_hi );
320 		tbs.insert(tbs.end(), rts.begin(),  rts.end()  );
321 	}
322 
323 	std::vector<std::pair<Interval, Interval> > result(tbs.size(),std::pair<Interval,Interval>());
324 
325 	/* for each solution I, find times when aa is in the neighborhood of bb(I).
326 	 * (Note: the preimage of bb[X](I) by aa[X], enlarged by tol, is a good approximation of this:
327 	 * it would give points in the 2*tol neighborhood of bb (if the slope of aa is never more than 1).
328 	 *  + faster computation.
329 	 *  - implies little jumps depending on the subdivision of the input curve into monotonic pieces
330 	 *  and on the choice of preferred axis. If noticeable, these jumps would feel random to the user :-(
331 	 */
332 	for (unsigned j=0; j<tbs.size(); j++){
333 		result[j].second = tbs[j];
334 		std::vector<Interval> tas;
335 		Piecewise<SBasis> fat_y_of_x = y_of_x;
336 		prolongateByConstants( fat_y_of_x, 100*(1+tol) );
337 
338 		D2<Piecewise<SBasis> > top_b, bot_b;
339 		D2<SBasis> bbj = portion( bb, tbs[j] );
340 		computeLinfinityNeighborhood( bbj, tol, top_b, bot_b );
341 
342 		Piecewise<SBasis> h;
343 		Interval level;
344 		h = top_b[Y] - compose( fat_y_of_x, top_b[X] );
345 		level = Interval( +infinity(), 0 );
346 		std::vector<Interval> rts_top = level_set( h, level);
347 		for (unsigned idx=0; idx < rts_top.size(); idx++){
348 			rts_top[idx] = Interval( top_b[X].valueAt( rts_top[idx].min() ),
349 									 top_b[X].valueAt( rts_top[idx].max() ) );
350 		}
351 		assert( rts_top.size() == 1 );
352 
353 		h = bot_b[Y] - compose( fat_y_of_x, bot_b[X] );
354 		level = Interval( 0, -infinity());
355 		std::vector<Interval> rts_bot = level_set( h, level);
356 		for (unsigned idx=0; idx < rts_bot.size(); idx++){
357 			rts_bot[idx] = Interval( bot_b[X].valueAt( rts_bot[idx].min() ),
358 									 bot_b[X].valueAt( rts_bot[idx].max() ) );
359 		}
360 		assert( rts_bot.size() == 1 );
361 
362 #if VERBOSE
363 		printf("range(aa[X]) = [%f, %f];\n", y_of_x.domain().min(), y_of_x.domain().max());
364 		printf("range(bbj[X]) = [%f, %f]; tol= %f\n", bbj[X].at0(), bbj[X].at1(), tol);
365 
366 		printf("rts_top = ");
367 		for (unsigned dbgi=0; dbgi<rts_top.size(); dbgi++){
368 			printf("[%f,%f]U", rts_top[dbgi].min(), rts_top[dbgi].max() );
369 		}
370 		printf("\n");
371 		printf("rts_bot = ");
372 		for (unsigned dbgi=0; dbgi<rts_bot.size(); dbgi++){
373 			printf("[%f,%f]U", rts_bot[dbgi].min(), rts_bot[dbgi].max() );
374 		}
375 		printf("\n");
376 #endif
377 		rts_top = intersect( rts_top, rts_bot );
378 #if VERBOSE
379 		printf("intersection = ");
380 		for (unsigned dbgi=0; dbgi<rts_top.size(); dbgi++){
381 			printf("[%f,%f]U", rts_top[dbgi].min(), rts_top[dbgi].max() );
382 		}
383 		printf("\n\n");
384 
385 		if (rts_top.size() != 1){
386 			printf("!!!!!!!!!!!!!!!!!!!!!!\n!!!!!!!!!!!!!!!!!!!!!!\n");
387 			rts_top[0].unionWith( rts_top[1] );
388 			assert( false );
389 		}
390 #endif
391 		assert (rts_top.size() == 1);
392 		Interval x_dom = rts_top[0];
393 
394 		if ( x_dom.max() <= x_range_strict.min() ){
395 			tas.push_back( Interval ( ( aa[X].at0() < aa[X].at1() ) ? 0 : 1 ) );
396 		}else if ( x_dom.min() >= x_range_strict.max() ){
397 			tas.push_back( Interval ( ( aa[X].at0() < aa[X].at1() ) ? 1 : 0 ) );
398 		}else{
399 			tas = level_set(aa[X], x_dom );
400 		}
401 
402 #if VERBOSE
403 		if ( tas.size() != 1 ){
404 			printf("Error: preimage of [%f, %f] by x:[0,1]->[%f, %f] is ",
405 					x_dom.min(), x_dom.max(), x_range_strict.min(), x_range_strict.max());
406 			if ( tas.size() == 0 ){
407 				printf( "empty.\n");
408 			}else{
409 				printf("\n   [%f,%f]", tas[0].min(), tas[0].max() );
410 				for (unsigned toto=1; toto<tas.size(); toto++){
411 					printf(" U [%f,%f]", tas[toto].min(), tas[toto].max() );
412 				}
413 			}
414 		}
415 #endif
416 		assert( tas.size()==1 );
417 		result[j].first = tas.front();
418 	}
419 
420 	if (swapresult) {
421 		for ( unsigned i=0; i<result.size(); i++){
422 			Interval temp = result[i].first;
423 			result[i].first = result[i].second;
424 			result[i].second = temp;
425 		}
426 	}
427 	return result;
428 }
429 
430 #endif
431 
432 class Intersector : public Toy
433 {
434   private:
draw(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream * timer_stream)435 	void draw( cairo_t *cr,	std::ostringstream *notify,
436     		   int width, int height, bool save, std::ostringstream *timer_stream)
437     {
438         double tol = exp_rescale(slider.value());
439     	D2<SBasis> A = handles_to_sbasis(psh.pts.begin(), A_bez_ord-1);
440         D2<SBasis> B = handles_to_sbasis(psh.pts.begin()+A_bez_ord, B_bez_ord-1);
441     	cairo_set_line_width (cr, .8);
442     	cairo_set_source_rgba(cr,0.,0.,0.,.6);
443         cairo_d2_sb(cr, A);
444         cairo_d2_sb(cr, B);
445     	cairo_stroke(cr);
446 
447     	Rect tolbytol( anchor.pos, anchor.pos );
448     	tolbytol.expandBy( tol );
449         cairo_rectangle(cr, tolbytol);
450     	cairo_stroke(cr);
451 /*
452 		Piecewise<D2<SBasis> > smthA = linearizeCusps(A+Point(0,10), tol);
453         cairo_set_line_width (cr, 1.);
454         cairo_set_source_rgba(cr, 1., 0., 1., 1. );
455         cairo_pw_d2_sb(cr, smthA);
456     	cairo_stroke(cr);
457 */
458 
459         std::vector<Interval> Acuts = monotonicSplit(A);
460         std::vector<Interval> Bcuts = monotonicSplit(B);
461 
462 #if 0
463         for (unsigned i=0; i<Acuts.size(); i++){
464         	D2<SBasis> Ai = portion( A, Acuts[i]);
465             cairo_set_line_width (cr, .2);
466             cairo_set_source_rgba(cr, 0., 0., 0., 1. );
467             draw_cross(cr, Ai.at0());
468         	cairo_stroke(cr);
469         	for (unsigned j=0; j<Bcuts.size(); j++){
470             	std::vector<std::pair<Interval, Interval> > my_intersections;
471             	D2<SBasis> Bj = portion( B, Bcuts[j]);
472                 cairo_set_line_width (cr, .2);
473                 cairo_set_source_rgba(cr, 0., 0., 0., 1. );
474                 draw_cross(cr, Bj.at0());
475             	cairo_stroke(cr);
476         	}
477         }
478 #endif
479 
480 		std::vector<SmashIntersection> my_intersections;
481      	my_intersections = smash_intersect( A, B, tol );
482 
483      	for (unsigned k=0; k<my_intersections.size(); k++){
484      		cairo_set_line_width (cr, 2.5);
485             cairo_set_source_rgba(cr, 1., 0., 0., .8 );
486             cairo_d2_sb(cr, portion( A, my_intersections[k].times[X]));
487         	cairo_stroke(cr);
488             cairo_set_line_width (cr, 2.5);
489             cairo_set_source_rgba(cr, 0., 0., 1., .8 );
490             cairo_d2_sb(cr, portion( B, my_intersections[k].times[Y]));
491         	cairo_stroke(cr);
492      	}
493 #if 0
494 
495         unsigned apiece( slidera.value()/100. * Acuts.size() );
496         unsigned bpiece( sliderb.value()/100. * Bcuts.size() );
497 
498 
499         for (unsigned i=0; i<Acuts.size(); i++){
500         	D2<SBasis> Ai = portion( A, Acuts[i]);
501         	for (unsigned j=0; j<Bcuts.size(); j++){
502             	if ( toggle.on &&  (i != apiece || j != bpiece) ) continue;
503 
504         		std::vector<SmashIntersection> my_intersections;
505             	D2<SBasis> Bj = portion( B, Bcuts[j]);
506             	bool draw_more = toggle.on &&  i == apiece && j == bpiece;
507 //             	my_intersections = smash_intersect( Ai, Bj, tol, cr, draw_more );
508              	my_intersections = monotonic_smash_intersect( Ai, Bj, tol );
509 
510              	for (unsigned k=0; k<my_intersections.size(); k++){
511              		cairo_set_line_width (cr, 2.5);
512                     cairo_set_source_rgba(cr, 1., 0., 0., .8 );
513                     cairo_d2_sb(cr, portion( Ai, my_intersections[k].times[X]));
514                 	cairo_stroke(cr);
515                     cairo_set_line_width (cr, 2.5);
516                     cairo_set_source_rgba(cr, 0., 0., 1., .8 );
517                     cairo_d2_sb(cr, portion( Bj, my_intersections[k].times[Y]));
518                 	cairo_stroke(cr);
519              	}
520             }
521         }
522 #endif
523         Toy::draw(cr, notify, width, height, save,timer_stream);
524     }
525 
526   public:
Intersector(unsigned int _A_bez_ord,unsigned int _B_bez_ord)527     Intersector(unsigned int _A_bez_ord, unsigned int _B_bez_ord)
528 		: A_bez_ord(_A_bez_ord), B_bez_ord(_B_bez_ord)
529 	{
530     	unsigned int total_handles = A_bez_ord + B_bez_ord;
531 		for ( unsigned int i = 0; i < total_handles; ++i )
532 			psh.push_back(Geom::Point(uniform()*400, uniform()*400));
533 		handles.push_back(&psh);
534 		slider = Slider(-4, 2, 0, 1.2, "tolerance");
535 		slider.geometry(Point(30, 20), 250);
536 		slider.formatter(&exp_formatter);
537 		handles.push_back(&slider);
538 		slidera = Slider(0, 100, 1, 0., "piece on A");
539 		slidera.geometry(Point(300, 50), 250);
540 		handles.push_back(&slidera);
541 		sliderb = Slider(0, 100, 1, 0., "piece on B");
542 		sliderb.geometry(Point(300, 80), 250);
543 		handles.push_back(&sliderb);
544 		toggle = Toggle( Rect(Point(300,10), Point(440,30)), "Piece by piece", false );
545 		handles.push_back(&toggle);
546 		anchor = PointHandle ( Point(100, 100 ) );
547 		handles.push_back(&anchor);
548 	}
549 
550   private:
551 	unsigned int A_bez_ord;
552 	unsigned int B_bez_ord;
553 	PointSetHandle psh;
554 	PointHandle anchor;
555 	Slider slider,slidera,sliderb;
556 	Toggle toggle;
557 };
558 
559 
main(int argc,char ** argv)560 int main(int argc, char **argv)
561 {
562 	unsigned int A_bez_ord=4;
563 	unsigned int B_bez_ord=4;
564     if(argc > 2)
565         sscanf(argv[2], "%d", &B_bez_ord);
566     if(argc > 1)
567         sscanf(argv[1], "%d", &A_bez_ord);
568 
569     init( argc, argv, new Intersector(A_bez_ord, B_bez_ord));
570     return 0;
571 }
572 
573 
574 /*
575   Local Variables:
576   mode:c++
577   c-file-style:"stroustrup"
578   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
579   indent-tabs-mode:nil
580   fill-column:99
581   End:
582 */
583 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
584