Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
rdm-area.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>
11#include <list>
12#include <algorithm>
13using std::vector;
14using namespace Geom;
15
16#define SIZE 4
17#define NB_SLIDER 2
18
19struct Triangle{
20 Point a,b,c;
21 double area;
22};
23
24//TODO: this would work only for C1 pw<d2<sb>> input. Split the input at corners to work with pwd2sb...
25//TODO: for more general purpose, return a path...
26void toPoly(D2<SBasis> const &f, std::list<Point> &p, double tol, bool include_first=true){
27 D2<SBasis> df = derivative(f);
28 D2<SBasis> d2f = derivative(df);
29 double t=0;
30 if ( include_first ){ p.push_back( f.at0() );}
31 while (t<1){
32 Point v = unit_vector(df.valueAt(t));
33 //OptInterval bounds = bounds_local(df[X]*v[Y]-df[Y]*v[X], Interval(t,1));
34 OptInterval bounds = bounds_local(d2f[X]*v[Y]-d2f[Y]*v[X], Interval(t,1));
35 if (bounds) {
36 double bds_max = (-bounds->min()>bounds->max() ? -bounds->min() : bounds->max());
37 double dt;
38 //if (bds_max<tol) dt = 1;
39 //else dt = tol/bds_max;
40 if (bds_max<tol/4) dt = 1;
41 else dt = 2*std::sqrt( tol / bds_max );
42 t+=dt*5;
43 if (t>1) t = 1;
44 }else{
45 t = 1;
46 }
47 p.push_back( f.valueAt(t) );
48 }
49 return;
50}
51
52std::list<Point> toPoly(std::vector<Piecewise<D2<SBasis> > > f, double tol){
53 assert ( f.size() >0 && f[0].size() >0 );
54 std::list<Point> res;
55
56 for (unsigned i = 0; i<f.size(); i++){
57 for (unsigned j = 0; j<f[i].size(); j++){
58 toPoly(f[i][j],res,tol, j==0);
59 }
60 if ( f[i].segs.front().at0() != f[i].segs.back().at1() ){
61 res.push_back( f[i].segs.front().at0() );
62 }
63 if ( i>0 ) res.push_back( f[0][0].at0() );
64 }
65 return res;
66}
67
68//TODO: this is an ugly hack, use path intersection instead!!
69bool intersect(Point const &a0, Point const &b0, Point const &a1, Point const &b1, Point &c, double tol=.0001){
70 double abaa1 = cross( b0-a0, a1-a0);
71 double abab1 = cross( b0-a0, b1-a0);
72 double abaa0 = cross( b1-a1, a0-a1);
73 double abab0 = cross( b1-a1, b0-a1);
74 if ( abaa1 * abab1 < -tol && abaa0 * abab0 < -tol ){
75 c = a1 - (b1-a1) * abaa1/(abab1-abaa1);
76 return true;
77 }
78#if 1
79 return false;//TODO: handle limit cases!!
80#else
81 if ( abaa1 == 0 && dot( a0-a1, b0-a1 ) < 0 ) {
82 c = a1;
83 return true;
84 }
85 if ( abab1 == 0 && dot( a0-b1, b0-b1 ) < 0 ) {
86 c = b1;
87 return true;
88 }
89 if ( abaa0 == 0 && dot( a1-a0, b1-a0 ) < 0 ) {
90 c = a0;
91 return true;
92 }
93 if ( abab0 == 0 && dot( a1-b0, b1-b0 ) < 0 ) {
94 c = b0;
95 return true;
96 }
97 return false;
98#endif
99}
100
101//TODO: use path intersection stuff!
102void uncross(std::list<Point> &loop){
103 std::list<Point>::iterator b0 = loop.begin(),a0,b1,a1;
104 if ( b0 == loop.end() ) return;
105 a0 = b0;
106 ++b0;
107 if ( b0 == loop.end() ) return;
108 //now a0,b0 are 2 consecutive points.
109 while ( b0 != loop.end() ){
110 b1 = b0;
111 ++b1;
112 if ( b1 != loop.end() ) {
113 a1 = b1;
114 ++b1;
115 if ( b1 != loop.end() ) {
116 //now a0,b0,a1,b1 are 4 consecutive points.
117 Point c;
118 while ( b1 != loop.end() ){
119 if ( intersect(*a0,*b0,*a1,*b1,c) ){
120 if ( c != (*a0) && c != (*b0) ){
121 loop.insert(b1,c);
122 loop.insert(b0,c);
123 ++a1;
124 std::list<Point> loop_piece;
125 loop_piece.insert(loop_piece.begin(), b0, a1 );
126 loop_piece.reverse();
127 loop.erase( b0, a1 );
128 loop.splice( a1, loop_piece );
129 b0 = a0;
130 ++b0;
131 //a1 = b1; a1--;//useless
132 }else{
133 //TODO: handle degenerated crossings...
134 }
135 }else{
136 a1=b1;
137 ++b1;
138 }
139 }
140 }
141 }
142 a0 = b0;
143 ++b0;
144 }
145 return;//We should never reach this point.
146}
147//------------------------------------------------------------
148//------------------------------------------------------------
149//------------------------------------------------------------
150void triangulate(std::list<Point> &pts, std::vector<Triangle> &tri, bool clockwise = false, double tol=.001){
151 pts.unique();
152 while ( !pts.empty() && pts.front() == pts.back() ){ pts.pop_back(); }
153 if ( pts.size() < 3 ) return;
154 //cycle by 1 to have a better looking output...
155 pts.push_back(pts.front()); pts.pop_front();
156 std::list<Point>::iterator a,b,c,m;
157 int sign = (clockwise ? -1 : 1 );
158 a = pts.end(); --a;
159 b = pts.begin();
160 c = b; ++c;
161 //now a,b,c are 3 consecutive points.
162 if ( pts.size() == 3 ) {
163 Triangle abc;
164 abc.a = (*a);
165 abc.b = (*b);
166 abc.c = (*c);
167 abc.area = sign *( cross((*b) - (*a),(*c) - (*b))/2) ;
168 if ( abc.area >0 ){
169 tri.push_back(abc);
170 pts.clear();
171 }
172 return;
173 }
174 bool found = false;
175 while( c != pts.end() ){
176 double abac = cross((*b)-(*a),(*c)-(*a));
177 if ( fabs(abac)<tol && dot( *b-*a, *c-*b ) <= 0) {
178 //this is a degenerated triangle. Remove it and continue.
179 pts.erase(b);
180 triangulate(pts,tri,clockwise);
181 return;
182 }
183 m = c;
184 ++m;
185 while ( m != pts.end() && !found && m!=a){
186 bool pointing_inside;
187 double abam = cross((*b)-(*a),(*m)-(*a));
188 double bcbm = cross((*c)-(*b),(*m)-(*b));
189 if ( sign * abac > 0 ){
190 pointing_inside = ( sign * abam >= 0 ) && ( sign * bcbm >= 0 );
191 }else {
192 pointing_inside = ( sign * abam >=0 ) || ( sign * bcbm >=0);
193 }
194 if ( pointing_inside ){
195 std::list<Point>::iterator p=c,q=++p;
196 Point inter;
197 while ( q != pts.end() && !intersect(*b,*m,*p,*q,inter) ){
198 p=q;
199 ++q;
200 }
201 if ( q == pts.end() ){
202 found = true;
203 }else{
204 ++m;
205 }
206 }else{
207 ++m;
208 }
209 }
210 if ( found ){
211 std::list<Point>pts_beg;
212 pts.insert(b,*b);
213 pts.insert(m,*m);
214 pts_beg.splice(pts_beg.begin(), pts, b, m);
215 triangulate(pts_beg,tri,clockwise);
216 triangulate(pts,tri,clockwise);
217 return;
218 }else{
219 a = b;
220 b = c;
221 ++c;
222 }
223 }
224 //we should never reach this point.
225}
226
227double
229 double x = std::rand();
230 return x/RAND_MAX;
231}
232
233class RandomGenerator {
234public:
235 RandomGenerator();
236 RandomGenerator(Piecewise<D2<SBasis> >f_in, double tol=.1);
237 ~RandomGenerator(){};
238 void setDomain(Piecewise<D2<SBasis> >f_in, double tol=.1);
239 void set_generator(double (*rand_func)());
240 void resetRandomizer();
241 Point pt();
242 double area();
243
244protected:
245 double (*rand)();//set this to your favorite generator of numbers in [0,1] (an inkscape param for instance!)
246 long start_seed;
247 long seed;
248 std::vector<Triangle> triangles;
249 std::vector<double> areas;
250};
251
252RandomGenerator::RandomGenerator(){
253 seed = start_seed = 10;
254 rand = &my_rand_generator;//set this to your favorite generator of numbers in [0,1]!
255}
256RandomGenerator::RandomGenerator(Piecewise<D2<SBasis> >f_in, double tol){
257 seed = start_seed = 10;
258 rand = &my_rand_generator;//set this to your favorite generator of numbers in [0,1]!
259 setDomain(f_in, tol);
260}
261void RandomGenerator::setDomain(Piecewise<D2<SBasis> >f_in, double tol){
262 std::vector<Piecewise<D2<SBasis> > >f = split_at_discontinuities(f_in);
263 std::list<Point> p = toPoly( f, tol);
264 uncross(p);
265 if ( p.size()<3) return;
266 double tot_area = 0;
267 std::list<Point>::iterator a = p.begin(), b=a;
268 ++b;
269 while(b!=p.end()){
270 tot_area += ((*b)[X]-(*a)[X]) * ((*b)[Y]+(*a)[Y])/2;
271 ++a;++b;
272 }
273 bool clockwise = tot_area < 0;
274 triangles = std::vector<Triangle>();
275 triangulate(p,triangles,clockwise);
276 areas = std::vector<double>(triangles.size(),0.);
277 double cumul = 0;
278 for (unsigned i = 0; i<triangles.size(); i++){
279 cumul += triangles[i].area;
280 areas[i] = cumul;
281 }
282}
283
284void RandomGenerator::resetRandomizer(){
285 seed = start_seed;
286}
287Point RandomGenerator::pt(){
288 if (areas.empty()) return Point(0,0);
289 double pick_area = rand()*areas.back();
290 std::vector<double>::iterator picked = std::lower_bound( areas.begin(), areas.end(), pick_area);
291 unsigned i = picked - areas.begin();
292 double x = (*rand)();
293 double y = (*rand)();
294 if ( x+y > 1) {
295 x = 1-x;
296 y = 1-y;
297 }
298 //x=.3; y=.3;
299 Point res;
300 res = triangles[i].a;
301 res += x * ( triangles[i].b - triangles[i].a );
302 res += y * ( triangles[i].c - triangles[i].a );
303 return res;
304}
305double RandomGenerator::area(){
306 if (areas.empty()) return 0;
307 return areas.back();
308}
309void RandomGenerator::set_generator(double (*f)()){
310 rand = f;//set this to your favorite generator of numbers in [0,1]!
311}
312
313
314
315
316
317
318//-------------------------------------------------------
319// The toy!
320//-------------------------------------------------------
321class RandomToy: public Toy {
322
323 PointHandle adjuster[NB_SLIDER];
324
325public:
326 PointSetHandle b1_handle;
327 PointSetHandle b2_handle;
328 void draw(cairo_t *cr,
329 std::ostringstream *notify,
330 int width, int height, bool save, std::ostringstream *timer_stream) override {
331 srand(10);
332 for(unsigned i=0; i<NB_SLIDER; i++){
333 adjuster[i].pos[X] = 30+i*20;
334 if (adjuster[i].pos[Y]<100) adjuster[i].pos[Y] = 100;
335 if (adjuster[i].pos[Y]>400) adjuster[i].pos[Y] = 400;
336 cairo_move_to(cr, Point(30+i*20,100));
337 cairo_line_to(cr, Point(30+i*20,400));
338 cairo_set_line_width (cr, .5);
339 cairo_set_source_rgba (cr, 0., 0., 0., 1);
340 cairo_stroke(cr);
341 }
342 double tol = (400-adjuster[0].pos[Y])/300.*5+0.05;
343 double tau = (400-adjuster[1].pos[Y])/300.;
344// double scale_topback = (250-adjuster[2].pos[Y])/150.*5;
345// double scale_botfront = (250-adjuster[3].pos[Y])/150.*5;
346// double scale_botback = (250-adjuster[4].pos[Y])/150.*5;
347// double growth = 1+(250-adjuster[5].pos[Y])/150.*.1;
348// double rdmness = 1+(400-adjuster[6].pos[Y])/300.*.9;
349// double bend_amount = (250-adjuster[7].pos[Y])/300.*100.;
350
351 b1_handle.pts.back() = b2_handle.pts.front();
352 b1_handle.pts.front() = b2_handle.pts.back();
353 D2<SBasis> B1 = b1_handle.asBezier();
354 D2<SBasis> B2 = b2_handle.asBezier();
355
356 cairo_set_line_width(cr, 0.3);
357 cairo_set_source_rgba(cr, 0, 0, 0, 1);
358 cairo_d2_sb(cr, B1);
359 cairo_d2_sb(cr, B2);
360 cairo_set_line_width (cr, .5);
361 cairo_set_source_rgba (cr, 0., 0., 0., 1);
362 cairo_stroke(cr);
363
364
366 B.concat(Piecewise<D2<SBasis> >(B1));
368
370
371 Point centroid_tmp(0,0);
372 are = integral(dot(B, rot90(derivative(B))))*0.5;
373 are = (are - are.firstValue())*(height/10) / (are.lastValue() - are.firstValue());
374
375 D2<Piecewise<SBasis> > are_graph(Piecewise<SBasis>(Linear(0, width)), are );
376 std::cout << are.firstValue() << "," << are.lastValue() << std::endl;
377 cairo_save(cr);
378 cairo_d2_pw_sb(cr, are_graph);
379 cairo_set_line_width (cr, .5);
380 cairo_set_source_rgba (cr, 0., 0., 0., 1);
381 cairo_stroke(cr);
382 cairo_restore(cr);
383
384
385#if 0
386 std::vector<Piecewise<D2<SBasis> > >f = split_at_discontinuities(B);
387 std::list<Point> p = toPoly( f, tol);
388 uncross(p);
389 cairo_move_to(cr, p.front());
390 for (std::list<Point>::iterator pt = p.begin(); pt!=p.end(); ++pt){
391 cairo_line_to(cr, *pt);
392 //if (i++>p.size()*tau) break;
393 }
394 cairo_set_line_width (cr, 3);
395 cairo_set_source_rgba (cr, 1., 0., 0., .5);
396 cairo_stroke(cr);
397
398 if ( p.size()<3) return;
399 double tot_area = 0;
400 std::list<Point>::iterator a = p.begin(), b=a;
401 b++;
402 while(b!=p.end()){
403 tot_area += ((*b)[X]-(*a)[X]) * ((*b)[Y]+(*a)[Y])/2;
404 a++;b++;
405 }
406 bool clockwise = tot_area < 0;
407
408 std::vector<Triangle> tri;
409 int nbiter =0;
410 triangulate(p,tri,clockwise);
411 cairo_set_source_rgba (cr, 1., 1., 0., 1);
412 cairo_stroke(cr);
413 for (unsigned i=0; i<tri.size(); i++){
414 cairo_move_to(cr, tri[i].a);
415 cairo_line_to(cr, tri[i].b);
416 cairo_line_to(cr, tri[i].c);
417 cairo_line_to(cr, tri[i].a);
418 cairo_set_line_width (cr, .5);
419 cairo_set_source_rgba (cr, 0., 0., .9, .5);
420 cairo_stroke(cr);
421 cairo_move_to(cr, tri[i].a);
422 cairo_line_to(cr, tri[i].b);
423 cairo_line_to(cr, tri[i].c);
424 cairo_line_to(cr, tri[i].a);
425 cairo_set_source_rgba (cr, 0.5, 0., .9, .1);
426 cairo_fill(cr);
427 }
428#endif
429
430 RandomGenerator rdm = RandomGenerator(B, tol);
431 for(int i = 0; i < rdm.area()/5*tau; i++) {
432 draw_handle(cr, rdm.pt());
433 }
434 cairo_set_source_rgba (cr, 0., 0., 0., 1);
435 cairo_stroke(cr);
436
437 Toy::draw(cr, notify, width, height, save,timer_stream);
438 }
439
440public:
441 RandomToy(){
442 for(int i = 0; i < SIZE; i++) {
443 b1_handle.push_back(150+uniform()*300,150+uniform()*300);
444 b2_handle.push_back(150+uniform()*300,150+uniform()*300);
445 }
446 b1_handle.pts[0] = Geom::Point(400,300);
447 b1_handle.pts[1] = Geom::Point(400,400);
448 b1_handle.pts[2] = Geom::Point(100,400);
449 b1_handle.pts[3] = Geom::Point(100,300);
450
451 b2_handle.pts[0] = Geom::Point(100,300);
452 b2_handle.pts[1] = Geom::Point(100,200);
453 b2_handle.pts[2] = Geom::Point(400,200);
454 b2_handle.pts[3] = Geom::Point(400,300);
455 handles.push_back(&b1_handle);
456 handles.push_back(&b2_handle);
457
458 for(unsigned i = 0; i < NB_SLIDER; i++) {
459 adjuster[i].pos = Geom::Point(30+i*20,250);
460 handles.push_back(&(adjuster[i]));
461 }
462 }
463};
464
465int main(int argc, char **argv) {
466 init(argc, argv, new RandomToy);
467 return 0;
468}
469
470/*
471 Local Variables:
472 mode:c++
473 c-file-style:"stroustrup"
474 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
475 indent-tabs-mode:nil
476 fill-column:99
477 End:
478*/
479//vim:filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99:
int main()
Conversion between Bezier control points and SBasis curves.
Geom::IntRect bounds
Definition canvas.cpp:182
static int constexpr SIZE
Definition cielab.cpp:45
Adaptor that creates 2D functions from 1D ones.
Definition d2.h:55
Point at0() const
Definition d2.h:121
Point valueAt(double t) const
Definition d2.h:133
CPoint min() const
Get the corner of the rectangle with smallest coordinate values.
CPoint max() const
Get the corner of the rectangle with largest coordinate values.
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
Function defined as discrete pieces.
Definition piecewise.h:71
void continuousConcat(const Piecewise< T > &other)
Definition piecewise.h:251
output_type lastValue() const
Definition piecewise.h:109
output_type firstValue() const
Definition piecewise.h:106
void concat(const Piecewise< T > &other)
Definition piecewise.h:235
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)
double c[8][4]
Lifts one dimensional objects into 2D.
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
Various utility functions.
Definition affine.h:22
OptInterval bounds_local(Bezier const &b, OptInterval const &i)
Definition bezier.cpp:320
static float sign(double number)
Returns +1 for positive numbers, -1 for negative numbers, and 0 otherwise.
std::vector< Geom::Piecewise< Geom::D2< Geom::SBasis > > > split_at_discontinuities(Geom::Piecewise< Geom::D2< Geom::SBasis > > const &pwsbin, double tol=.0001)
Bezier integral(Bezier const &a)
Definition bezier.cpp:294
Bezier derivative(Bezier const &a)
Definition bezier.cpp:282
Piecewise< SBasis > cross(Piecewise< D2< SBasis > > const &a, Piecewise< D2< SBasis > > const &b)
T dot(D2< T > const &a, D2< T > const &b)
Definition d2.h:355
Point unit_vector(Point const &a)
D2< T > rot90(D2< T > const &a)
Definition d2.h:397
std::vector< Point > intersect(const xAx &C1, const xAx &C2)
Definition conicsec.cpp:361
void cairo_line_to(cairo_t *cr, Geom::Point p1)
struct _cairo cairo_t
Definition path-cairo.h:16
void cairo_d2_pw_sb(cairo_t *cr, Geom::D2< Geom::Piecewise< Geom::SBasis > > const &p)
void draw_handle(cairo_t *cr, Geom::Point h)
void cairo_move_to(cairo_t *cr, Geom::Point p1)
void cairo_d2_sb(cairo_t *cr, Geom::D2< Geom::SBasis > const &p)
void triangulate(std::list< Point > &pts, std::vector< Triangle > &tri, bool clockwise=false, double tol=.001)
Definition rdm-area.cpp:150
void toPoly(D2< SBasis > const &f, std::list< Point > &p, double tol, bool include_first=true)
Definition rdm-area.cpp:26
void uncross(std::list< Point > &loop)
Definition rdm-area.cpp:102
double my_rand_generator()
Definition rdm-area.cpp:228
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)