#include <2geom/d2.h> #include <2geom/sbasis.h> #include <2geom/bezier-to-sbasis.h> #include <2geom/sbasis-geometric.h> #include #include #include #include using std::vector; using namespace Geom; #define SIZE 4 #define NB_SLIDER 8 //------------------------------------------------ // Some goodies to navigate through curve's levels. //------------------------------------------------ struct LevelCrossing{ Point pt; double t; bool sign; bool used; }; struct LevelCrossingOrder { bool operator()(LevelCrossing a, LevelCrossing b) { return a.pt[Y] < b.pt[Y]; } }; typedef std::vector LevelCrossings; class LevelsCrossings: public std::vector{ public: LevelsCrossings():std::vector(){}; LevelsCrossings(std::vector > const ×, Piecewise > const &f, Piecewise const &dx){ for (const auto & time : times){ LevelCrossings lcs; for (double j : time){ LevelCrossing lc; lc.pt = f.valueAt(j); lc.t = j; lc.sign = ( dx.valueAt(j)>0 ); lc.used = false; lcs.push_back(lc); } std::sort(lcs.begin(), lcs.end(), LevelCrossingOrder()); //TODO: reverse all "in" flag if we had the wrong orientation! push_back(lcs); } } void flipInOut(){ for (unsigned i=0; i= (*this)[level].size()-1 || (*this)[level][idx+1].used ) { level = size(); std::cout << "max end of level reached...\n"; return; } idx += 1; }else{ if ( idx <= 0 || (*this)[level][idx-1].used ) { level = size(); std::cout << "min end of level reached...\n"; return; } idx -= 1; } direction += 1; std::cout << "exit with: "< 0 ){ if( next_t == t || sign*(tj-next_t)<0 ){ next_t = tj; idx = j; } } } if ( next_t == t ){//not found. level = size(); std::cout << "no next time found\n"; return; } //TODO: time is periodic!!! //TODO: allow several components. if ( (*this)[level][idx].used ) { level = size(); std::cout << " reached a point already used\n"; return; } std::cout << "exit with: "<generateLevels(Interval const &domain, double const width, double const growth, double randomness){ std::vector result; std::srand(0); double x = domain.min() + width/2; double step = width; while (x linearSnake(Piecewise > const &f, double dy,double growth, double rdmness){ std::vector result; Piecewise x = make_cuts_independent(f)[X]; //Rque: derivative is computed twice in the 2 lines below!! Piecewise dx = derivative(x); OptInterval range = bounds_exact(x); //TODO: test range non emptyness!! std::vector levels = generateLevels((*range), dy, growth, rdmness); std::vector > times; times = multi_roots(x,levels); //TODO: fix multi_roots!!!***************************************** //remove doubles :-( std::vector > cleaned_times(levels.size(),std::vector()); for (unsigned i=0; i0 ){ double last_t = times[i][0]-1;//ugly hack!! for (unsigned j=0; j0.000001){ last_t = times[i][j]; cleaned_times[i].push_back(last_t); } } } } times = cleaned_times; for (unsigned i=0; ismoothSnake. // lscs.findFirstUnused(i,j); } return result; } //------------------------------------------------------- // Smooth the linear hatches according to params... //------------------------------------------------------- Piecewise > smoothSnake(std::vector const &linearSnake, double scale_bf = 1, double scale_bb = 1, double scale_tf = 1, double scale_tb = 1){ if (linearSnake.size()<2) return Piecewise >(); bool is_top = true; Point last_pt = linearSnake[0]; Point last_hdle = linearSnake[0]; Path result(last_pt); unsigned i=1; while( i+1(last_hdle,new_hdle,new_pt); last_pt = new_pt; scale = (is_top ? scale_tb : scale_bb ); last_hdle = new_pt+(pt1-new_pt)*scale; i+=2; is_top = !is_top; } if ( i(last_hdle,linearSnake[i],linearSnake[i]); return result.toPwSb(); } //------------------------------------------------------- // Bend a path... //------------------------------------------------------- Piecewise > bend(Piecewise > const &f, Piecewise bending){ D2 > ff = make_cuts_independent(f); ff[X] += compose(bending, ff[Y]); return sectionize(ff); } //------------------------------------------------------- // The toy! //------------------------------------------------------- class HatchesToy: public Toy { PointHandle adjuster[NB_SLIDER]; public: PointSetHandle b1_handle; PointSetHandle b2_handle; void draw(cairo_t *cr, std::ostringstream *notify, int width, int height, bool save, std::ostringstream *timer_stream) override { for(unsigned i=0; i400) adjuster[i].pos[Y] = 400; cairo_move_to(cr, Point(30+i*20,100)); cairo_line_to(cr, Point(30+i*20,400)); cairo_set_line_width (cr, .5); cairo_set_source_rgba (cr, 0., 0., 0., 1); cairo_stroke(cr); } double hatch_width = (400-adjuster[0].pos[Y])/300.*50; double scale_topfront = (250-adjuster[1].pos[Y])/150.*5; double scale_topback = (250-adjuster[2].pos[Y])/150.*5; double scale_botfront = (250-adjuster[3].pos[Y])/150.*5; double scale_botback = (250-adjuster[4].pos[Y])/150.*5; double growth = 1+(250-adjuster[5].pos[Y])/150.*.1; double rdmness = 1+(400-adjuster[6].pos[Y])/300.*.9; double bend_amount = (250-adjuster[7].pos[Y])/300.*100.; b1_handle.pts.back() = b2_handle.pts.front(); b1_handle.pts.front() = b2_handle.pts.back(); D2 B1 = b1_handle.asBezier(); D2 B2 = b2_handle.asBezier(); { cairo_save(cr); cairo_set_line_width(cr, 0.3); cairo_set_source_rgb(cr, 0, 0, 0); cairo_d2_sb(cr, B1); cairo_d2_sb(cr, B2); cairo_restore(cr); } Piecewise >B; B.concat(Piecewise >(B1)); B.continuousConcat(Piecewise >(B2)); Piecewise bending = Piecewise(shift(Linear(bend_amount),1)); //TODO: test optrect non empty!! bending.setDomain((*bounds_exact(B))[Y]); Piecewise >bentB = bend(B, bending); std::vector snakePoints; snakePoints = linearSnake(bentB, hatch_width, growth, rdmness); Piecewise >smthSnake = smoothSnake(snakePoints, scale_topfront, scale_topback, scale_botfront, scale_botback); smthSnake = bend(smthSnake, -bending); cairo_pw_d2_sb(cr, smthSnake); cairo_set_line_width (cr, 1.5); cairo_set_source_rgba (cr, 0., 0., 0., 1); cairo_stroke(cr); if ( snakePoints.size() > 0 ){ Path snake(snakePoints.front()); for (unsigned i=1; i(snakePoints[i]); } //cairo_pw_d2_sb(cr, snake.toPwSb() ); } //cairo_pw_d2_sb(cr, B); cairo_set_line_width (cr, .5); cairo_set_source_rgba (cr, 0.7, 0.2, 0., 1); cairo_stroke(cr); Toy::draw(cr, notify, width, height, save,timer_stream); } public: HatchesToy(){ for(int i = 0; i < SIZE; i++) { b1_handle.push_back(150+uniform()*300,150+uniform()*300); b2_handle.push_back(150+uniform()*300,150+uniform()*300); } b1_handle.pts[0] = Geom::Point(400,300); b1_handle.pts[1] = Geom::Point(400,400); b1_handle.pts[2] = Geom::Point(100,400); b1_handle.pts[3] = Geom::Point(100,300); b2_handle.pts[0] = Geom::Point(100,300); b2_handle.pts[1] = Geom::Point(100,200); b2_handle.pts[2] = Geom::Point(400,200); b2_handle.pts[3] = Geom::Point(400,300); handles.push_back(&b1_handle); handles.push_back(&b2_handle); for(unsigned i = 0; i < NB_SLIDER; i++) { adjuster[i].pos = Geom::Point(30+i*20,250); handles.push_back(&(adjuster[i])); } } }; int main(int argc, char **argv) { init(argc, argv, new HatchesToy); 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: