Subversion Repositories DIN Is Noise

Rev

Rev 2211 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
964 jag 1
/*
2
* multi_curve.cc
2302 jag 3
* DIN Is Noise is copyright (c) 2006-2025 Jagannathan Sampath
1713 jag 4
* DIN Is Noise is released under GNU Public License 2.0
1479 jag 5
* For more information, please visit https://dinisnoise.org/
964 jag 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;
1695 jag 17
static const char eol = '\n';
964 jag 18
 
19
multi_curve::multi_curve (const string& filename) {
20
  load (filename);
21
}
22
 
23
multi_curve::multi_curve () {
24
  clear ();
25
}
26
 
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;
1943 jag 46
 
964 jag 47
}
48
 
49
//
2189 jag 50
// must call following 3 funcs together with add_vertex called first
964 jag 51
// to correctly add curve
52
//
2208 jag 53
void multi_curve::add_vertex (float x, float y, int p) {
964 jag 54
  last_vertex = num_vertices++;
2208 jag 55
  vertices.push_back (pointinfo<float> (x, y, p));
2195 jag 56
  if (num_vertices > 1) {
57
    curv.push_back (curve());
58
    ncurvs = curv.size ();
59
  }
964 jag 60
}
61
 
2208 jag 62
void multi_curve::add_left_tangent (float x, float y, int p) {
63
  left_tangents.push_back (pointinfo<float> (x, y, p));
964 jag 64
}
65
 
2208 jag 66
void multi_curve::add_right_tangent (float x, float y, int p) {
67
  right_tangents.push_back (pointinfo<float> (x, y, p));
964 jag 68
}
69
 
70
void multi_curve::get_vertex (int i, float& x, float& y) {
2208 jag 71
  pointinfo<float>& v = vertices[i];
964 jag 72
  x = v.x;
73
  y = v.y;
74
}
75
 
2184 jag 76
int multi_curve::set_vertex (int i, float x, float y) {
2195 jag 77
 
2184 jag 78
  pointinfo<float>& v = vertices[i];
2195 jag 79
 
964 jag 80
  if ((v.x != x) || (v.y != y)) {
2195 jag 81
 
2208 jag 82
    pointinfo<float>& lt = left_tangents[i];
83
    if (lt.pin == 0) {
84
      float dlx, dly;
85
      dlx = lt.x - v.x;
86
      dly = lt.y - v.y;
87
      lt.x = x + dlx; lt.y = y + dly;
88
    }
89
    pointinfo<float>& rt = right_tangents[i];
90
    if (rt.pin == 0) {
91
      float drx, dry;
92
      drx = rt.x - v.x; dry = rt.y - v.y;
93
      rt.x = x + drx; rt.y = y + dry;
94
    }
2195 jag 95
 
964 jag 96
    v.x = x;
97
    v.y = y;
2195 jag 98
 
2197 jag 99
    if (i < ncurvs) curv[i].eval = 1;
100
    int i_1 = i - 1; if (i_1 > -1) curv[i_1].eval = 1;
2195 jag 101
 
964 jag 102
    return 1;
2195 jag 103
 
964 jag 104
  } else return 0;
2208 jag 105
 
964 jag 106
}
107
 
108
int multi_curve::set_left_tangent (int i, float x, float y) {
109
  point<float>& lt = left_tangents [i];
110
  if ((lt.x != x) || (lt.y != y)) {
111
    lt.x = x;
112
    lt.y = y;
113
    int j = i - 1;
2197 jag 114
    if (j > -1) curv[j].eval = 1;
964 jag 115
    return 1;
116
  }
117
  return 0;
118
}
119
 
120
int multi_curve::set_right_tangent (int i, float x, float y) {
121
  point<float>& rt = right_tangents[i];
122
  if ((rt.x != x) || (rt.y != y)) {
123
    rt.x = x;
124
    rt.y = y;
2197 jag 125
    if (i < ncurvs) curv[i].eval = 1;
964 jag 126
    return 1;
127
  }
128
  return 0;
129
}
130
 
131
void multi_curve::get_left_tangent (int i, float& x, float& y) {
132
  point<float>& lt = left_tangents [i];
133
  x = lt.x;
134
  y = lt.y;
135
}
136
 
137
void multi_curve::get_right_tangent (int i, float& x, float& y) {
138
  point<float>& rt = right_tangents[i];
139
  x = rt.x;
140
  y = rt.y;
141
}
142
 
143
int multi_curve::insert (float x, float y, float tx, float ty) {
144
  points_array::iterator vter = ++vertices.begin (), lter = ++left_tangents.begin (), rter = ++right_tangents.begin ();
145
  vector<curve>::iterator cter = curv.begin ();
146
  for (int i = 0, j = last_vertex; i < j; ++i, ++vter, ++lter, ++rter, ++cter) {
147
    point<float>& vi = vertices[i];
148
    point<float>& vi1 = vertices[i + 1];
149
    if ((x >= vi.x) && (x <= vi1.x)) {
1605 jag 150
      /*float ltx, lty; unit_vector (ltx, lty, x, y, vi.x, vi.y);
964 jag 151
      float rtx, rty; unit_vector (rtx, rty, x, y, vi1.x, vi1.y);
1605 jag 152
      point<float> v (x, y), lt (x + tx * ltx, y + ty * lty), rt (x + tx * rtx, y + ty * rty);*/
