Subversion Repositories DIN Is Noise

Rev

Rev 2302 | 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) {
2311 jag 144
 
145
  point<float>& vl = vertices[last_vertex];
146
  if (x > vl.x) { // append
147
    add_vertex (x, y);
148
    add_left_tangent (x, y);
149
    add_right_tangent (x, y);
150
    evaluate ();
151
    return 1;
152
  }
153
 
154
  // insert
155
  //
156
 
157
  points_array::iterator \
158
    vter = ++vertices.begin (), \
159
    lter = ++left_tangents.begin (), \
160
    rter = ++right_tangents.begin ();
161
 
964 jag 162
  vector<curve>::iterator cter = curv.begin ();
2311 jag 163
  for (int i = 0, k = last_vertex; i < k; ++i, ++vter, ++lter, ++rter, ++cter) {
964 jag 164
    point<float>& vi = vertices[i];
2311 jag 165
    point<float>& vj = vertices[i + 1];
166
    if ((x >= vi.x) && (x <= vj.x)) {
1605 jag 167
      point<float> v (x, y);
964 jag 168
      vertices.insert (vter, v);
169
      last_vertex = num_vertices++;
1605 jag 170
      left_tangents.insert (lter, v);
171
      right_tangents.insert (rter, v);
2197 jag 172
      (*cter).eval = 1;
964 jag 173
      curv.insert (cter, curve());
2195 jag 174
      ++ncurvs;
964 jag 175
      return 1;
176
    }
177
  }
2311 jag 178
 
964 jag 179
  return 0;
180
}
181
 
182
int multi_curve::remove (int i) {
183
  // remove ith vertex and its tangents
184
  if (num_vertices < 3) return 0;
185
  erase_id (vertices, i);
186
  erase_id (left_tangents, i);
187
  erase_id (right_tangents, i);
2195 jag 188
  if (i == ncurvs) {
964 jag 189
    int j = i - 1;
190
    erase_id (curv, j);
191
  } else {
192
    erase_id (curv, i);
2197 jag 193
    if (--i > -1) curv[i].eval = 1; else curv[0].eval = 1;
964 jag 194
  }
195
  --num_vertices;
2195 jag 196
  --ncurvs;
964 jag 197
  last_vertex = num_vertices - 1;
198
  return 1;
199
}
200
 
201
void multi_curve::set_limit (float d) {
202
  limit = d;
203
  require_eval ();
204
}
205
 
206
void multi_curve::require_eval () {
2197 jag 207
  for (int i = 0; i < ncurvs; ++i) curv[i].eval = 1;
964 jag 208
}
209
 
210
void multi_curve::set_color () {
211
  static const float base = 0.1f;
212
  static rnd<float> rd (base, 1.0f - base);
213
  r = rd (); g = rd (); b = rd ();
214
  calc_tangent_color ();
215
}
216
 
217
void multi_curve::set_color (float rr, float gg, float bb) {
218
  r = rr;
219
  g = gg;
220
  b = bb;
221
  calc_tangent_color ();
222
}
223
 
224
void multi_curve::calc_tangent_color () {
225
  static const float factor = 0.5;
226
  rt = factor * r;
227
  gt = factor * g;
228
  bt = factor * b;
229
}
230
 
231
void multi_curve::evaluate () {
232
  int nevals = 0;
2195 jag 233
  for (int i = 0; i < ncurvs; ++i) {
964 jag 234
    curve& crv = curv[i];
2197 jag 235
    if (crv.eval) {
964 jag 236
      int j = i + 1;
237
      point<float>& v0 = vertices[i];
238
      point<float>& v1 = vertices[j];
239
      point<float>& rt0 = right_tangents[i];
240
      point<float>& lt1 = left_tangents[j];
241
      crv.vertex (0, v0.x, v0.y);
242
      crv.vertex (1, v1.x, v1.y);
243
      crv.tangent (0, rt0.x, rt0.y);
244
      crv.tangent (1, lt1.x, lt1.y);
245
      crv.set_limit (limit);
2191 jag 246
      crv.evaluate (shapeform);
964 jag 247
      ++nevals;
248
    }
249
  }
250
 
251
  if (nevals) {
252
    if (shapeform) {
253
      float total_length = 0.0f;
254
      float now_length = 0.0f;
2195 jag 255
      for (int i = 0; i < ncurvs; ++i) total_length += curv[i].length;
256
      for (int i = 0; i < ncurvs; ++i) curv[i].normalise_length (now_length, total_length);
964 jag 257
      check_shapeform ();
258
    }
259
  }
260
}
261
 
