> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://cd3d.sketchpad.cc/sp/pad/view/ro.52tKjQHt9Cl/rev.1
 * 
 * authors: 
 *   Raphael Gaudy

 * license (unless otherwise specified): 
 *   creative commons attribution-share alike 3.0 license.
 *   https://creativecommons.org/licenses/by-sa/3.0/ 
 */ 



/*
This simple program draws a curve by the flow of 
 the circular field
 Pascal Romon 2016-09-30 
 with contribution of Celine Noel for building the curve with the mouse
 */

/*
Start drawing a curve by pressing 'c'; 
 if a curve already eists, it is erased and a new one is created
 Finish by 'c' for a closing the curve
 Flow by 'f' (only if the curve exists)
 */


int vertexSize = 10;  // circle size for drawing vertices
Curve C;
int n;
float tau = 0.1;
boolean pause = false;  // control the mouse during the flow
boolean make_curve = false; // set to true to create the curve
boolean startFlow = false;  // set to true to start the flow
float Lstart = 0;

void setup() {
  frameRate(10);
  size(300, 300);
  background(100);
  C = new Curve();
}

void draw() {
  translate(width/2, height/2);  // sets the origin of the coordinates in the center of the frame

  if (C.vertices.size() > 0) {
    C.drawCurve();
  }

  if (startFlow && !pause) {
    background(100);
    C.drawCurve();

    ArrayList<Vector> flow = new ArrayList<Vector>();
    //flow = badRotationFlow(C);
    flow = badCurvatureFlow(C);
    C.shift(flow);

    float L = C.length();
    ArrayList<Vector> homot = homotFlow(C, Lstart/L);
    C.shift(homot);

    println(L);
  }
}

ArrayList<Vector> badRotationFlow(Curve Ctemp) {
  ArrayList<Vector> ts = new ArrayList<Vector>();
  for (int i = 0; i < Ctemp.vertices.size(); i++) {
    Vector v = new Vector();
    v.x = Ctemp.vertices.get(i).x;
    v.y = Ctemp.vertices.get(i).y;
    v.rot();
    v.mult(tau);
    ts.add(v);
  }

  return(ts);
}

ArrayList<Vector> badCurvatureFlow(Curve Ctemp) {
  ArrayList<Vector> ts = new ArrayList<Vector>();
  int n = Ctemp.vertices.size();
  for (int i = 0; i < n; i++) {
    Vector v = new Vector(Ctemp.vertices.get(i).x, Ctemp.vertices.get(i).y);

    Vector next = new Vector();
    next.x = Ctemp.vertices.get((i+1) % n).x;
    next.y = Ctemp.vertices.get((i+1) % n).y;
    next.sub(v);
    next.normalize();

    Vector previous = new Vector();
    previous.x = Ctemp.vertices.get((i-1 + n) % n).x;
    previous.y = Ctemp.vertices.get((i-1 + n) % n).y;
    previous.sub(v);

    previous.normalize();
    v.x = previous.x + next.x;
    v.y = previous.y + next.y;

    //v.x = 2*(previous.x + next.x)/(previous.norm() + next.norm());
    //v.y = 2*(previous.y + next.y)/(previous.norm() + next.norm());

    v.mult(tau);
    ts.add(v);
  }
  return ts;
}

ArrayList<Vector> homotFlow(Curve Ctemp, float lambda) {
  ArrayList<Vector> ret = new ArrayList<Vector>();
  for (int i = 0; i < Ctemp.vertices.size(); i++) {
    Vector homo_x = new Vector((lambda - 1)*(Ctemp.vertices.get(i).x - 0), 
      (lambda - 1)*(Ctemp.vertices.get(i).y - 0));
    ret.add(homo_x);
  }
  return ret;
}

void mouseClicked() {
  if (make_curve) {
    C.addVertex(mouseX - width / 2, mouseY - height / 2);
    Lstart = C.length();
  }
  if (startFlow) {
    pause = !pause; // during the flow, clicking pause and restarts the process
  }
}

void keyReleased() {
  if (key == 'c') {
    startFlow = false; //interrupts flow
    if (make_curve && C.vertices.size() >= 3) {  // we close the loop
      C.loop = true;
    } else {  // we start a new loop
      background(100);
      C.vertices.clear();
      C.loop = false;
    }
    make_curve = !make_curve;
  }
  if (key == 'f' && make_curve == false) {
    startFlow = true;
  }
}

class Vector {
  float x = 0.0;
  float y = 0.0;

  Vector() {
  }

  Vector(float xi, float yi) {
    x = xi;
    y = yi;
  }

  void homo(Vector origin, float k) {
    x += (k-1)*(x-origin.x) ;
    y += (k-1)*(y-origin.y);
  }

  void add(Vector v) {
    x += v.x;
    y += v.y;
  }
  void sub(Vector v) {
    x-= v.x;
    y-= v.y;
  }

  void mult(float c) {
    x = x*c;
    y = y*c;
  }

  float det(Vector v) {
    return (x * v.y - y *v.x);
  }

  float scal(Vector v) {
    return (x * v.x + y *v.y);
  }

  float norm() {
    return(sqrt((x*x+y*y)));
  }
  float normsq() {
    return((x*x+y*y));
  }
  void normalize() {
    float mag = sqrt ((float)(x*x+y*y));
    x = x/mag;
    y= y/mag;
  }
  void rot() {
    float u, v;
    u=x;
    v=y;
    x= -v;
    y = u;
  }
}

class Curve {
  ArrayList<Vector> vertices = new ArrayList<Vector>();
  boolean loop;  // if true, indicate only n vertices

  void drawCurve() {
    int n = vertices.size();
    Vector current, next;
    //first: lines
    for (int i=0; i < n-1; i++) {
      current = vertices.get(i);
      next = vertices.get(i+1);
      line((float)current.x, (float)current.y, (float)next.x, (float)next.y);
    }
    current = vertices.get(n-1);  // last point
    if (loop) {
      next = vertices.get(0);
      line((float)current.x, (float)current.y, (float)next.x, (float)next.y);
    }
    // second: vertices
    for (int i=0; i < n-1; i++) {
      current = vertices.get(i);
      next = vertices.get(i+1);
      //println(i,current.x, current.y);
      ellipse((float)current.x, (float)current.y, vertexSize, vertexSize);
    }
    current = vertices.get(n-1);  // last point
    ellipse((float)current.x, (float)current.y, vertexSize, vertexSize);
  }

  void addVertex(float xnew, float ynew) { // adds a vertex
    Vector v = new Vector(xnew, ynew);
    vertices.add(v);
  }

  void shift(ArrayList<Vector> shifts) {
    // one should check that the size of both list is the same ...
    //Vector v,s;
    for (int i = 0; i < vertices.size(); i++) {
      Vector v = vertices.get(i);
      Vector s = shifts.get(i);
      //vertices.get(i).add(s);
      v.x += s.x;
      v.y += s.y;
    }
  }

  float length() {
    float ret = 0;
    for (int i = 0; i < vertices.size(); i++) {
      Vector p = vertices.get(i);
      Vector q = vertices.get((i+1) % vertices.size());
      ret += sqrt((p.x - q.x)*(p.x - q.x) + (p.y - q.y)*(p.y - q.y));
    }
    return ret;
  }
}