153
      point<float> v (x, y);
964 jag 154
      vertices.insert (vter, v);
155
      last_vertex = num_vertices++;
1605 jag 156
      left_tangents.insert (lter, v);
157
      right_tangents.insert (rter, v);
158
      /*left_tangents.insert (lter, lt);
159
      right_tangents.insert (rter, rt);*/
2197 jag 160
      (*cter).eval = 1;
964 jag 161
      curv.insert (cter, curve());
2195 jag 162
      ++ncurvs;
964 jag 163
      return 1;
164
    }
165
  }
166
  return 0;
167
}
168
 
169
int multi_curve::remove (int i) {
170
  // remove ith vertex and its tangents
171
  if (num_vertices < 3) return 0;
172
  erase_id (vertices, i);
173
  erase_id (left_tangents, i);
174
  erase_id (right_tangents, i);
2195 jag 175
  if (i == ncurvs) {
964 jag 176
    int j = i - 1;
177
    erase_id (curv, j);
178
  } else {
179
    erase_id (curv, i);
2197 jag 180
    if (--i > -1) curv[i].eval = 1; else curv[0].eval = 1;
964 jag 181
  }
182
  --num_vertices;
2195 jag 183
  --ncurvs;
964 jag 184
  last_vertex = num_vertices - 1;
185
  return 1;
186
}
187
 
188
void multi_curve::set_limit (float d) {
189
  limit = d;
190
  require_eval ();
191
}
192
 
193
void multi_curve::require_eval () {
2197 jag 194
  for (int i = 0; i < ncurvs; ++i) curv[i].eval = 1;
964 jag 195
}
196
 
197
void multi_curve::set_color () {
198
  static const float base = 0.1f;
199
  static rnd<float> rd (base, 1.0f - base);
200
  r = rd (); g = rd (); b = rd ();
201
  calc_tangent_color ();
202
}
203
 
204
void multi_curve::set_color (float rr, float gg, float bb) {
205
  r = rr;
206
  g = gg;
207
  b = bb;
208
  calc_tangent_color ();
209
}
210
 
211
void multi_curve::calc_tangent_color () {
212
  static const float factor = 0.5;
213
  rt = factor * r;
214
  gt = factor * g;
215
  bt = factor * b;
216
}
217
 
