summaryrefslogtreecommitdiffstats
path: root/src/toys/smash-intersector.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/toys/smash-intersector.cpp')
-rw-r--r--src/toys/smash-intersector.cpp583
1 files changed, 583 insertions, 0 deletions
diff --git a/src/toys/smash-intersector.cpp b/src/toys/smash-intersector.cpp
new file mode 100644
index 0000000..b01acfb
--- /dev/null
+++ b/src/toys/smash-intersector.cpp
@@ -0,0 +1,583 @@
+/*
+ * Diffeomorphism-based intersector: given two curves
+ * M(t)=(x(t),y(t)) and N(u)=(X(u),Y(u))
+ * and supposing M is a graph over the x-axis, we compute y(x) and solve
+ * Y(u) - y(X(u)) = 0
+ * to get the intersections of the two curves...
+ *
+ * Notice the result can be far from intuitive because of the choice we have
+ * to make to consider a curve as a graph over x or y. For instance the two
+ * branches of xy=eps are never close from this point of view (!)...
+ *
+ * Authors:
+ * J.-F. Barraud <jfbarraud at gmail.com>
+ * Copyright 2010 authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+
+#include <2geom/d2.h>
+#include <2geom/sbasis.h>
+#include <2geom/path.h>
+#include <2geom/bezier-to-sbasis.h>
+#include <2geom/sbasis-geometric.h>
+#include <toys/path-cairo.h>
+#include <toys/toy-framework-2.h>
+
+#include <cstdlib>
+#include <cstdio>
+#include <set>
+#include <vector>
+#include <algorithm>
+
+#include <2geom/orphan-code/intersection-by-smashing.h>
+#include "../2geom/orphan-code/intersection-by-smashing.cpp"
+
+using namespace Geom;
+
+#define VERBOSE 0
+
+static double exp_rescale(double x){ return ::pow(10, x);}
+std::string exp_formatter(double x){ return default_formatter(exp_rescale(x));}
+
+
+
+#if 0
+//useless here;
+Piecewise<D2<SBasis> > linearizeCusps( D2<SBasis> f, double tol){
+ D2<SBasis> df = derivative( f );
+ std::vector<Interval> xdoms = level_set( df[X], 0., tol);
+ std::vector<Interval> ydoms = level_set( df[Y], 0., tol);
+ std::vector<Interval> doms;
+ //TODO: use order!!
+ for ( unsigned i=0; i<xdoms.size(); i++ ){
+ OptInterval inter = xdoms[i];
+ for ( unsigned j=0; j<ydoms.size(); j++ ){
+ inter &= ydoms[j];
+ }
+ if (inter) {
+ doms.push_back( *inter );
+ }
+ }
+ Piecewise<D2<SBasis> > result;
+ if (doms.size() == 0 ) return Piecewise<D2<SBasis> >(f);
+ if (doms[0].min() > 0 ){
+ result.cuts.push_back( 0 );
+ result.cuts.push_back( doms[0].min() );
+ result.segs.push_back( portion( f, Interval( 0, doms[0].min() ) ) );
+ }
+ for ( unsigned i=0; i<doms.size(); i++ ){
+ Point a = result.segs.back().at1();
+ Point b = f.valueAt( doms[i].middle() );
+ Point c = f.valueAt( doms[i].max() );
+ result.cuts.push_back( doms[i].middle() );
+ result.segs.push_back( D2<SBasis>( Linear( a[X], b[X] ), Linear( a[Y], b[Y] ) ) );
+ result.cuts.push_back( doms[i].max() );
+ result.segs.push_back( D2<SBasis>( Linear( b[X], c[X] ), Linear( b[Y], c[Y] ) ) );
+ double t = ( i+1 == doms.size() )? 1 : doms[i+1].min();
+ result.cuts.push_back( t );
+ result.segs.push_back( portion( f, Interval( doms[i].max(), t ) ) );
+ }
+ return result;
+}
+#endif
+
+#if 0
+/* Computes the intersection of two sets given as (ordered) union intervals.
+ */
+std::vector<Interval> intersect( std::vector<Interval> const &a, std::vector<Interval> const &b){
+ std::vector<Interval> result;
+ //TODO: use order!
+ for (unsigned i=0; i < a.size(); i++){
+ for (unsigned j=0; j < b.size(); j++){
+ OptInterval c( a[i] );
+ c &= b[j];
+ if ( c ) {
+ result.push_back( *c );
+ }
+ }
+ }
+ return result;
+}
+
+/* Computes the top and bottom boundaries of the L_\infty neighborhood
+ * of a curve. The curve is supposed to be a graph over the x-axis.
+ */
+void computeLinfinityNeighborhood( D2<SBasis > const &f, double tol, D2<Piecewise<SBasis> > &topside, D2<Piecewise<SBasis> > &botside ){
+ double signx = ( f[X].at0() > f[X].at1() )? -1 : 1;
+ double signy = ( f[Y].at0() > f[Y].at1() )? -1 : 1;
+
+ Piecewise<D2<SBasis> > top, bot;
+ top = Piecewise<D2<SBasis> > (f);
+ top.cuts.insert( top.cuts.end(), 2);
+ top.segs.insert( top.segs.end(), D2<SBasis>(Linear( f[X].at1(), f[X].at1()+2*tol*signx),
+ Linear( f[Y].at1() )) );
+ bot = Piecewise<D2<SBasis> >(f);
+ bot.cuts.insert( bot.cuts.begin(), - 1 );
+ bot.segs.insert( bot.segs.begin(), D2<SBasis>(Linear( f[X].at0()-2*tol*signx, f[X].at0()),
+ Linear( f[Y].at0() )) );
+ top += Point(-tol*signx, tol);
+ bot += Point( tol*signx, -tol);
+
+ if ( signy < 0 ){
+ swap( top, bot );
+ top += Point( 0, 2*tol);
+ bot += Point( 0, -2*tol);
+ }
+ topside = make_cuts_independent(top);
+ botside = make_cuts_independent(bot);
+}
+
+
+/*Compute top and bottom boundaries of the L^infty nbhd of the graph of a *monotonic* function f.
+ * if f is increasing, it is given by [f(t-tol)-tol, f(t+tol)+tol].
+ * if not, it is [f(t+tol)-tol, f(t-tol)+tol].
+ */
+void computeLinfinityNeighborhood( Piecewise<SBasis> const &f, double tol, Piecewise<SBasis> &top, Piecewise<SBasis> &bot){
+ top = f + tol;
+ top.offsetDomain( - tol );
+ top.cuts.insert( top.cuts.end(), f.domain().max() + tol);
+ top.segs.insert( top.segs.end(), SBasis(Linear( f.lastValue() + tol )) );
+
+ bot = f - tol;
+ bot.offsetDomain( tol );
+ bot.cuts.insert( bot.cuts.begin(), f.domain().min() - tol);
+ bot.segs.insert( bot.segs.begin(), SBasis(Linear( f.firstValue() - tol )) );
+
+ if ( f.firstValue() > f.lastValue() ){
+ swap( top, bot );
+ top += 2*tol;
+ bot -= 2*tol;
+ }
+}
+
+std::vector<Interval> level_set( D2<SBasis> const &f, Rect region){
+ std::vector<Interval> x_in_reg = level_set( f[X], region[X] );
+ std::vector<Interval> y_in_reg = level_set( f[Y], region[Y] );
+ std::vector<Interval> result = intersect ( x_in_reg, y_in_reg );
+ return result;
+}
+
+void prolongateByConstants( Piecewise<SBasis> &f, double paddle_width ){
+ if ( f.size() == 0 ) return; //do we have a covention about the domain of empty pwsb?
+ f.cuts.insert( f.cuts.begin(), f.cuts.front() - paddle_width );
+ f.segs.insert( f.segs.begin(), SBasis( f.segs.front().at0() ) );
+ f.cuts.insert( f.cuts.end(), f.cuts.back() + paddle_width );
+ f.segs.insert( f.segs.end(), SBasis( f.segs.back().at1() ) );
+}
+
+
+
+/* Returns the intervals over which the curve keeps its slope
+ * in one of the 8 sectors delimited by x=0, y=0, y=x, y=-x.
+ * WARNING: both curves are supposed to be a graphs over x or y axis,
+ * and the smaller the slopes the better (typically <=45°).
+ */
+std::vector<std::pair<Interval, Interval> > smash_intersect( D2<SBasis> const &a, D2<SBasis> const &b,
+ double tol, cairo_t *cr , bool draw_more_stuff=false ){
+
+ std::vector<std::pair<Interval, Interval> > res;
+
+ // a and b or X and Y may have to be exchanged, so make local copies.
+ D2<SBasis> aa = a;
+ D2<SBasis> bb = b;
+ bool swapresult = false;
+ bool swapcoord = false;//debug only!
+
+ if ( draw_more_stuff ){
+ cairo_set_line_width (cr, 3);
+ cairo_set_source_rgba(cr, .5, .9, .7, 1 );
+ cairo_d2_sb(cr, aa);
+ cairo_d2_sb(cr, bb);
+ cairo_stroke(cr);
+ }
+
+#if 1
+ //if the (enlarged) bounding boxes don't intersect, stop.
+ if ( !draw_more_stuff ){
+ OptRect abounds = bounds_fast( a );
+ OptRect bbounds = bounds_fast( b );
+ if ( !abounds || !bbounds ) return res;
+ abounds->expandBy(tol);
+ if ( !(abounds->intersects(*bbounds))){
+ return res;
+ }
+ }
+#endif
+
+ //Choose the best curve to be re-parametrized by x or y values.
+ OptRect dabounds = bounds_exact(derivative(a));
+ OptRect dbbounds = bounds_exact(derivative(b));
+ if ( dbbounds->min().length() > dabounds->min().length() ){
+ aa=b;
+ bb=a;
+ swap( dabounds, dbbounds );
+ swapresult = true;
+ }
+
+ //Choose the best coordinate to use as new parameter
+ double dxmin = std::min( abs((*dabounds)[X].max()), abs((*dabounds)[X].min()) );
+ double dymin = std::min( abs((*dabounds)[Y].max()), abs((*dabounds)[Y].min()) );
+ if ( (*dabounds)[X].max()*(*dabounds)[X].min() < 0 ) dxmin=0;
+ if ( (*dabounds)[Y].max()*(*dabounds)[Y].min() < 0 ) dymin=0;
+ assert (dxmin>=0 && dymin>=0);
+
+ if (dxmin < dymin) {
+ aa = D2<SBasis>( aa[Y], aa[X] );
+ bb = D2<SBasis>( bb[Y], bb[X] );
+ swapcoord = true;
+ }
+
+ //re-parametrize aa by the value of x.
+ Interval x_range_strict( aa[X].at0(), aa[X].at1() );
+ Piecewise<SBasis> y_of_x = pw_compose_inverse(aa[Y],aa[X], 2, 1e-5);
+
+ //Compute top and bottom boundaries of the L^infty nbhd of aa.
+ Piecewise<SBasis> top_ay, bot_ay;
+ computeLinfinityNeighborhood( y_of_x, tol, top_ay, bot_ay);
+
+ Interval ax_range = top_ay.domain();//i.e. aa[X] domain ewpanded by tol.
+
+ if ( draw_more_stuff ){
+ Piecewise<SBasis> dbg_x( SBasis( Linear( top_ay.domain().min(), top_ay.domain().max() ) ) );
+ dbg_x.setDomain( top_ay.domain() );
+ D2<Piecewise<SBasis> > dbg_side ( Piecewise<SBasis>( SBasis( Linear( 0 ) ) ),
+ Piecewise<SBasis>( SBasis( Linear( 0, 2*tol) ) ) );
+
+ D2<Piecewise<SBasis> > dbg_rgn;
+ unsigned h = ( swapcoord ) ? Y : X;
+ dbg_rgn[h].concat ( dbg_x );
+ dbg_rgn[h].concat ( dbg_side[X] + dbg_x.lastValue() );
+ dbg_rgn[h].concat ( reverse(dbg_x) );
+ dbg_rgn[h].concat ( dbg_side[X] + dbg_x.firstValue() );
+
+ dbg_rgn[1-h].concat ( bot_ay );
+ dbg_rgn[1-h].concat ( dbg_side[Y] + bot_ay.lastValue() );
+ dbg_rgn[1-h].concat ( reverse(top_ay) );
+ dbg_rgn[1-h].concat ( reverse( dbg_side[Y] ) + bot_ay.firstValue() );
+
+ cairo_set_line_width (cr, 1.);
+ cairo_set_source_rgba(cr, 0., 1., 0., .75 );
+ cairo_d2_pw_sb(cr, dbg_rgn );
+ cairo_stroke(cr);
+
+ D2<SBasis> bbb = bb;
+ if ( swapcoord ) swap( bbb[X], bbb[Y] );
+ //Piecewise<D2<SBasis> > dbg_rgnB = neighborhood( bbb, tol );
+ D2<Piecewise<SBasis> > dbg_topB, dbg_botB;
+ computeLinfinityNeighborhood( bbb, tol, dbg_topB, dbg_botB );
+ cairo_set_line_width (cr, 1.);
+ cairo_set_source_rgba(cr, .2, 8., .2, .4 );
+// cairo_pw_d2_sb(cr, dbg_rgnB );
+ cairo_d2_pw_sb(cr, dbg_topB );
+ cairo_d2_pw_sb(cr, dbg_botB );
+ cairo_stroke(cr);
+ }
+
+ std::vector<Interval> bx_in_ax_range = level_set(bb[X], ax_range );
+
+ // find times when bb is in the neighborhood of aa.
+ std::vector<Interval> tbs;
+ for (unsigned i=0; i<bx_in_ax_range.size(); i++){
+ D2<Piecewise<SBasis> > bb_in;
+ bb_in[X] = Piecewise<SBasis> ( portion( bb[X], bx_in_ax_range[i] ) );
+ bb_in[Y] = Piecewise<SBasis> ( portion( bb[Y], bx_in_ax_range[i]) );
+ bb_in[X].setDomain( bx_in_ax_range[i] );
+ bb_in[Y].setDomain( bx_in_ax_range[i] );
+
+ Piecewise<SBasis> h;
+ Interval level;
+ h = bb_in[Y] - compose( top_ay, bb_in[X] );
+ level = Interval( -infinity(), 0 );
+ std::vector<Interval> rts_lo = level_set( h, level);
+ h = bb_in[Y] - compose( bot_ay, bb_in[X] );
+ level = Interval( 0, infinity());
+ std::vector<Interval> rts_hi = level_set( h, level);
+
+ std::vector<Interval> rts = intersect( rts_lo, rts_hi );
+ tbs.insert(tbs.end(), rts.begin(), rts.end() );
+ }
+
+ std::vector<std::pair<Interval, Interval> > result(tbs.size(),std::pair<Interval,Interval>());
+
+ /* for each solution I, find times when aa is in the neighborhood of bb(I).
+ * (Note: the preimage of bb[X](I) by aa[X], enlarged by tol, is a good approximation of this:
+ * it would give points in the 2*tol neighborhood of bb (if the slope of aa is never more than 1).
+ * + faster computation.
+ * - implies little jumps depending on the subdivision of the input curve into monotonic pieces
+ * and on the choice of preferred axis. If noticeable, these jumps would feel random to the user :-(
+ */
+ for (unsigned j=0; j<tbs.size(); j++){
+ result[j].second = tbs[j];
+ std::vector<Interval> tas;
+ Piecewise<SBasis> fat_y_of_x = y_of_x;
+ prolongateByConstants( fat_y_of_x, 100*(1+tol) );
+
+ D2<Piecewise<SBasis> > top_b, bot_b;
+ D2<SBasis> bbj = portion( bb, tbs[j] );
+ computeLinfinityNeighborhood( bbj, tol, top_b, bot_b );
+
+ Piecewise<SBasis> h;
+ Interval level;
+ h = top_b[Y] - compose( fat_y_of_x, top_b[X] );
+ level = Interval( +infinity(), 0 );
+ std::vector<Interval> rts_top = level_set( h, level);
+ for (unsigned idx=0; idx < rts_top.size(); idx++){
+ rts_top[idx] = Interval( top_b[X].valueAt( rts_top[idx].min() ),
+ top_b[X].valueAt( rts_top[idx].max() ) );
+ }
+ assert( rts_top.size() == 1 );
+
+ h = bot_b[Y] - compose( fat_y_of_x, bot_b[X] );
+ level = Interval( 0, -infinity());
+ std::vector<Interval> rts_bot = level_set( h, level);
+ for (unsigned idx=0; idx < rts_bot.size(); idx++){
+ rts_bot[idx] = Interval( bot_b[X].valueAt( rts_bot[idx].min() ),
+ bot_b[X].valueAt( rts_bot[idx].max() ) );
+ }
+ assert( rts_bot.size() == 1 );
+
+#if VERBOSE
+ printf("range(aa[X]) = [%f, %f];\n", y_of_x.domain().min(), y_of_x.domain().max());
+ printf("range(bbj[X]) = [%f, %f]; tol= %f\n", bbj[X].at0(), bbj[X].at1(), tol);
+
+ printf("rts_top = ");
+ for (unsigned dbgi=0; dbgi<rts_top.size(); dbgi++){
+ printf("[%f,%f]U", rts_top[dbgi].min(), rts_top[dbgi].max() );
+ }
+ printf("\n");
+ printf("rts_bot = ");
+ for (unsigned dbgi=0; dbgi<rts_bot.size(); dbgi++){
+ printf("[%f,%f]U", rts_bot[dbgi].min(), rts_bot[dbgi].max() );
+ }
+ printf("\n");
+#endif
+ rts_top = intersect( rts_top, rts_bot );
+#if VERBOSE
+ printf("intersection = ");
+ for (unsigned dbgi=0; dbgi<rts_top.size(); dbgi++){
+ printf("[%f,%f]U", rts_top[dbgi].min(), rts_top[dbgi].max() );
+ }
+ printf("\n\n");
+
+ if (rts_top.size() != 1){
+ printf("!!!!!!!!!!!!!!!!!!!!!!\n!!!!!!!!!!!!!!!!!!!!!!\n");
+ rts_top[0].unionWith( rts_top[1] );
+ assert( false );
+ }
+#endif
+ assert (rts_top.size() == 1);
+ Interval x_dom = rts_top[0];
+
+ if ( x_dom.max() <= x_range_strict.min() ){
+ tas.push_back( Interval ( ( aa[X].at0() < aa[X].at1() ) ? 0 : 1 ) );
+ }else if ( x_dom.min() >= x_range_strict.max() ){
+ tas.push_back( Interval ( ( aa[X].at0() < aa[X].at1() ) ? 1 : 0 ) );
+ }else{
+ tas = level_set(aa[X], x_dom );
+ }
+
+#if VERBOSE
+ if ( tas.size() != 1 ){
+ printf("Error: preimage of [%f, %f] by x:[0,1]->[%f, %f] is ",
+ x_dom.min(), x_dom.max(), x_range_strict.min(), x_range_strict.max());
+ if ( tas.size() == 0 ){
+ printf( "empty.\n");
+ }else{
+ printf("\n [%f,%f]", tas[0].min(), tas[0].max() );
+ for (unsigned toto=1; toto<tas.size(); toto++){
+ printf(" U [%f,%f]", tas[toto].min(), tas[toto].max() );
+ }
+ }
+ }
+#endif
+ assert( tas.size()==1 );
+ result[j].first = tas.front();
+ }
+
+ if (swapresult) {
+ for ( unsigned i=0; i<result.size(); i++){
+ Interval temp = result[i].first;
+ result[i].first = result[i].second;
+ result[i].second = temp;
+ }
+ }
+ return result;
+}
+
+#endif
+
+class Intersector : public Toy
+{
+ private:
+ void draw( cairo_t *cr, std::ostringstream *notify,
+ int width, int height, bool save, std::ostringstream *timer_stream) override
+ {
+ double tol = exp_rescale(slider.value());
+ D2<SBasis> A = handles_to_sbasis(psh.pts.begin(), A_bez_ord-1);
+ D2<SBasis> B = handles_to_sbasis(psh.pts.begin()+A_bez_ord, B_bez_ord-1);
+ cairo_set_line_width (cr, .8);
+ cairo_set_source_rgba(cr,0.,0.,0.,.6);
+ cairo_d2_sb(cr, A);
+ cairo_d2_sb(cr, B);
+ cairo_stroke(cr);
+
+ Rect tolbytol( anchor.pos, anchor.pos );
+ tolbytol.expandBy( tol );
+ cairo_rectangle(cr, tolbytol);
+ cairo_stroke(cr);
+/*
+ Piecewise<D2<SBasis> > smthA = linearizeCusps(A+Point(0,10), tol);
+ cairo_set_line_width (cr, 1.);
+ cairo_set_source_rgba(cr, 1., 0., 1., 1. );
+ cairo_pw_d2_sb(cr, smthA);
+ cairo_stroke(cr);
+*/
+
+ std::vector<Interval> Acuts = monotonicSplit(A);
+ std::vector<Interval> Bcuts = monotonicSplit(B);
+
+#if 0
+ for (unsigned i=0; i<Acuts.size(); i++){
+ D2<SBasis> Ai = portion( A, Acuts[i]);
+ cairo_set_line_width (cr, .2);
+ cairo_set_source_rgba(cr, 0., 0., 0., 1. );
+ draw_cross(cr, Ai.at0());
+ cairo_stroke(cr);
+ for (unsigned j=0; j<Bcuts.size(); j++){
+ std::vector<std::pair<Interval, Interval> > my_intersections;
+ D2<SBasis> Bj = portion( B, Bcuts[j]);
+ cairo_set_line_width (cr, .2);
+ cairo_set_source_rgba(cr, 0., 0., 0., 1. );
+ draw_cross(cr, Bj.at0());
+ cairo_stroke(cr);
+ }
+ }
+#endif
+
+ std::vector<SmashIntersection> my_intersections;
+ my_intersections = smash_intersect( A, B, tol );
+
+ for (auto & my_intersection : my_intersections){
+ cairo_set_line_width (cr, 2.5);
+ cairo_set_source_rgba(cr, 1., 0., 0., .8 );
+ cairo_d2_sb(cr, portion( A, my_intersection.times[X]));
+ cairo_stroke(cr);
+ cairo_set_line_width (cr, 2.5);
+ cairo_set_source_rgba(cr, 0., 0., 1., .8 );
+ cairo_d2_sb(cr, portion( B, my_intersection.times[Y]));
+ cairo_stroke(cr);
+ }
+#if 0
+
+ unsigned apiece( slidera.value()/100. * Acuts.size() );
+ unsigned bpiece( sliderb.value()/100. * Bcuts.size() );
+
+
+ for (unsigned i=0; i<Acuts.size(); i++){
+ D2<SBasis> Ai = portion( A, Acuts[i]);
+ for (unsigned j=0; j<Bcuts.size(); j++){
+ if ( toggle.on && (i != apiece || j != bpiece) ) continue;
+
+ std::vector<SmashIntersection> my_intersections;
+ D2<SBasis> Bj = portion( B, Bcuts[j]);
+ bool draw_more = toggle.on && i == apiece && j == bpiece;
+// my_intersections = smash_intersect( Ai, Bj, tol, cr, draw_more );
+ my_intersections = monotonic_smash_intersect( Ai, Bj, tol );
+
+ for (unsigned k=0; k<my_intersections.size(); k++){
+ cairo_set_line_width (cr, 2.5);
+ cairo_set_source_rgba(cr, 1., 0., 0., .8 );
+ cairo_d2_sb(cr, portion( Ai, my_intersections[k].times[X]));
+ cairo_stroke(cr);
+ cairo_set_line_width (cr, 2.5);
+ cairo_set_source_rgba(cr, 0., 0., 1., .8 );
+ cairo_d2_sb(cr, portion( Bj, my_intersections[k].times[Y]));
+ cairo_stroke(cr);
+ }
+ }
+ }
+#endif
+ Toy::draw(cr, notify, width, height, save,timer_stream);
+ }
+
+ public:
+ Intersector(unsigned int _A_bez_ord, unsigned int _B_bez_ord)
+ : A_bez_ord(_A_bez_ord), B_bez_ord(_B_bez_ord)
+ {
+ unsigned int total_handles = A_bez_ord + B_bez_ord;
+ for ( unsigned int i = 0; i < total_handles; ++i )
+ psh.push_back(Geom::Point(uniform()*400, uniform()*400));
+ handles.push_back(&psh);
+ slider = Slider(-4, 2, 0, 1.2, "tolerance");
+ slider.geometry(Point(30, 20), 250);
+ slider.formatter(&exp_formatter);
+ handles.push_back(&slider);
+ slidera = Slider(0, 100, 1, 0., "piece on A");
+ slidera.geometry(Point(300, 50), 250);
+ handles.push_back(&slidera);
+ sliderb = Slider(0, 100, 1, 0., "piece on B");
+ sliderb.geometry(Point(300, 80), 250);
+ handles.push_back(&sliderb);
+ toggle = Toggle( Rect(Point(300,10), Point(440,30)), "Piece by piece", false );
+ handles.push_back(&toggle);
+ anchor = PointHandle ( Point(100, 100 ) );
+ handles.push_back(&anchor);
+ }
+
+ private:
+ unsigned int A_bez_ord;
+ unsigned int B_bez_ord;
+ PointSetHandle psh;
+ PointHandle anchor;
+ Slider slider,slidera,sliderb;
+ Toggle toggle;
+};
+
+
+int main(int argc, char **argv)
+{
+ unsigned int A_bez_ord=4;
+ unsigned int B_bez_ord=4;
+ if(argc > 2)
+ sscanf(argv[2], "%d", &B_bez_ord);
+ if(argc > 1)
+ sscanf(argv[1], "%d", &A_bez_ord);
+
+ init( argc, argv, new Intersector(A_bez_ord, B_bez_ord));
+ return 0;
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :