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