218
void multi_curve::evaluate () {
219
  int nevals = 0;
2195 jag 220
  for (int i = 0; i < ncurvs; ++i) {
964 jag 221
    curve& crv = curv[i];
2197 jag 222
    if (crv.eval) {
964 jag 223
      int j = i + 1;
224
      point<float>& v0 = vertices[i];
225
      point<float>& v1 = vertices[j];
226
      point<float>& rt0 = right_tangents[i];
227
      point<float>& lt1 = left_tangents[j];
228
      crv.vertex (0, v0.x, v0.y);
229
      crv.vertex (1, v1.x, v1.y);
230
      crv.tangent (0, rt0.x, rt0.y);
231
      crv.tangent (1, lt1.x, lt1.y);
232
      crv.set_limit (limit);
2191 jag 233
      crv.evaluate (shapeform);
964 jag 234
      ++nevals;
235
    }
236
  }
237
 
238
  if (nevals) {
239
    if (shapeform) {
240
      float total_length = 0.0f;
241
      float now_length = 0.0f;
2195 jag 242
      for (int i = 0; i < ncurvs; ++i) total_length += curv[i].length;
243
      for (int i = 0; i < ncurvs; ++i) curv[i].normalise_length (now_length, total_length);
964 jag 244
      check_shapeform ();
245
    }
246
  }
247
}
248
 
249
void multi_curve::check_shapeform () {
2195 jag 250
  for (int i = 0, j = 1, k = ncurvs - 1; i < k; ++i, ++j) {
964 jag 251
    curve& crvi = curv[i];
252
    curve& crvj = curv[j];
253
    vector<crvpt>& dptsi = crvi.dpts;
254
    vector<crvpt>& dptsj = crvj.dpts;
255
    int last = dptsi.size () - 1;
256
    crvpt& dl = dptsi[last];
257
    crvpt& d0 = dptsj[0];
258
    d0.x = dl.x;
259
    d0.y = dl.y;
260
  }
261
}
262
 
263
void multi_curve::calc_bbox (box<float>& b) {
2195 jag 264
  if (ncurvs) {
964 jag 265
    int num_points = curv[0].vpts.size ();
266
    if (num_points) {
267
      const vector<crvpt>& vpts = curv[0].vpts;
268
      b.left = b.right = vpts[0].x;
269
      b.bottom = b.top = vpts[0].y;
2195 jag 270
      for (int i = 0; i < ncurvs; ++i) {
964 jag 271
        const vector <crvpt>& vpts = curv[i].vpts;
272
        num_points = vpts.size ();
273
        for (int j = 0; j < num_points; ++j) {
274
          const crvpt& p = vpts[j];
275
          if (p.x < b.left) b.left = p.x; else if (p.x > b.right) b.right = p.x;
276
          if (p.y < b.bottom) b.bottom = p.y; else if (p.y > b.top) b.top = p.y;
277
        }
278
      }
279
    }
280
  }
281
  b.calc ();
282
}
283
 
284
 
285
void multi_curve::load (const string& filename) {
286
  ifstream file ((user_data_dir + filename).c_str (), ios::in);
287
  if (!file) {
288
    dlog << "!!! failed to load curve from: " << filename << " !!!" << endl;
289
    return;
290
  }
291
  load (file);
292
  dlog << "+++ loaded curve from: " << filename << " +++" << endl;
293
}
294
 
