Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
hatches.cpp
Go to the documentation of this file.
1#include <2geom/d2.h>
2#include <2geom/sbasis.h>
5
6#include <toys/path-cairo.h>
8
9#include <cstdlib>
10#include <vector>
11using std::vector;
12using namespace Geom;
13
14#define SIZE 4
15#define NB_SLIDER 8
16
17//------------------------------------------------
18// Some goodies to navigate through curve's levels.
19//------------------------------------------------
20struct LevelCrossing{
21 Point pt;
22 double t;
23 bool sign;
24 bool used;
25};
26struct LevelCrossingOrder {
27 bool operator()(LevelCrossing a, LevelCrossing b) {
28 return a.pt[Y] < b.pt[Y];
29 }
30};
31
32typedef std::vector<LevelCrossing> LevelCrossings;
33
34class LevelsCrossings: public std::vector<LevelCrossings>{
35public:
36 LevelsCrossings():std::vector<LevelCrossings>(){};
37 LevelsCrossings(std::vector<std::vector<double> > const &times,
38 Piecewise<D2<SBasis> > const &f,
39 Piecewise<SBasis> const &dx){
40 for (const auto & time : times){
42 for (double j : time){
43 LevelCrossing lc;
44 lc.pt = f.valueAt(j);
45 lc.t = j;
46 lc.sign = ( dx.valueAt(j)>0 );
47 lc.used = false;
48 lcs.push_back(lc);
49 }
50 std::sort(lcs.begin(), lcs.end(), LevelCrossingOrder());
51 //TODO: reverse all "in" flag if we had the wrong orientation!
52 push_back(lcs);
53 }
54 }
55 void flipInOut(){
56 for (unsigned i=0; i<size(); i++){
57 for (auto & j : (*this)){
58 j.sign = !j.sign;
59 }
60 }
61 }
62 void findFirstUnused(unsigned &level, unsigned &idx){
63 level = size();
64 idx = 0;
65 for (unsigned i=0; i<size(); i++){
66 for (unsigned j=0; j<(*this)[i].size(); j++){
67 if (!(*this)[i][j].used){
68 level = i;
69 idx = j;
70 return;
71 }
72 }
73 }
74 }
75 //set indexes to point to the next point in the "snake walk"
76 //follow_level's meaning:
77 // 0=yes upward
78 // 1=no, last move was upward,
79 // 2=yes downward
80 // 3=no, last move was downward.
81 void step(unsigned &level, unsigned &idx, int &direction){
82 std::cout << "Entering step: "<<level<<","<<idx<<", dir="<< direction<<"\n";
83
84 if ( direction % 2 == 0 ){
85 if (direction == 0) {
86 if ( idx >= (*this)[level].size()-1 || (*this)[level][idx+1].used ) {
87 level = size();
88 std::cout << "max end of level reached...\n";
89 return;
90 }
91 idx += 1;
92 }else{
93 if ( idx <= 0 || (*this)[level][idx-1].used ) {
94 level = size();
95 std::cout << "min end of level reached...\n";
96 return;
97 }
98 idx -= 1;
99 }
100 direction += 1;
101 std::cout << "exit with: "<<level<<","<<idx<<", dir="<< direction<<"\n";
102 return;
103 }
104 double t = (*this)[level][idx].t;
105 double sign = ((*this)[level][idx].sign ? 1 : -1);
106 double next_t = t;
107 level += 1;
108 direction = (direction + 1)%4;
109 if (level == size()){
110 std::cout << "max level reached\n";
111 return;
112 }
113 for (unsigned j=0; j<(*this)[level].size(); j++){
114 double tj = (*this)[level][j].t;
115 if ( sign*(tj-t) > 0 ){
116 if( next_t == t || sign*(tj-next_t)<0 ){
117 next_t = tj;
118 idx = j;
119 }
120 }
121 }
122 if ( next_t == t ){//not found.
123 level = size();
124 std::cout << "no next time found\n";
125 return;
126 }
127 //TODO: time is periodic!!!
128 //TODO: allow several components.
129 if ( (*this)[level][idx].used ) {
130 level = size();
131 std::cout << " reached a point already used\n";
132 return;
133 }
134 std::cout << "exit with: "<<level<<","<<idx<<"\n";
135 return;
136 }
137};
138
139
140//------------------------------------------------
141// Generate the levels with random, growth...
142//------------------------------------------------
143std::vector<double>generateLevels(Interval const &domain,
144 double const width,
145 double const growth,
146 double randomness){
147 std::vector<double> result;
148 std::srand(0);
149 double x = domain.min() + width/2;
150 double step = width;
151 while (x<domain.max()){
152 result.push_back(x);
153 double rdm = 1+ ( (rand() % 100) - 50) /100.*randomness;
154 x+= step*growth*rdm;
155 step*=growth;
156 }
157 return result;
158}
159
160
161//-------------------------------------------------------
162// Walk through the intersections to create linear hatches
163//-------------------------------------------------------
164std::vector<Point> linearSnake(Piecewise<D2<SBasis> > const &f, double dy,double growth, double rdmness){
165
166 std::vector<Point> result;
167
169 //Rque: derivative is computed twice in the 2 lines below!!
171 OptInterval range = bounds_exact(x);
172 //TODO: test range non emptyness!!
173 std::vector<double> levels = generateLevels((*range), dy, growth, rdmness);
174 std::vector<std::vector<double> > times;
175 times = multi_roots(x,levels);
176
177//TODO: fix multi_roots!!!*****************************************
178//remove doubles :-(
179 std::vector<std::vector<double> > cleaned_times(levels.size(),std::vector<double>());
180 for (unsigned i=0; i<times.size(); i++){
181 if ( times[i].size()>0 ){
182 double last_t = times[i][0]-1;//ugly hack!!
183 for (unsigned j=0; j<times[i].size(); j++){
184 if (times[i][j]-last_t >0.000001){
185 last_t = times[i][j];
186 cleaned_times[i].push_back(last_t);
187 }
188 }
189 }
190 }
191 times = cleaned_times;
192 for (unsigned i=0; i<times.size(); i++){
193 std::cout << "roots on level "<<i<<": ";
194 for (double j : times){
195 std::cout << j <<" ";
196 }
197 std::cout <<"\n";
198 }
199//*******************************************************************
200 LevelsCrossings lscs(times,f,dx);
201 unsigned i,j;
202 lscs.findFirstUnused(i,j);
203 while ( i < lscs.size() ){
204 int dir = 0;
205 while ( i < lscs.size() ){
206 result.push_back(lscs[i][j].pt);
207 lscs[i][j].used = true;
208 lscs.step(i,j, dir);
209 }
210 //TODO: handle "non convex cases" where hatches have to be restarted at some point.
211 //This needs some care in linearSnake->smoothSnake.
212 //
213 lscs.findFirstUnused(i,j);
214 }
215 return result;
216}
217
218//-------------------------------------------------------
219// Smooth the linear hatches according to params...
220//-------------------------------------------------------
221Piecewise<D2<SBasis> > smoothSnake(std::vector<Point> const &linearSnake,
222 double scale_bf = 1, double scale_bb = 1,
223 double scale_tf = 1, double scale_tb = 1){
224
225 if (linearSnake.size()<2) return Piecewise<D2<SBasis> >();
226 bool is_top = true;
227 Point last_pt = linearSnake[0];
228 Point last_hdle = linearSnake[0];
229 Path result(last_pt);
230 unsigned i=1;
231 while( i+1<linearSnake.size() ){
232 Point pt0 = linearSnake[i];
233 Point pt1 = linearSnake[i+1];
234 Point new_pt = (pt0+pt1)/2;
235 double scale = (is_top ? scale_tf : scale_bf );
236 Point new_hdle = new_pt+(pt0-new_pt)*scale;
237
238 result.appendNew<CubicBezier>(last_hdle,new_hdle,new_pt);
239
240 last_pt = new_pt;
241 scale = (is_top ? scale_tb : scale_bb );
242 last_hdle = new_pt+(pt1-new_pt)*scale;
243 i+=2;
244 is_top = !is_top;
245 }
246 if ( i<linearSnake.size() )
247 result.appendNew<CubicBezier>(last_hdle,linearSnake[i],linearSnake[i]);
248 return result.toPwSb();
249}
250
251//-------------------------------------------------------
252// Bend a path...
253//-------------------------------------------------------
254
257 ff[X] += compose(bending, ff[Y]);
258 return sectionize(ff);
259}
260
261//-------------------------------------------------------
262// The toy!
263//-------------------------------------------------------
264class HatchesToy: public Toy {
265
266 PointHandle adjuster[NB_SLIDER];
267
268public:
269 PointSetHandle b1_handle;
270 PointSetHandle b2_handle;
271 void draw(cairo_t *cr,
272 std::ostringstream *notify,
273 int width, int height, bool save, std::ostringstream *timer_stream) override {
274 for(unsigned i=0; i<NB_SLIDER; i++){
275 adjuster[i].pos[X] = 30+i*20;
276 if (adjuster[i].pos[Y]<100) adjuster[i].pos[Y] = 100;
277 if (adjuster[i].pos[Y]>400) adjuster[i].pos[Y] = 400;
278 cairo_move_to(cr, Point(30+i*20,100));
279 cairo_line_to(cr, Point(30+i*20,400));
280 cairo_set_line_width (cr, .5);
281 cairo_set_source_rgba (cr, 0., 0., 0., 1);
282 cairo_stroke(cr);
283 }
284 double hatch_width = (400-adjuster[0].pos[Y])/300.*50;
285 double scale_topfront = (250-adjuster[1].pos[Y])/150.*5;
286 double scale_topback = (250-adjuster[2].pos[Y])/150.*5;
287 double scale_botfront = (250-adjuster[3].pos[Y])/150.*5;
288 double scale_botback = (250-adjuster[4].pos[Y])/150.*5;
289 double growth = 1+(250-adjuster[5].pos[Y])/150.*.1;
290 double rdmness = 1+(400-adjuster[6].pos[Y])/300.*.9;
291 double bend_amount = (250-adjuster[7].pos[Y])/300.*100.;
292
293 b1_handle.pts.back() = b2_handle.pts.front();
294 b1_handle.pts.front() = b2_handle.pts.back();
295 D2<SBasis> B1 = b1_handle.asBezier();
296 D2<SBasis> B2 = b2_handle.asBezier();
297
298 {
299 cairo_save(cr);
300 cairo_set_line_width(cr, 0.3);
301 cairo_set_source_rgb(cr, 0, 0, 0);
302 cairo_d2_sb(cr, B1);
303 cairo_d2_sb(cr, B2);
304 cairo_restore(cr);
305 }
306
308 B.concat(Piecewise<D2<SBasis> >(B1));
310
311 Piecewise<SBasis> bending = Piecewise<SBasis>(shift(Linear(bend_amount),1));
312 //TODO: test optrect non empty!!
313 bending.setDomain((*bounds_exact(B))[Y]);
314 Piecewise<D2<SBasis> >bentB = bend(B, bending);
315
316 std::vector<Point> snakePoints;
317 snakePoints = linearSnake(bentB, hatch_width, growth, rdmness);
318 Piecewise<D2<SBasis> >smthSnake = smoothSnake(snakePoints,
319 scale_topfront,
320 scale_topback,
321 scale_botfront,
322 scale_botback);
323
324 smthSnake = bend(smthSnake, -bending);
325 cairo_pw_d2_sb(cr, smthSnake);
326 cairo_set_line_width (cr, 1.5);
327 cairo_set_source_rgba (cr, 0., 0., 0., 1);
328 cairo_stroke(cr);
329
330 if ( snakePoints.size() > 0 ){
331 Path snake(snakePoints.front());
332 for (unsigned i=1; i<snakePoints.size(); i++){
333 snake.appendNew<LineSegment>(snakePoints[i]);
334 }
335 //cairo_pw_d2_sb(cr, snake.toPwSb() );
336 }
337
338 //cairo_pw_d2_sb(cr, B);
339 cairo_set_line_width (cr, .5);
340 cairo_set_source_rgba (cr, 0.7, 0.2, 0., 1);
341 cairo_stroke(cr);
342
343
344 Toy::draw(cr, notify, width, height, save,timer_stream);
345 }
346
347public:
348 HatchesToy(){
349 for(int i = 0; i < SIZE; i++) {
350 b1_handle.push_back(150+uniform()*300,150+uniform()*300);
351 b2_handle.push_back(150+uniform()*300,150+uniform()*300);
352 }
353 b1_handle.pts[0] = Geom::Point(400,300);
354 b1_handle.pts[1] = Geom::Point(400,400);
355 b1_handle.pts[2] = Geom::Point(100,400);
356 b1_handle.pts[3] = Geom::Point(100,300);
357
358 b2_handle.pts[0] = Geom::Point(100,300);
359 b2_handle.pts[1] = Geom::Point(100,200);
360 b2_handle.pts[2] = Geom::Point(400,200);
361 b2_handle.pts[3] = Geom::Point(400,300);
362 handles.push_back(&b1_handle);
363 handles.push_back(&b2_handle);
364
365 for(unsigned i = 0; i < NB_SLIDER; i++) {
366 adjuster[i].pos = Geom::Point(30+i*20,250);
367 handles.push_back(&(adjuster[i]));
368 }
369 }
370};
371
372int main(int argc, char **argv) {
373 init(argc, argv, new HatchesToy);
374 return 0;
375}
376
377/*
378 Local Variables:
379 mode:c++
380 c-file-style:"stroustrup"
381 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
382 indent-tabs-mode:nil
383 fill-column:99
384 End:
385*/
386//vim:filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99:
double scale
Definition aa.cpp:228
int main()
Conversion between Bezier control points and SBasis curves.
static int constexpr SIZE
Definition cielab.cpp:45
Bezier curve with compile-time specified order.
Adaptor that creates 2D functions from 1D ones.
Definition d2.h:55
constexpr C min() const
constexpr C max() const
Range of real numbers that is never empty.
Definition interval.h:59
Function that interpolates linearly between two values.
Definition linear.h:55
Range of real numbers that can be empty.
Definition interval.h:199
Sequence of contiguous curves, aka spline.
Definition path.h:353
Function defined as discrete pieces.
Definition piecewise.h:71
output_type valueAt(double t) const
Definition piecewise.h:102
void continuousConcat(const Piecewise< T > &other)
Definition piecewise.h:251
void concat(const Piecewise< T > &other)
Definition piecewise.h:235
void setDomain(Interval dom)
Definition piecewise.h:218
Two-dimensional point that doubles as a vector.
Definition point.h:66
Geom::Point pos
void push_back(double x, double y)
Geom::D2< Geom::SBasis > asBezier()
std::vector< Geom::Point > pts
vector< Handle * > handles
virtual void save(FILE *f)
virtual void draw(cairo_t *cr, std::ostringstream *notify, int w, int h, bool save, std::ostringstream *timing_stream)
Css & result
Geom::IntPoint size
Lifts one dimensional objects into 2D.
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
std::vector< double > generateLevels(Interval const &domain, double const width, double const growth, double randomness)
Definition hatches.cpp:143
std::vector< LevelCrossing > LevelCrossings
Definition hatches.cpp:32
Piecewise< D2< SBasis > > bend(Piecewise< D2< SBasis > > const &f, Piecewise< SBasis > bending)
Definition hatches.cpp:255
Piecewise< D2< SBasis > > smoothSnake(std::vector< Point > const &linearSnake, double scale_bf=1, double scale_bb=1, double scale_tf=1, double scale_tb=1)
Definition hatches.cpp:221
std::vector< Point > linearSnake(Piecewise< D2< SBasis > > const &f, double dy, double growth, double rdmness)
Definition hatches.cpp:164
Various utility functions.
Definition affine.h:22
D2< Piecewise< SBasis > > make_cuts_independent(Piecewise< D2< SBasis > > const &a)
Definition d2-sbasis.cpp:75
OptInterval bounds_exact(Bezier const &b)
Definition bezier.cpp:310
static float sign(double number)
Returns +1 for positive numbers, -1 for negative numbers, and 0 otherwise.
Piecewise< D2< SBasis > > sectionize(D2< Piecewise< SBasis > > const &a)
Definition d2-sbasis.cpp:65
SBasisOf< T > shift(SBasisOf< T > const &a, int sh)
Definition sbasis-of.h:435
D2< T > compose(D2< T > const &a, T const &b)
Definition d2.h:405
Bezier derivative(Bezier const &a)
Definition bezier.cpp:282
std::vector< std::vector< double > > multi_roots(SBasis const &f, std::vector< double > const &levels, double htol=1e-7, double vtol=1e-7, double a=0, double b=1)
void cairo_line_to(cairo_t *cr, Geom::Point p1)
struct _cairo cairo_t
Definition path-cairo.h:16
void cairo_move_to(cairo_t *cr, Geom::Point p1)
void cairo_d2_sb(cairo_t *cr, Geom::D2< Geom::SBasis > const &p)
void cairo_pw_d2_sb(cairo_t *cr, Geom::Piecewise< Geom::D2< Geom::SBasis > > const &p)
bool used
two-dimensional geometric operators.
Polynomial in symmetric power basis (S-basis)
double height
double width
double uniform()
void cairo_set_source_rgba(cairo_t *cr, colour c)
void init(int argc, char **argv, Toy *t, int width=600, int height=600)