Subversion Repositories DIN Is Noise

Rev

Rev 2236 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
* fractaliser.cc
* DIN Is Noise is copyright (c) 2006-2025 Jagannathan Sampath
* DIN Is Noise is released under GNU Public License 2.0
* For more information, please visit https://dinisnoise.org/
*/


#include "fractaliser.h"
#include "vector2d.h"
#include "ui_list.h"
#include "console.h"
#include <vector>
#include <fstream>
using namespace std;


fractaliser::fractaliser () : seed ("seed.crv"), ed ("seed.ed"), lib ("seed.lib") {
  name = "Fractaliser";
  ed.add (&seed, &lis);
  ed.attach_library (&lib);
  threshold = 0;
  back_ed = 0;
  load_params ();
}

fractaliser::~fractaliser () {
  seed.save ("seed.crv");
  widget_save ("d_fractaliser", ctrls);
  save_params ();
}

void fractaliser::load_params () {
  ifstream f (make_fname().c_str(), ios::in);
  string ignore;
  f >> ignore >> threshold;
}

void fractaliser::save_params () {
  ofstream f (make_fname().c_str(), ios::out);
  string ignore;
  f << "threshold " << sp_threshold.value << endl;
}

void fractaliser::setup () {

  plugin::setup ();

  widget* _ctrls [] = {&sp_threshold, &cd_seed, &ab_left, &ab_right, &l_seed, &b_edit, &b_mirror, &sp_scale};
  for (int i = 0; i < 8; ++i) ctrls.push_back (_ctrls[i]);
  num_ctrls = ctrls.size ();

  sp_threshold.set ("Threshold", 0.01f, 0, MILLION, 0, 0);
  sp_threshold.set_value (threshold);

  sp_scale.set ("Scale", 0.01f, -MILLION, MILLION, 0, 0);
  sp_scale.set_value (1.0f);

  l_seed.set_text ("Seed");

  b_edit.set_text ("Edit");
  b_mirror.set_text ("Flip");

  cd_seed.crv = &seed;
  cd_seed.unit_bbox ();

  ab_left.set_dir (arrow_button::left);
  ab_right.set_dir (arrow_button::right);
  ab_left.click_repeat = 1;
  ab_right.click_repeat = 1;

  widget_load ("d_fractaliser", ctrls);

  button* btn [] = {&ab_left, &ab_right, &b_edit, &b_mirror};
  for (int i = 0; i < 4; ++i) btn[i]->set_listener (this);

}

void fractaliser::clicked (button& b) {
  if (&b == &ab_left) {
    ed.win.calc ();
    ed.load_curve (-1);
    render ();
  } else if (&b == &ab_right) {
    ed.win.calc ();
    ed.load_curve (+1);
    render ();
  } else if (&b == &b_edit) {
    if (b_edit.text == "Edit") {
      b_edit.set_text ("Go back");
      back_ed = uis.crved;
      uis.set_current (&ed);
    } else {
      uis.set_current (back_ed);
      b_edit.set_text ("Edit");
    }
  } else if (&b == &b_mirror) {
    ed.get_picked_curve ();
    int whole_curve = 1;
    ed.mirror (whole_curve);
  } else plugin::clicked (b);
}

void fractaliser::render () {
  // replace all segments of input curve with seed curve
  //

  curve_editor* ed = uis.crved;
  multi_curve& in = *ed->get_picked_curve (); // apply seed curve on this input curve

  vector<crvpt> in_pts;
  int n = in.get_profile_points (in_pts); // get curve profile
  if (n < 2) {
    cons << RED << "not enough points on curve!" << eol;
    return;
  }
  int m = n - 1;

  points.clear ();
  left_tangents.clear ();
  right_tangents.clear ();

  threshold = sp_threshold.value;

  points_array sv = seed.vertices;
  points_array slt = seed.left_tangents;
  points_array srt = seed.right_tangents;
  int q = sv.size () - 1;
  scale_seed_curve (sv, slt, srt, q+1, sp_scale.value);

  point<float>& rt0 = srt[0];
  point<float>& ltq = slt[q];
  float dx=0, dy=0, pdx=0, pdy=0;
  crvpt vk;
  int subdivided=0;
  for (int i = 0; i < m; ++i) { // replace all segments of input curve with seed curve
    crvpt& vi = in_pts [i];  // ith vertex
    int j = i + 1;
    crvpt& vj = in_pts[j]; // jth vertex
    points.push_back (point<float>(vi.x, vi.y));
    if (i == 0) left_tangents.push_back (point<float>(vi.x, vi.y));
    else {
      if (subdivided)
        left_tangents.push_back (point<float>(vk.x + ltq.x * dx + ltq.y * pdx, vk.y + ltq.x * dy + ltq.y * pdy));
      else
        left_tangents.push_back (point<float>(vi.x, vi.y));
    }
    dx = vj.x - vi.x; dy = vj.y - vi.y; // find vector joining i and j vertex
    pdx = -dy; pdy = dx; // find perpendiular vector (ie y-axis) to vector joining i and j vertices (ie x-axis)
    double length = magnitude (dx, dy); // find length of this vector
    subdivided = 0;
    if (length >= threshold) { // replace vector joining vertices with seed curve
      subdivided = 1;
      right_tangents.push_back (point<float>(vi.x + rt0.x * dx + rt0.y * pdx, vi.y + rt0.x * dy + rt0.y * pdy)); // = right tangent of seed curve's 1st vertex
      for (int p = 1; p < q; ++p) { // go thru the seed curve and replace
        // get a point of seed curve
        point<float>& pt = sv[p];
        point<float>& plt = slt[p];
        point<float>& prt = srt[p];
        // compute output by going along x-axis and y-axis vectors from input
        // using x and y amounts from seed curve
        point<float> ov (vi.x + pt.x * dx + pt.y * pdx, vi.y + pt.x * dy + pt.y * pdy);
        point<float> olt (vi.x + plt.x * dx + plt.y * pdx, vi.y + plt.x * dy + plt.y * pdy);
        point<float> ort (vi.x + prt.x * dx + prt.y * pdx, vi.y + prt.x * dy + prt.y * pdy);
        // add vertex, left and right tangent to output curve
        points.push_back (ov);
        left_tangents.push_back (olt);
        right_tangents.push_back (ort);
      }
    } else right_tangents.push_back (point<float> (vi.x, vi.y));   
    vk = vi;
  }

  crvpt& mv = in_pts[m];
  crvpt& mv1 = in_pts[m-1];
  points.push_back (point<float>(mv.x, mv.y));
  if (subdivided)
    left_tangents.push_back (point<float>(mv1.x + ltq.x * dx + ltq.y * pdx, mv1.y + ltq.x * dy + ltq.y * pdy));
  else
    left_tangents.push_back (point<float>(mv.x, mv.y));
  right_tangents.push_back (point<float>(mv.x, mv.y));

  ss.str("");
  ss << in.name << "_" << seed.name;

}

void fractaliser::draw (curve_editor* ed) {}

void fractaliser::scale_seed_curve (points_array& v, points_array& lt, points_array& rt, int n, float scale) {
  // we only scale in y, because x is normalised from 0 to 1 and cant be changed
  point<float> o; // origin at 0,0
  points_array* pa[3] = {&v, &lt, &rt};
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < 3; ++j) {
      points_array& p = *pa[j];
      point<float>& pi = p[i];
      point<float> dp; direction (dp, o, pi);
      dp.y *= scale;
      pi = o + dp;
    }
  }
}