> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://studio.sketchpad.cc/sp/pad/view/ro.lrs9Em$F0xB/rev.698
 * 
 * authors: 
 *   Wolfe

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



//This is meant for java processing
//Overloading functions and java libary work with array doesn't work
//Copy to Processing editor
//uncomment line  //  bez_DrawSPLine(100, color(255, 0, 0), points, 3);
//You will be able to add new points and to see quadratic Bezier curve (not cubic only)
// Maybe once I'll remake it for javascript processing too

// Green - Catmull-Rom; Blue - cubic Bezier

// You can still move the points in javascript processing version but cannot add another

//  cp_DrawSPLine (int segments, color color, PVector[] points, boolean loop) 
// bez_DrawSPLine (int segments, color color, PVector[] points, int params) { //params: 3=quadratic, 4=cubc 

int highlighted = -1;
int locked = -1;
float highlightArea = 30;

// numbers labels
float numAngle= TWO_PI - QUARTER_PI;
float numSpacing = 15;
float numSpacingHL = 30;
float numSpacingLk = 25;

PVector[] points = new PVector[7];

void setup() {
  size(600, 400);
  background(0);
  noSmooth();

  for (int i=0; i<points.length; i++) {
    points[i] = new PVector(random(20, width-20), random(20, height-20));
  }
}