295
void multi_curve::load (ifstream& file) {
296
 
297
  clear ();
298
 
299
  string ignore;
300
 
301
  file >> ignore >> name;
302
 
303
  int nvertices;
304
  file >> ignore >> nvertices;
305
 
306
  for (int i = 0; i < nvertices; ++i) {
307
 
308
    float x, y;
2208 jag 309
    int p;
310
    file >> ignore >> x >> y >> p;
311
    add_vertex (x, y, p);
312
    file >> ignore >> x >> y >> p;
313
    add_left_tangent (x, y, p);
314
    file >> ignore >> x >> y >> p;
315
    add_right_tangent (x, y, p);
964 jag 316
 
317
  }
318
 
319
  file >> ignore >> limit;
320
 
321
  float ir, ig, ib; file >> ignore >> ir >> ig >> ib;
322
  set_color (ir, ig, ib);
323
 
324
  file >> ignore >> shapeform;
1947 jag 325
  setcentroid (shapeform);
964 jag 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
 
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
 
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) {
2208 jag 355
    pointinfo<float>& v = vertices[i];
356
    pointinfo<float>& lt = left_tangents[i];
357
    pointinfo<float>& rt = right_tangents[i];
2211 jag 358
    file << "v" << i << spc << v.x << spc << v.y << spc << v.pin << eol;
359
    file << "lt" << i << spc << lt.x << spc << lt.y << spc << lt.pin << eol;
360
    file << "rt" << i << spc << rt.x << spc << rt.y << spc << rt.pin << eol;
964 jag 361
  }
362
  file << "limit " << limit << eol;
363
  file << "color " << r << spc << g << spc << b << eol;
364
  file << "shapeform " << shapeform << eol;
365
}
366
 
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
 
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
 
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
 
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
 
433
vector<crvpt>& multi_curve::get_profile_points (int i) {
434
  if (shapeform) return curv[i].dpts; else return curv[i].vpts;
435
}
436
 
1947 jag 437
void multi_curve::setcentroid (int shapeform) {
438
  static const float CX [] = {0.0f, 0.5f};
2178 jag 439
  cen.x = CX [shapeform];
440
  cen.y = 0.0f;
1947 jag 441
}
442
 
964 jag 443
void multi_curve::set_shapeform (int what) {
444
  shapeform = what;
1947 jag 445
  setcentroid (shapeform);
1107 jag 446
  require_eval ();
447
  evaluate ();
964 jag 448
}
449
 
1947 jag 450
void multi_curve::rotate (float angle) {
2185 jag 451
  for (int i = 1; i < num_vertices; ++i) {
2184 jag 452
    pointinfo<float>& lt = left_tangents[i];
2185 jag 453
    if (lt.pin == 0) lt.rotate (cen, angle);
454
  }
455
  for (int i = 0; i < last_vertex; ++i) {
2184 jag 456
    pointinfo<float>& rt = right_tangents[i];
2181 jag 457
    if (rt.pin == 0) rt.rotate (cen, angle);
964 jag 458
  }
2187 jag 459
  for (int i = 0; i < num_vertices; ++i) {
460
    pointinfo<float>& vi = vertices[i];
461
    if (vi.pin == 0) vi.rotate (cen, angle);
462
  }
463
 
464
  point<float>& v0 = vertices[0];
465
  point<float>& lt0 = left_tangents[0];
466
  lt0 = v0;
467
 
468
  point<float>& vl = vertices[last_vertex];
469
  point<float>& rtl = right_tangents[last_vertex];
470
  rtl = vl;
471
 
964 jag 472
  require_eval ();
473
  evaluate ();
2187 jag 474
 
964 jag 475
}
476
 
477
void multi_curve::scale (float sx, float sy) {
478
  for (int i = 0; i < num_vertices; ++i) {
479
    point<float>& vi = vertices[i];
480
    point<float>& lt = left_tangents[i];
481
    point<float>& rt = right_tangents[i];
2178 jag 482
    vi.scale (cen, sx, sy);
483
    lt.scale (cen, sx, sy);
484
    rt.scale (cen, sx, sy);
964 jag 485
  }
486
  require_eval ();
487
  evaluate ();
488
}
489
 