262
void multi_curve::check_shapeform () {
2195 jag 263
  for (int i = 0, j = 1, k = ncurvs - 1; i < k; ++i, ++j) {
964 jag 264
    curve& crvi = curv[i];
265
    curve& crvj = curv[j];
266
    vector<crvpt>& dptsi = crvi.dpts;
267
    vector<crvpt>& dptsj = crvj.dpts;
268
    int last = dptsi.size () - 1;
269
    crvpt& dl = dptsi[last];
270
    crvpt& d0 = dptsj[0];
271
    d0.x = dl.x;
272
    d0.y = dl.y;
273
  }
274
}
275
 
276
void multi_curve::calc_bbox (box<float>& b) {
2195 jag 277
  if (ncurvs) {
964 jag 278
    int num_points = curv[0].vpts.size ();
279
    if (num_points) {
280
      const vector<crvpt>& vpts = curv[0].vpts;
281
      b.left = b.right = vpts[0].x;
282
      b.bottom = b.top = vpts[0].y;
2195 jag 283
      for (int i = 0; i < ncurvs; ++i) {
964 jag 284
        const vector <crvpt>& vpts = curv[i].vpts;
285
        num_points = vpts.size ();
286
        for (int j = 0; j < num_points; ++j) {
287
          const crvpt& p = vpts[j];
288
          if (p.x < b.left) b.left = p.x; else if (p.x > b.right) b.right = p.x;
289
          if (p.y < b.bottom) b.bottom = p.y; else if (p.y > b.top) b.top = p.y;
290
        }
291
      }
292
    }
293
  }
294
  b.calc ();
295
}
296
 
297
 
298
void multi_curve::load (const string& filename) {
299
  ifstream file ((user_data_dir + filename).c_str (), ios::in);
300
  if (!file) {
301
    dlog << "!!! failed to load curve from: " << filename << " !!!" << endl;
302
    return;
303
  }
304
  load (file);
305
  dlog << "+++ loaded curve from: " << filename << " +++" << endl;
306
}
307
 
308
void multi_curve::load (ifstream& file) {
309
 
310
  clear ();
311
 
312
  string ignore;
313
 
314
  file >> ignore >> name;
315
 
316
  int nvertices;
317
  file >> ignore >> nvertices;
318
 
319
  for (int i = 0; i < nvertices; ++i) {
320
 
321
    float x, y;
2208 jag 322
    int p;
323
    file >> ignore >> x >> y >> p;
324
    add_vertex (x, y, p);
325
    file >> ignore >> x >> y >> p;
326
    add_left_tangent (x, y, p);
327
    file >> ignore >> x >> y >> p;
328
    add_right_tangent (x, y, p);
964 jag 329
 
330
  }
331
 
332
  file >> ignore >> limit;
333
 
334
  float ir, ig, ib; file >> ignore >> ir >> ig >> ib;
335
  set_color (ir, ig, ib);
336
 
337
  file >> ignore >> shapeform;
1947 jag 338
  setcentroid (shapeform);
964 jag 339
 
340
  point<float>& lt0 = left_tangents[0];
341
  point<float>& v0 = vertices[0];
342
  int last = nvertices - 1;
343
  point<float>& rtl = right_tangents[last];
344
  point<float>& vl = vertices[last];
345
  lt0.x = v0.x; lt0.y = v0.y;
346
  rtl.x = vl.x; rtl.y = vl.y;
347
 
348
  evaluate ();
349
 
350
}
351
 
352
void multi_curve::save (const string& filename) {
353
  ofstream file ((user_data_dir + filename).c_str (), ios::out);
354
  if (!file) {
355
    dlog << "!!! failed writing curve to: " << filename << endl;
356
    return;
357
  }
358
  save (file);
359
  dlog << "+++ saved curve into: " << filename << " +++" << endl;
360
}
361
 
362
void multi_curve::save (ofstream& file) {
363
  string ignore;
364
  if (name == "") name = "nameless";
365
  file << "name " << name << eol;
366
  file << "num_vertices " << num_vertices << eol;
367
  for (int i = 0; i < num_vertices; ++i) {
2208 jag 368
    pointinfo<float>& v = vertices[i];
369
    pointinfo<float>& lt = left_tangents[i];
370
    pointinfo<float>& rt = right_tangents[i];
2211 jag 371
    file << "v" << i << spc << v.x << spc << v.y << spc << v.pin << eol;
372
    file << "lt" << i << spc << lt.x << spc << lt.y << spc << lt.pin << eol;
373
    file << "rt" << i << spc << rt.x << spc << rt.y << spc << rt.pin << eol;
964 jag 374
  }
375
  file << "limit " << limit << eol;
376
  file << "color " << r << spc << g << spc << b << eol;
377
  file << "shapeform " << shapeform << eol;
378
}
379
 