void draw() {
  background(0);

  DrawPoints(color(255, 100), color(200, 90), 10, points);
  DrawNumbers(color(255, 140), numSpacing, points);
  DrawLines(color(255, 50), points);
  cr_DrawSPLine(100, color(0, 200, 0), points, false);
  bez_DrawSPLine(100, color(0, 0, 255), points, 4);
//  bez_DrawSPLine(100, color(255, 0, 0), points, 3);

  highlighted = Check();
  if ((highlighted>=0) && (locked<0))  Highlight(highlighted, false);


  if (locked>=0) { 
    points[locked] = new PVector(mouseX, mouseY);
    Highlight(locked, true);
  }
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////

void DrawNumbers(color clr, float spacing, PVector[] points) {

  pushStyle();
  fill(clr);

  for (int i=0; i<(points.length); i++) {
    if ((i == highlighted) && (locked<0)) {
      //  print(locked +" ");
      spacing = numSpacingHL;
    }

    if  (i == locked) {
      spacing = numSpacingLk;
    }

    if ((i!=highlighted) && (i!=locked)) {
      spacing = numSpacing;
    }

    PVector pos = new PVector(cos(numAngle)*spacing, sin(numAngle)*spacing);
    textAlign(CENTER, CENTER);

    text(i, points[i].x+pos.x, points[i].y+pos.y);
  }

  numAngle += 0.03;
  popStyle();
}

/////////////////////////////////////

void DrawPoints(color strColor, color fillColor, float rad, PVector[] points) {
  pushStyle();
  stroke(strColor);
  fill(fillColor);
  for (int i=0; i<(points.length); i++) {
    ellipse(points[i].x, points[i].y, rad, rad);
  }
  popStyle();
} 

/////////////////////////////////////

void DrawLines(color clr, PVector[] points) {
  pushStyle();
  stroke(clr);
  for (int i=0; i<(points.length); i++) {
    if (i<points.length-1) {
      line(points[i].x, points[i].y, points[i+1].x, points[i+1].y);
    }
  }
  popStyle();
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////


///////////////// CATMULL-ROM CURVE /////////////////////////////////////////////////////////////////////////

PVector CatmullRom (float t, PVector P0, PVector P1, PVector P2, PVector P3) { // cubic
  float t2 = t*t;   // squared 
  float t3 = t2*t;  // cubed 

  float x = 0.5 * ((2*P1.x) + (-P0.x + P2.x)*t + (2*P0.x - 5*P1.x + 4*P2.x -P3.x) * t2 + (-P0.x + 3*P1.x - 3*P2.x + P3.x) * t3);
  float y = 0.5 * ((2*P1.y) + (-P0.y + P2.y)*t + (2*P0.y - 5*P1.y + 4*P2.y -P3.y) * t2 + (-P0.y + 3*P1.y - 3*P2.y + P3.y) * t3);

  return new PVector (x, y);
}

/////////////////////////////////////

void DrawCatmullRom (int segments, color clr, PVector P0, PVector P1, PVector P2, PVector P3) {
  PVector currPos, prevPos;
  pushStyle();

  for (float i=0; i<segments; i++) {
    currPos = CatmullRom(i/segments, P0, P1, P2, P3);
    prevPos = CatmullRom((i-1)/segments, P0, P1, P2, P3);

    stroke(clr);
    line(prevPos.x, prevPos.y, currPos.x, currPos.y);
  }
  popStyle();
}  

/////////////////////////////////////

void cr_DrawSPLine (int segments, color clr, PVector[] splineCR, boolean looped) {
  for (int i=0; i<(splineCR.length-3); i++) {
    DrawCatmullRom(segments, clr, splineCR[i], splineCR[i+1], splineCR[i+2], splineCR[i+3]);
  }
  if (looped) {
    DrawCatmullRom(segments, clr, splineCR[splineCR.length-1], splineCR[0], splineCR[1], splineCR[2]);
    DrawCatmullRom(segments, clr, splineCR[splineCR.length-3], splineCR[splineCR.length-2], splineCR[splineCR.length-1], splineCR[0]);
    DrawCatmullRom(segments, clr, splineCR[splineCR.length-2], splineCR[splineCR.length-1], splineCR[0], splineCR[1]);
  }
}

///////////////// CATMULL-ROM CURVE /////////////////////////////////////////////////////////////////////////



/////////////////// BEZIER CURVE ///////////////////////////////////////////////////////////////////////////



///// QUADRATIC /////

PVector Bezier(float t, PVector P0, PVector P1, PVector P2) {
  float t2 = t*t;   // squared 

  float b1 = 2*t*(1-t); 
  float b2 = (1-t)*(1-t); 

  float x = b2*P0.x  +  b1*P1.x  +  t2*P2.x;
  float y = b2*P0.y  +  b1*P1.y  +  t2*P2.y;

  return new PVector(x, y);
}

///// QUADRATIC /////

///// CUBIC /////
PVector Bezier(float t, PVector P0, PVector P1, PVector P2, PVector P3) {
  float t2 = t*t;   // squared 
  float t3 = t2*t;  // cubed

  float b1 = 3*t*t*(1-t); 
  float b2 = 3*t*(1-t)*(1-t); 
  float b3 = (1-t)*(1-t)*(1-t); 

  float x = b3 * P0.x   +   b2 * P1.x   +   b1 * P2.x   +   t3 * P3.x;
  float y = b3 * P0.y   +   b2 * P1.y   +   b1 * P2.y   +   t3 * P3.y;

  return new PVector(x, y);
}
///// CUBIC /////


//////// QUADRATIC /////////////////////////////

void DrawBezier (int segments, color clr, PVector P0, PVector P1, PVector P2) {
  PVector currPos, prevPos;
  pushStyle();

  for (float i=0; i<segments; i++) {
    currPos = Bezier(i/segments, P0, P1, P2);
    prevPos = Bezier((i-1)/segments, P0, P1, P2);

    stroke(clr);
    line(prevPos.x, prevPos.y, currPos.x, currPos.y);
  }
  popStyle();
}  

//////// QUADRATIC /////////////////////////////

//////// CUBIC /////////////////////////////

void DrawBezier (int segments, color clr, PVector P0, PVector P1, PVector P2, PVector P3) {
  PVector currPos, prevPos;
  pushStyle();

  for (float i=0; i<segments; i++) {
    currPos = Bezier(i/segments, P0, P1, P2, P3);
    prevPos = Bezier((i-1)/segments, P0, P1, P2, P3);

    stroke(clr);
    line(prevPos.x, prevPos.y, currPos.x, currPos.y);
  }
  popStyle();
}  

//////// CUBIC /////////////////////////////



void bez_DrawSPLine (int segments, color clr, PVector[] splineBez, int params) { // 3=quadratic, 4=cubc 

  if (params == 3) {
    for (int i=0; i<splineBez.length; i+=2) { // quadratic
      if (i+2>=splineBez.length) break;
      //DrawBezier(segments, clr, splineBez[i], splineBez[i+1], splineBez[i+2]);
    }
  }

  if (params == 4) {
    for (int i=0; i<splineBez.length; i+=3) { // cubic
      if (i+3>=splineBez.length) break;
      DrawBezier(segments, clr, splineBez[i], splineBez[i+1], splineBez[i+2], splineBez[i+3]);
    }
  }
}



/////////////////// BEZIER CURVE ///////////////////////////////////////////////////////////////////////////

///////////////// CHECKS /////////////

int Check() {

  float actDist;
  float nearest = highlightArea + 1; 
  int nearestIndex = -1; 

  if (locked<0) {
    for (int i=0; i<points.length; i++) {
      PVector mouse = new PVector(mouseX, mouseY);
      actDist= PVector.dist(mouse, points[i]);
      if ((actDist<highlightArea) && (actDist<nearest)) {
        nearestIndex = i;
        nearest = actDist;
      }
    }
  }
  return nearestIndex;
}

///////////////// CHECKS /////////////

///////////////// HIGHLIGHT /////////////
void Highlight(int index, boolean lock) {
  pushStyle();
  colorMode(HSB, 360, 100, 100);
  color stroHL = color (203, 91, 99, 250);
  color fillHL = color (203, 91, 99, 90);
  float radHL  = 35;

  color stroLk = color (359, 91, 99, 250);
  color fillLk = color (359, 91, 99, 90);
  float radLk  = 25;



  color strokeColor;
  color fillColor;
  float radius;

  if (lock) {
    strokeColor = (stroLk);
    fillColor = (fillLk);
    radius = radLk;
  } 
  else {
    strokeColor = (stroHL);
    fillColor = (fillHL);
    radius = radHL;
  }

  stroke(strokeColor);
  fill(fillColor);
  ellipse(points[index].x, points[index].y, radius, radius); 
  popStyle();
}

///////////////// HIGHLIGHT /////////////


////////////////  INPUT  ////////////////////////////////////////////
void mousePressed() {


// only processing - java
  if ((highlighted<0) && (locked<0)) {  
    PVector[] extended = new PVector[points.length+1];
    extended[extended.length-1] = new PVector(mouseX, mouseY);
    System.arraycopy(points, 0, extended, 0, points.length);
    points = extended;
    extended = null;
  }

  if ((highlighted>=0) && (locked<0)) {
    locked = highlighted;
  }
}


void mouseReleased() {
  if (locked>=0) {
    locked = -1;
  }
}


////////////////  INPUT  ////////////////////////////////////////////