490
void multi_curve::get_xy (double d, float& x, float& y) {
2195 jag 491
  for (int i = 0; i < ncurvs; ++i) {
964 jag 492
    curve& c = curv[i];
493
    vector<crvpt>& vpts = c.vpts;
494
    vector<crvpt>& dpts = c.dpts;
495
    for (int m = 0, n = dpts.size () - 1; m < n; ++m) {
496
      crvpt& dm = dpts[m];
497
      crvpt& dn = dpts[m+1];
498
      crvpt& vm = vpts[m];
499
      crvpt& vn = vpts[m+1];
500
      if (d >= dm.x && d <= dn.x) {
501
        float amt = (d - dm.x) / (dn.x - dm.x);
502
        x = vm.x + amt * (vn.x - vm.x);
503
        y = vm.y + amt * (vn.y - vm.y);
504
        return;
505
      }
506
    }
507
  }
508
}
509
 
510
int multi_curve::get_total_points () {
511
  int total = 0;
2195 jag 512
  for (int i = 0; i < ncurvs; ++i) {
964 jag 513
    vector<crvpt>& vpts = curv[i].vpts;
2133 jag 514
    int ni = vpts.size () - 1;
515
    total = total + ni;
964 jag 516
  }
2133 jag 517
  ++total;
964 jag 518
  return total;
519
}
520
 
2133 jag 521
int multi_curve::get_total_points (vector<int>& crvs) {
2195 jag 522
  int j = ncurvs, j_1 = j - 1;
2133 jag 523
  crvs.reserve (j);
524
  int total = 0;
525
  for (int i = 0; i < j; ++i) {
526
    vector<crvpt>& vpts = curv[i].vpts;
527
    int ni = vpts.size () - 1;
528
    crvs[i] = ni;
529
    total = total + ni;
530
  }
531
  ++crvs[j_1];
532
  ++total;
533
 
534
  return total;
535
}
536
 
2158 jag 537
int multi_curve::get_profile_points (vector<crvpt>& profile) {
2133 jag 538
  vector<int> crvs;
539
  int n = get_total_points (crvs);
540
  profile.reserve (n);
541
  profile.clear ();
2195 jag 542
  for (int i = 0, k = 0; i < ncurvs; ++i) {
964 jag 543
    vector<crvpt>& vpts = curv[i].vpts;
2133 jag 544
    for (int p = 0; p < crvs[i]; ++p) profile[k++] = vpts[p];
964 jag 545
  }
2158 jag 546
  return n;
964 jag 547
}
548
 
2120 jag 549
double multi_curve::get_tangent_mag (int i, points_array& pa) {
550
  point<float>& v = vertices[i];
551
  point<float>& t = pa[i];
552
  return magnitude (v.x, v.y, t.x, t.y);
553
}
554
 
555
double multi_curve::get_left_tangent_mag (int i) {
556
  return get_tangent_mag (i, left_tangents);
557
}
558
 
559
 
560
double multi_curve::get_right_tangent_mag (int i) {
561
  return get_tangent_mag (i, right_tangents);
562
}
2133 jag 563
 
2189 jag 564
void multi_curve::move (float dx, float dy) {
565
  for (int i = 0; i < num_vertices; ++i) {
566
    pointinfo<float>& p = vertices [i];
567
    set_vertex (i, p.x + dx, p.y + dy);
2199 jag 568
    /*if (p.carry == 0) {
2189 jag 569
      pointinfo<float>& l = left_tangents [i];
2198 jag 570
      if (l.pin == 0) set_left_tangent (i, l.x + dx, l.y + dy);
2189 jag 571
      pointinfo<float>& r = right_tangents [i];
2198 jag 572
      if (r.pin == 0) set_right_tangent (i, r.x + dx, r.y + dy);
2199 jag 573
    //}*/
2189 jag 574
  }
575
  require_eval ();
576
  evaluate ();
577
}
578
 
2133 jag 579
extern void make_good_name (string& name);
580
 
581
void multi_curve::set_name (const string& _name) {
582
  name = _name;
583
  make_good_name (name);
584
}
585
 
2178 jag 586
void scale (point<float>& p, point<float>& c, float sx, float sy) {
587
  float px = sx * (p.x - c.x), py = sy * (p.y - c.y);
588
  p.x = c.x + px;
589
  p.y = c.y + py;
590
}