380
void create_polyline (multi_curve& crv, const points_array& pts) {
381
  int npts = pts.size ();
382
  if (npts < 2) return;
383
  crv.clear (0);
384
  for (int i = 0; i < npts; ++i) {
385
    float xi = pts[i].x, yi = pts[i].y;
386
    crv.add_vertex (xi, yi);
387
    crv.add_left_tangent (xi, yi);
388
    crv.add_right_tangent (xi, yi);
389
  }
390
  crv.evaluate ();
391
}
392
 
393
void convert2_polyline (multi_curve& crv) {
394
  for (int i = 0, j = crv.num_vertices; i < j; ++i) {
395
    point<float>& v = crv.vertices[i];
396
    crv.set_left_tangent (i, v.x, v.y);
397
    crv.set_right_tangent (i, v.x, v.y);
398
  }
399
  crv.evaluate ();
400
}
401
 
402
void convert2_catmull_rom (multi_curve& crv, float tangent_size) {
403
 
404
  int npts = crv.num_vertices;
405
 
406
  if (npts < 2) return;
407
 
408
  int last = npts - 1;
409
  point<float>& p0 = crv.vertices[0];
410
  point<float>& p1 = crv.vertices[1];
411
  point<float>& pl = crv.vertices[last];
412
  point<float>& pl1 = crv.vertices[last-1];
413
 
414
  // set tangents for 1st vertex
415
  float dx, dy; direction (dx, dy, p0.x, p0.y, p1.x, p1.y);
416
  float tx = tangent_size * dx, ty = tangent_size * dy;
417
  crv.set_left_tangent (0, p0.x - tx, p0.y - ty);
418
  crv.set_right_tangent (0, p0.x + tx, p0.y + ty);
419
 
420
  // set tangents for last vertex
421
  direction (dx, dy, pl.x, pl.y, pl1.x, pl1.y);
422
  tx = tangent_size * dx; ty = tangent_size * dy;
423
  crv.set_left_tangent (last, pl.x + tx, pl.y + ty);
424
  crv.set_right_tangent (last, pl.x - tx, pl.y - ty);
425
 
426
  // set left, right tangent for inbetween vertices
427
  for (int i = 1; i < last; ++i) {
428
    int l = i - 1, r = i + 1;
429
    point<float>& pi = crv.vertices[i];
430
    point<float>& pl = crv.vertices[l];
431
    point<float>& pr = crv.vertices[r];
432
    direction (dx, dy, pl.x, pl.y, pr.x, pr.y);
433
    crv.set_left_tangent (i, pi.x - tangent_size * dx, pi.y - tangent_size * dy);
434
    crv.set_right_tangent (i, pi.x + tangent_size * dx, pi.y + tangent_size * dy);
435
  }
436
 
437
  crv.evaluate ();
438
 
439
}
440
 
441
multi_curve* check_list (multi_curve** lst, int n, const string& name) {
442
  for (int m = 0; m < n; ++m) if (lst[m]->name == name) return lst[m];
443
  return 0;
444
}
445
 
446
vector<crvpt>& multi_curve::get_profile_points (int i) {
447
  if (shapeform) return curv[i].dpts; else return curv[i].vpts;
448
}
449
 
1947 jag 450
void multi_curve::setcentroid (int shapeform) {
451
  static const float CX [] = {0.0f, 0.5f};
2178 jag 452
  cen.x = CX [shapeform];
453
  cen.y = 0.0f;
1947 jag 454
}
455
 
964 jag 456
void multi_curve::set_shapeform (int what) {
457
  shapeform = what;
1947 jag 458
  setcentroid (shapeform);
1107 jag 459
  require_eval ();
460
  evaluate ();
964 jag 461
}
462
 
1947 jag 463
void multi_curve::rotate (float angle) {
2185 jag 464
  for (int i = 1; i < num_vertices; ++i) {
2184 jag 465
    pointinfo<float>& lt = left_tangents[i];
2185 jag 466
    if (lt.pin == 0) lt.rotate (cen, angle);
467
  }
468
  for (int i = 0; i < last_vertex; ++i) {
2184 jag 469
    pointinfo<float>& rt = right_tangents[i];
2181 jag 470
    if (rt.pin == 0) rt.rotate (cen, angle);
964 jag 471
  }
2187 jag 472
  for (int i = 0; i < num_vertices; ++i) {
473
    pointinfo<float>& vi = vertices[i];
474
    if (vi.pin == 0) vi.rotate (cen, angle);
475
  }
476
 
477
  point<float>& v0 = vertices[0];
478
  point<float>& lt0 = left_tangents[0];
479
  lt0 = v0;
480
 
481
  point<float>& vl = vertices[last_vertex];
482
  point<float>& rtl = right_tangents[last_vertex];
483
  rtl = vl;
484
 
964 jag 485
  require_eval ();
486
  evaluate ();
2187 jag 487
 
964 jag 488
}
489
 
