1 /*
2 * multi_curve.cc
3 * DIN Is Noise is copyright (c) 2006-2021 Jagannathan Sampath
4 * DIN Is Noise is released under GNU Public License 2.0
5 * For more information, please visit https://dinisnoise.org/
6 */
7 
8 
9 #include "multi_curve.h"
10 #include "vector2d.h"
11 #include "container.h"
12 #include "random.h"
13 #include "log.h"
14 #include <iostream>
15 
16 extern string user_data_dir;
17 static const char eol = '\n';
18 
multi_curve(const string & filename)19 multi_curve::multi_curve (const string& filename) {
20   load (filename);
21 }
22 
multi_curve()23 multi_curve::multi_curve () {
24   clear ();
25 }
26 
clear(int all)27 void multi_curve::clear (int all) {
28 
29   if (all) {
30     name = "nameless";
31     set_color (1, 1, 1);
32   }
33 
34   vertices.clear ();
35 	num_vertices = 0;
36 	last_vertex = -1;
37 
38   left_tangents.clear ();
39   right_tangents.clear ();
40 
41   curv.clear ();
42 
43   limit = 0.001f;
44 
45   shapeform = 0;
46 
47 	centroid_style = CALC;
48 	scx = 0.5f;
49 	scy = 0.0f;
50 	cx = scx;
51 	cy = scy;
52 
53 }
54 
55 //
56 // must call following 3 together with add_vertex called first
57 // to correctly add curve
58 //
add_vertex(float x,float y)59 void multi_curve::add_vertex (float x, float y) {
60 	last_vertex = num_vertices++;
61   vertices.push_back (point<float> (x, y));
62   if (num_vertices > 1) curv.push_back (curve());
63 }
64 
add_left_tangent(float x,float y)65 void multi_curve::add_left_tangent (float x, float y) {
66   left_tangents.push_back (point<float> (x, y));
67 }
68 
add_right_tangent(float x,float y)69 void multi_curve::add_right_tangent (float x, float y) {
70   right_tangents.push_back (point<float> (x, y));
71 }
72 
get_vertex(int i,float & x,float & y)73 void multi_curve::get_vertex (int i, float& x, float& y) {
74   point<float>& v = vertices[i];
75   x = v.x;
76   y = v.y;
77 }
78 
set_vertex(int i,float x,float y,int carry_tangents)79 int multi_curve::set_vertex (int i, float x, float y, int carry_tangents) {
80   point<float>& v = vertices[i];
81   if ((v.x != x) || (v.y != y)) {
82     float dlx, dly, drx, dry;
83     if (carry_tangents) {
84       point<float>& lt = left_tangents[i];
85       point<float>& rt = right_tangents[i];
86       dlx = lt.x - v.x; dly = lt.y - v.y;
87       drx = rt.x - v.x; dry = rt.y - v.y;
88       lt.x = x + dlx; lt.y = y + dly;
89       rt.x = x + drx; rt.y = y + dry;
90     }
91     v.x = x;
92     v.y = y;
93 		if ((unsigned int) i < curv.size ()) curv[i].eval_state = curve::EVAL_REQUIRED;
94 		int i_1 = i - 1; if (i_1 > -1) curv[i_1].eval_state = curve::EVAL_REQUIRED;
95     return 1;
96   } else return 0;
97 }
98 
set_left_tangent(int i,float x,float y)99 int multi_curve::set_left_tangent (int i, float x, float y) {
100   point<float>& lt = left_tangents [i];
101   if ((lt.x != x) || (lt.y != y)) {
102     lt.x = x;
103     lt.y = y;
104     int j = i - 1;
105 		if (j > -1) curv[j].eval_state = curve::EVAL_REQUIRED;
106     return 1;
107   }
108 	return 0;
109 }
110 
set_right_tangent(int i,float x,float y)111 int multi_curve::set_right_tangent (int i, float x, float y) {
112   point<float>& rt = right_tangents[i];
113   if ((rt.x != x) || (rt.y != y)) {
114     rt.x = x;
115     rt.y = y;
116 		if (i < (int) curv.size()) curv[i].eval_state = curve::EVAL_REQUIRED;
117     return 1;
118   }
119 	return 0;
120 }
121 
get_left_tangent(int i,float & x,float & y)122 void multi_curve::get_left_tangent (int i, float& x, float& y) {
123   point<float>& lt = left_tangents [i];
124   x = lt.x;
125   y = lt.y;
126 }
127 
get_right_tangent(int i,float & x,float & y)128 void multi_curve::get_right_tangent (int i, float& x, float& y) {
129   point<float>& rt = right_tangents[i];
130   x = rt.x;
131   y = rt.y;
132 }
133 
insert(float x,float y,float tx,float ty)134 int multi_curve::insert (float x, float y, float tx, float ty) {
135   points_array::iterator vter = ++vertices.begin (), lter = ++left_tangents.begin (), rter = ++right_tangents.begin ();
136   vector<curve>::iterator cter = curv.begin ();
137   for (int i = 0, j = last_vertex; i < j; ++i, ++vter, ++lter, ++rter, ++cter) {
138     point<float>& vi = vertices[i];
139     point<float>& vi1 = vertices[i + 1];
140     if ((x >= vi.x) && (x <= vi1.x)) {
141       /*float ltx, lty; unit_vector (ltx, lty, x, y, vi.x, vi.y);
142       float rtx, rty; unit_vector (rtx, rty, x, y, vi1.x, vi1.y);
143 			point<float> v (x, y), lt (x + tx * ltx, y + ty * lty), rt (x + tx * rtx, y + ty * rty);*/
144       point<float> v (x, y);
145       vertices.insert (vter, v);
146 			last_vertex = num_vertices++;
147       left_tangents.insert (lter, v);
148       right_tangents.insert (rter, v);
149       /*left_tangents.insert (lter, lt);
150       right_tangents.insert (rter, rt);*/
151 			(*cter).eval_state = curve::EVAL_REQUIRED;
152       curv.insert (cter, curve());
153       return 1;
154     }
155   }
156   return 0;
157 }
158 
remove(int i)159 int multi_curve::remove (int i) {
160   // remove ith vertex and its tangents
161   if (num_vertices < 3) return 0;
162   erase_id (vertices, i);
163   erase_id (left_tangents, i);
164   erase_id (right_tangents, i);
165   if (i == (int) curv.size ()) {
166     int j = i - 1;
167     erase_id (curv, j);
168   } else {
169     erase_id (curv, i);
170 		if (--i > -1) curv[i].eval_state = curve::EVAL_REQUIRED; else curv[0].eval_state = curve::EVAL_REQUIRED;
171   }
172 	--num_vertices;
173 	last_vertex = num_vertices - 1;
174   return 1;
175 }
176 
set_limit(float d)177 void multi_curve::set_limit (float d) {
178   limit = d;
179   require_eval ();
180 }
181 
require_eval()182 void multi_curve::require_eval () {
183   for (int i = 0, j = curv.size(); i < j; ++i) curv[i].eval_state = curve::EVAL_REQUIRED;
184 }
185 
set_color()186 void multi_curve::set_color () {
187   static const float base = 0.1f;
188   static rnd<float> rd (base, 1.0f - base);
189   r = rd (); g = rd (); b = rd ();
190   calc_tangent_color ();
191 }
192 
set_color(float rr,float gg,float bb)193 void multi_curve::set_color (float rr, float gg, float bb) {
194   r = rr;
195   g = gg;
196   b = bb;
197   calc_tangent_color ();
198 }
199 
calc_tangent_color()200 void multi_curve::calc_tangent_color () {
201   static const float factor = 0.5;
202   rt = factor * r;
203   gt = factor * g;
204   bt = factor * b;
205 }
206 
evaluate()207 void multi_curve::evaluate () {
208   int nevals = 0;
209 	int ncrvs = curv.size ();
210   for (int i = 0; i < ncrvs; ++i) {
211     curve& crv = curv[i];
212     if (crv.eval_required ()) {
213       int j = i + 1;
214       point<float>& v0 = vertices[i];
215       point<float>& v1 = vertices[j];
216       point<float>& rt0 = right_tangents[i];
217       point<float>& lt1 = left_tangents[j];
218       crv.vertex (0, v0.x, v0.y);
219       crv.vertex (1, v1.x, v1.y);
220       crv.tangent (0, rt0.x, rt0.y);
221       crv.tangent (1, lt1.x, lt1.y);
222       crv.set_limit (limit);
223       crv.eval (shapeform);
224       ++nevals;
225     }
226   }
227 
228   if (nevals) {
229 		if (shapeform) {
230     	float total_length = 0.0f;
231 			float now_length = 0.0f;
232     	for (int i = 0; i < ncrvs; ++i) total_length += curv[i].length;
233 			for (int i = 0; i < ncrvs; ++i) curv[i].normalise_length (now_length, total_length);
234 			check_shapeform ();
235 		}
236   }
237 }
238 
check_shapeform()239 void multi_curve::check_shapeform () {
240 	for (int i = 0, j = 1, k = curv.size () - 1; i < k; ++i, ++j) {
241 		curve& crvi = curv[i];
242 		curve& crvj = curv[j];
243 		vector<crvpt>& dptsi = crvi.dpts;
244 		vector<crvpt>& dptsj = crvj.dpts;
245 		int last = dptsi.size () - 1;
246 		crvpt& dl = dptsi[last];
247 		crvpt& d0 = dptsj[0];
248 		d0.x = dl.x;
249 		d0.y = dl.y;
250 	}
251 }
252 
calc_centroid()253 void multi_curve::calc_centroid () {
254 	if (centroid_style == SET) {
255 		cx = scx;
256 		cy = scy;
257 	} else {
258 		box<float> b; calc_bbox (b);
259 		cx = (b.left + b.right) / 2.;
260 		cy = (b.bottom + b.top) / 2.;
261 	}
262 }
263 
calc_bbox(box<float> & b)264 void multi_curve::calc_bbox (box<float>& b) {
265   int num_curves = curv.size ();
266   if (num_curves) {
267     int num_points = curv[0].vpts.size ();
268     if (num_points) {
269       const vector<crvpt>& vpts = curv[0].vpts;
270       b.left = b.right = vpts[0].x;
271       b.bottom = b.top = vpts[0].y;
272       for (int i = 0; i < num_curves; ++i) {
273         const vector <crvpt>& vpts = curv[i].vpts;
274         num_points = vpts.size ();
275         for (int j = 0; j < num_points; ++j) {
276           const crvpt& p = vpts[j];
277           if (p.x < b.left) b.left = p.x; else if (p.x > b.right) b.right = p.x;
278           if (p.y < b.bottom) b.bottom = p.y; else if (p.y > b.top) b.top = p.y;
279         }
280       }
281     }
282   }
283 	b.calc ();
284 }
285 
286 
load(const string & filename)287 void multi_curve::load (const string& filename) {
288   ifstream file ((user_data_dir + filename).c_str (), ios::in);
289   if (!file) {
290     dlog << "!!! failed to load curve from: " << filename << " !!!" << endl;
291     return;
292   }
293   load (file);
294   dlog << "+++ loaded curve from: " << filename << " +++" << endl;
295 }
296 
load(ifstream & file)297 void multi_curve::load (ifstream& file) {
298 
299   clear ();
300 
301   string ignore;
302 
303   file >> ignore >> name;
304 
305   int nvertices;
306   file >> ignore >> nvertices;
307 
308   for (int i = 0; i < nvertices; ++i) {
309 
310     float x, y;
311     file >> ignore >> x >> y;
312     add_vertex (x, y);
313     file >> ignore >> x >> y;
314     add_left_tangent (x, y);
315     file >> ignore >> x >> y;
316     add_right_tangent (x, y);
317 
318   }
319 
320   file >> ignore >> limit;
321 
322   float ir, ig, ib; file >> ignore >> ir >> ig >> ib;
323   set_color (ir, ig, ib);
324 
325   file >> ignore >> shapeform;
326 
327   point<float>& lt0 = left_tangents[0];
328   point<float>& v0 = vertices[0];
329   int last = nvertices - 1;
330   point<float>& rtl = right_tangents[last];
331   point<float>& vl = vertices[last];
332   lt0.x = v0.x; lt0.y = v0.y;
333   rtl.x = vl.x; rtl.y = vl.y;
334 
335   evaluate ();
336 
337 }
338 
save(const string & filename)339 void multi_curve::save (const string& filename) {
340   ofstream file ((user_data_dir + filename).c_str (), ios::out);
341   if (!file) {
342     dlog << "!!! failed writing curve to: " << filename << endl;
343     return;
344   }
345   save (file);
346   dlog << "+++ saved curve into: " << filename << " +++" << endl;
347 }
348 
save(ofstream & file)349 void multi_curve::save (ofstream& file) {
350   string ignore;
351 	if (name == "") name = "nameless";
352   file << "name " << name << eol;
353   file << "num_vertices " << num_vertices << eol;
354   for (int i = 0; i < num_vertices; ++i) {
355     point<float>& v = vertices[i];
356     point<float>& lt = left_tangents[i];
357     point<float>& rt = right_tangents[i];
358     file << "vertex_" << i << spc << v.x << spc << v.y << eol;
359     file << "left_tangent_" << i << spc << lt.x << spc << lt.y << eol;
360     file << "right_tangent_" << i << spc << rt.x << spc << rt.y << eol;
361   }
362   file << "limit " << limit << eol;
363   file << "color " << r << spc << g << spc << b << eol;
364   file << "shapeform " << shapeform << eol;
365 }
366 
create_polyline(multi_curve & crv,const points_array & pts)367 void create_polyline (multi_curve& crv, const points_array& pts) {
368   int npts = pts.size ();
369   if (npts < 2) return;
370   crv.clear (0);
371   for (int i = 0; i < npts; ++i) {
372     float xi = pts[i].x, yi = pts[i].y;
373     crv.add_vertex (xi, yi);
374     crv.add_left_tangent (xi, yi);
375     crv.add_right_tangent (xi, yi);
376   }
377   crv.evaluate ();
378 }
379 
convert2_polyline(multi_curve & crv)380 void convert2_polyline (multi_curve& crv) {
381   for (int i = 0, j = crv.num_vertices; i < j; ++i) {
382     point<float>& v = crv.vertices[i];
383     crv.set_left_tangent (i, v.x, v.y);
384     crv.set_right_tangent (i, v.x, v.y);
385   }
386   crv.evaluate ();
387 }
388 
convert2_catmull_rom(multi_curve & crv,float tangent_size)389 void convert2_catmull_rom (multi_curve& crv, float tangent_size) {
390 
391   int npts = crv.num_vertices;
392 
393   if (npts < 2) return;
394 
395   int last = npts - 1;
396   point<float>& p0 = crv.vertices[0];
397   point<float>& p1 = crv.vertices[1];
398   point<float>& pl = crv.vertices[last];
399   point<float>& pl1 = crv.vertices[last-1];
400 
401   // set tangents for 1st vertex
402   float dx, dy; direction (dx, dy, p0.x, p0.y, p1.x, p1.y);
403   float tx = tangent_size * dx, ty = tangent_size * dy;
404   crv.set_left_tangent (0, p0.x - tx, p0.y - ty);
405   crv.set_right_tangent (0, p0.x + tx, p0.y + ty);
406 
407   // set tangents for last vertex
408   direction (dx, dy, pl.x, pl.y, pl1.x, pl1.y);
409   tx = tangent_size * dx; ty = tangent_size * dy;
410   crv.set_left_tangent (last, pl.x + tx, pl.y + ty);
411   crv.set_right_tangent (last, pl.x - tx, pl.y - ty);
412 
413   // set left, right tangent for inbetween vertices
414   for (int i = 1; i < last; ++i) {
415     int l = i - 1, r = i + 1;
416     point<float>& pi = crv.vertices[i];
417     point<float>& pl = crv.vertices[l];
418     point<float>& pr = crv.vertices[r];
419     direction (dx, dy, pl.x, pl.y, pr.x, pr.y);
420     crv.set_left_tangent (i, pi.x - tangent_size * dx, pi.y - tangent_size * dy);
421     crv.set_right_tangent (i, pi.x + tangent_size * dx, pi.y + tangent_size * dy);
422   }
423 
424   crv.evaluate ();
425 
426 }
427 
check_list(multi_curve ** lst,int n,const string & name)428 multi_curve* check_list (multi_curve** lst, int n, const string& name) {
429   for (int m = 0; m < n; ++m) if (lst[m]->name == name) return lst[m];
430   return 0;
431 }
432 
get_profile_points(int i)433 vector<crvpt>& multi_curve::get_profile_points (int i) {
434   if (shapeform) return curv[i].dpts; else return curv[i].vpts;
435 }
436 
set_shapeform(int what)437 void multi_curve::set_shapeform (int what) {
438   shapeform = what;
439   require_eval ();
440   evaluate ();
441 }
442 
rotate(float angle)443 void multi_curve::rotate (float angle) { // rotate curve about centroid
444   for (int i = 0; i < num_vertices; ++i) {
445     point<float>& vi = vertices[i];
446     point<float>& lt = left_tangents[i];
447     point<float>& rt = right_tangents[i];
448     rotate (vi, angle);
449     rotate (lt, angle);
450     rotate (rt, angle);
451   }
452   require_eval ();
453   evaluate ();
454 }
455 
rotate(point<float> & p,float angle)456 void multi_curve::rotate (point<float>& p, float angle) { // rotate point about centroid by angle
457   float px = p.x - cx, py = p.y - cy;
458   p.x = px * cos (angle) - py * sin (angle) + cx;
459   p.y = px * sin (angle) + py * cos (angle) + cy;
460 }
461 
scale(float sx,float sy)462 void multi_curve::scale (float sx, float sy) {
463   for (int i = 0; i < num_vertices; ++i) {
464     point<float>& vi = vertices[i];
465     point<float>& lt = left_tangents[i];
466     point<float>& rt = right_tangents[i];
467     scale (vi, sx, sy);
468     scale (lt, sx, sy);
469     scale (rt, sx, sy);
470   }
471   require_eval ();
472   evaluate ();
473 }
474 
scale(point<float> & p,float sx,float sy)475 void multi_curve::scale (point<float>& p, float sx, float sy) {
476   float px = sx * (p.x - cx), py = sy * (p.y - cy);
477   p.x = cx + px;
478   p.y = cy + py;
479 }
480 
get_xy(double d,float & x,float & y)481 void multi_curve::get_xy (double d, float& x, float& y) {
482   for (int i = 0, j = curv.size (); i < j; ++i) {
483     curve& c = curv[i];
484     vector<crvpt>& vpts = c.vpts;
485     vector<crvpt>& dpts = c.dpts;
486     for (int m = 0, n = dpts.size () - 1; m < n; ++m) {
487       crvpt& dm = dpts[m];
488       crvpt& dn = dpts[m+1];
489       crvpt& vm = vpts[m];
490       crvpt& vn = vpts[m+1];
491       if (d >= dm.x && d <= dn.x) {
492         float amt = (d - dm.x) / (dn.x - dm.x);
493         x = vm.x + amt * (vn.x - vm.x);
494         y = vm.y + amt * (vn.y - vm.y);
495         return;
496       }
497     }
498   }
499 }
500 
get_total_points()501 int multi_curve::get_total_points () {
502 	int total = 0;
503   for (int i = 0, j = curv.size(); i < j; ++i) {
504     vector<crvpt>& vpts = curv[i].vpts;
505 		total = total + vpts.size () - 1;
506 	}
507 	total += 1;
508 	return total;
509 }
510 
get_profile_points(vector<crvpt> & profile)511 void multi_curve::get_profile_points (vector<crvpt>& profile) {
512 	profile.clear ();
513   for (int i = 0, j = curv.size(), k = j - 1; i < j; ++i) {
514     vector<crvpt>& vpts = curv[i].vpts;
515 		int q = vpts.size() - 1;
516 		if (i == k) ++q;
517 		for (int p = 0; p < q; ++p) profile.push_back (vpts[p]);
518   }
519 }
520 
521 extern void make_good_name (string& name);
set_name(const string & _name)522 void multi_curve::set_name (const string& _name) {
523 	name = _name;
524 	make_good_name (name);
525 }
526