490
void multi_curve::scale (float sx, float sy) {
491
  for (int i = 0; i < num_vertices; ++i) {
492
    point<float>& vi = vertices[i];
493
    point<float>& lt = left_tangents[i];
494
    point<float>& rt = right_tangents[i];
2178 jag 495
    vi.scale (cen, sx, sy);
496
    lt.scale (cen, sx, sy);
497
    rt.scale (cen, sx, sy);
964 jag 498
  }
499
  require_eval ();
500
  evaluate ();
501
}
502
 
503
void multi_curve::get_xy (double d, float& x, float& y) {
2195 jag 504
  for (int i = 0; i < ncurvs; ++i) {
964 jag 505
    curve& c = curv[i];
506
    vector<crvpt>& vpts = c.vpts;
507
    vector<crvpt>& dpts = c.dpts;
508
    for (int m = 0, n = dpts.size () - 1; m < n; ++m) {
509
      crvpt& dm = dpts[m];
510
      crvpt& dn = dpts[m+1];
511
      crvpt& vm = vpts[m];
512
      crvpt& vn = vpts[m+1];
513
      if (d >= dm.x && d <= dn.x) {
514
        float amt = (d - dm.x) / (dn.x - dm.x);
515
        x = vm.x + amt * (vn.x - vm.x);
516
        y = vm.y + amt * (vn.y - vm.y);
517
        return;
518
      }
519
    }
520
  }
521
}
522
 
523
int multi_curve::get_total_points () {
524
  int total = 0;
2195 jag 525
  for (int i = 0; i < ncurvs; ++i) {
964 jag 526
    vector<crvpt>& vpts = curv[i].vpts;
2133 jag 527
    int ni = vpts.size () - 1;
528
    total = total + ni;
964 jag 529
  }
2133 jag 530
  ++total;
964 jag 531
  return total;
532
}
533
 
2133 jag 534
int multi_curve::get_total_points (vector<int>& crvs) {
2195 jag 535
  int j = ncurvs, j_1 = j - 1;
2133 jag 536
  crvs.reserve (j);
537
  int total = 0;
538
  for (int i = 0; i < j; ++i) {
539
    vector<crvpt>& vpts = curv[i].vpts;
540
    int ni = vpts.size () - 1;
541
    crvs[i] = ni;
542
    total = total + ni;
543
  }
544
  ++crvs[j_1];
545
  ++total;
546
 
547
  return total;
548
}
549
 
2158 jag 550
int multi_curve::get_profile_points (vector<crvpt>& profile) {
2133 jag 551
  vector<int> crvs;
552
  int n = get_total_points (crvs);
553
  profile.reserve (n);
554
  profile.clear ();
2195 jag 555
  for (int i = 0, k = 0; i < ncurvs; ++i) {
964 jag 556
    vector<crvpt>& vpts = curv[i].vpts;
2133 jag 557
    for (int p = 0; p < crvs[i]; ++p) profile[k++] = vpts[p];
964 jag 558
  }
2158 jag 559
  return n;
964 jag 560
}
561
 
2120 jag 562
double multi_curve::get_tangent_mag (int i, points_array& pa) {
563
  point<float>& v = vertices[i];
564
  point<float>& t = pa[i];
565
  return magnitude (v.x, v.y, t.x, t.y);
566
}
567
 
568
double multi_curve::get_left_tangent_mag (int i) {
569
  return get_tangent_mag (i, left_tangents);
570
}
571
 
572
 
573
double multi_curve::get_right_tangent_mag (int i) {
574
  return get_tangent_mag (i, right_tangents);
575
}
2133 jag 576
 
2189 jag 577
void multi_curve::move (float dx, float dy) {
578
  for (int i = 0; i < num_vertices; ++i) {
579
    pointinfo<float>& p = vertices [i];
580
    set_vertex (i, p.x + dx, p.y + dy);
2199 jag 581
    /*if (p.carry == 0) {
2189 jag 582
      pointinfo<float>& l = left_tangents [i];
2198 jag 583
      if (l.pin == 0) set_left_tangent (i, l.x + dx, l.y + dy);
2189 jag 584
      pointinfo<float>& r = right_tangents [i];
2198 jag 585
      if (r.pin == 0) set_right_tangent (i, r.x + dx, r.y + dy);
2199 jag 586
    //}*/
2189 jag 587
  }
588
  require_eval ();
589
  evaluate ();
590
}
591
 
2133 jag 592
extern void make_good_name (string& name);
593
 
594
void multi_curve::set_name (const string& _name) {
595
  name = _name;
596
  make_good_name (name);
597
}
598
 
2178 jag 599
void scale (point<float>& p, point<float>& c, float sx, float sy) {
600
  float px = sx * (p.x - c.x), py = sy * (p.y - c.y);
601
  p.x = c.x + px;
602
  p.y = c.y + py;
603
}