//*************************************************************
//**** 2D GEOMETRY CLASSES AND UTILITIES, Jarek Rossignac *****
//****            REVISED October 3, 2008                 *****
//*************************************************************
// Transforms the local CS
void push() {pushMatrix();}                           // saves current LCS (local coordinate system) by pushing matrix stack
void tra(vec V) {translate(V.x,V.y);}                 // translates view by vector V
void tra(pt P) {translate(P.x,P.y);}                  // translates view from origin to P
void traRev(pt P) {translate(-P.x,-P.y);}             // translates view back by vector from P to origin
void rot(float a) {rotate(a);}                        // rotates view around origin by angle a
void rot(float a, pt P) {tra(P); rot(a); traRev(P);}  // rotates view by a around point P
void sca(float s) {scale(s);}                         // scales view wrt origin
void sca(float s, pt P) {tra(P); sca(s); traRev(P);}  // scales view wrt point P
void pop() {popMatrix();}                             // restores previously saved CS by popping stack
// screen and mouse
pt screenCenter() {return(new pt(height/2,height/2));}                                           // returns point at center of screen
boolean mouseIsInWindow() {return(((mouseX>0)&&(mouseX<height)&&(mouseY>0)&&(mouseY<height)));}; // returns true if mouse is in window
pt Mouse() {return(new pt(mouseX,mouseY));};                                                     // returns point with current mouse location
pt pMouse() {return(new pt(pmouseX,pmouseY));};                                                  // returns point with previous mouse location
vec MouseDrag() {return new vec(mouseX-pmouseX,mouseY-pmouseY);};                                // vector representing recent mouse displacement
float angleTracedWithMouse () {vec V1=U(screenCenter(),pMouse()); vec V2=U(screenCenter(),Mouse()); return a(V1,V2);}
float scaleTracedWithMouse () {float d1=d(screenCenter(),pMouse()); float d2=d(screenCenter(),Mouse()); return d2/d1;}
// Make points
pt P(float x, float y) {return new pt(x,y); };                                                       // make point (x,y)
pt P() {return P(0,0); };                                                                            // make point (0,0)
pt P(pt P) {return P(P.x,P.y); };                                                                    // make copy of point P
// Weighted combinations of points
pt A(pt A, pt B) {return P((A.x+B.x)/2.0,(A.y+B.y)/2.0); };                                          // Average: (A+B)/2
pt A(pt A, pt B, pt C) {return P((A.x+B.x+C.x)/3.0,(A.y+B.y+C.y)/3.0); };                            // Average: (A+B+C)/3
pt L(pt A, float s, pt B) {return P(A.x+s*(B.x-A.x),A.y+s*(B.y-A.y)); };                             // Linear interpolation: A+sAB
pt S(float a, pt A, float b, pt B){return P(a*A.x+b*B.x,a*A.y+b*B.y); }                                                         // Weighted sum: aA+bB, make sure that a+b=1
pt S(float a, pt A, float b, pt B, float c, pt C){return P(a*A.x+b*B.x+c*C.x,a*A.y+b*B.y+c*C.y); }                              // Weighted sum: aA+bB+cC, make sure that a+b+c=1
pt S(float a, pt A, float b, pt B, float c, pt C, float d, pt D){return P(a*A.x+b*B.x+c*C.x+d*D.x,a*A.y+b*B.y+c*C.y+d*D.y); }   // Weighted sum: aA+bB+cC+dD, make sure that a+b+c+d=1
vec M(float a, vec U, float b, vec V) {return new vec(a*U.x+b*V.x,a*U.y+b*V.y);}      // aU+bV

// transformed points
pt T(pt P, vec V) {return P(P.x + V.x, P.y + V.y); }                                                 // P+V (transaltes P by displacement vector V
pt T(pt P, float s, vec V) {return T(P,S(s,V)); }                                                    // P+sV (transaltes P by a fraction s of displacement vector V
pt T(pt P, float s, pt Q) {return T(P,s,V(P,Q)); }                                                   // P+sPQ/||PQ|| transaltes P by a fraction s of PQ (towards Q)
pt T(pt O, float x, vec I, float y, vec J) {return P(O.x + x*I.x+ y*J.x, O.y + x*I.y+ y*J.y); }      // O+xI+yJ  (change of coordinate systems)
pt R(pt Q, float a) {float c=cos(a), s=sin(a); return P(c*Q.x-s*Q.y, s*Q.x+c*Q.y); }                 // Rotated Q by a around origin
pt R(pt Q, float a, pt P) {return T(P,R(V(P,Q),a)); }                                                // Rotated Q by a around P
// distance
float d(pt P, pt Q) {return n(V(P,Q));};                                                // ||AB||
float d2(pt P, pt Q) {return n2(V(P,Q));};                                              // ||AB||^2
// Make vectors
vec V(float x, float y) {return new vec(x,y); };                                     // make V(x,y)
vec V() {return V(0,0);}                                                             // V(0,0)
vec V(vec V) {return new vec(V.x,V.y); };                                            // make copy of V
vec V(pt P, pt Q) {return new vec(Q.x-P.x,Q.y-P.y);};                                // PQ (vector from P to Q)
// scale, and combine vectors
vec S(float s,vec V) {return V(s*V.x,s*V.y);};                                                                                        // sV
vec S(vec U, vec V) {return V(U.x+V.x,U.y+V.y); };                                                                                    // U+V
vec S(float a, vec A, float b, vec B){return V(a*A.x+b*B.x,a*A.y+b*B.y); }                                                            // Weighted sum: aA+bB
vec S(float a, vec A, float b, vec B, float c, vec C){return V(a*A.x+b*B.x+c*C.x,a*A.y+b*B.y+c*C.y); }                                // Weighted sum: aA+bB+cC
vec S(float a, vec A, float b, vec B, float c, vec C, float d, vec D){return V(a*A.x+b*B.x+c*C.x+d*D.x,a*A.y+b*B.y+c*C.y+d*D.y); }    // Weighted sum: aA+bB+cC+dD
vec L(vec U,float s,vec V) {return V(U.x+s*(V.x-U.x),U.y+s*(V.y-U.y));};                                                              // (1-s)U+sV
vec A(vec U, vec V) {return L(U,0.5,V); };                                                                                            // (U+V)/2
// dot product, norm, normalization
float dot(vec U, vec V) {return U.x*V.x+U.y*V.y; };                                     // U*V  (dot product)
float n2(vec V) {return sq(V.x)+sq(V.y);};                                              // V*V  (V^2)
float n(vec V) {return sqrt(n2(V));};                                                   // ||V||
vec U(vec V) {float n = n(V); if (n==0) return null; else return S(1/n,V);};            // V/||V||  (normalized V, unit vector)
vec U(pt Q, pt P) {return U(V(Q,P)); };                                                 // Unit PQ
// angles and rotations
float a (vec U, vec V) {float a = atan2(dot(R(U),V),dot(U,V)); if(a>PI) return mPItoPIangle(a-2*PI); if(a<-PI) return mPItoPIangle(a+2*PI); return a; };  // angle(U,V) between -PI and PI
vec R(vec V) {return V(-V.y,V.x);};                                                         // V rotated 90 degrees (clockwise as seen on screen)
vec R(pt Q, pt P) {return R(V(Q,P)); };                                                     // vector QP rotated 90 degrees
vec R(vec U, float a) {float c=cos(a), s=sin(a); return V(c*U.x-s*U.y, s*U.x+c*U.y); };     // U rotated by a
vec R(vec U, float t, vec V) {return S(1+t*(n(V)-n(U))/n(U),R(U,t*angle(U,V)));  };         // interpolation (angle and length) between U and V
// three points
float a(pt A, pt B, pt C) {return  a(V(B,A),V(B,C)); }                                 // angle (BA,BC)
float turnAngle(pt A, pt B, pt C) {return  a(V(A,B),V(B,C)); }                         // angle (AB,BC)
boolean isRightTurn(pt A, pt B, pt C) {return dot( R(A,B),V(B,C)) > 0 ; };             // right turn (as seen on screen)
boolean isRightOf(pt A, pt Q, vec T) {return dot(R(T),V(Q,A)) > 0 ; };                 // A is on right of ray(Q,T) (as seen on screen)
boolean isInFrontOf(pt A, pt Q, vec T) {return dot(T,V(Q,A)) > 0 ; };                  // A is in frontof ray(Q,T) 
// display
void v(pt P) {vertex(P.x,P.y);};                                                     // next point when drawing polygons between beginShape(); and endShape();
void showCross(pt P, float r) {line(P.x-r,P.y,P.x+r,P.y); line(P.x,P.y-r,P.x,P.y+r);};   // shows P as cross of length r
void showCross(pt P) {showCross(P,2);};                                                      // shows P as small cross
void show(pt P, float r) {ellipse(P.x, P.y, 2*r, 2*r);};                             // draws circle of center r around point
void show(pt P) {ellipse(P.x, P.y, 4,4);};                                           // draws small circle around point
void show(pt P, pt Q) {line(P.x,P.y,Q.x,Q.y); };                                     // draws edge (P,Q)
void show(pt P, vec V) {line(P.x,P.y,P.x+V.x,P.y+V.y); }                             // show line from P along V
void show(pt P, float s, vec V) {show(P,S(s,V));}                                    // show line from P along sV
void arrow(pt P, pt Q) {arrow(P,V(P,Q)); }                                           // draws arrow from P to Q
void arrow(pt P, float s, vec V) {arrow(P,S(s,V));}                                   // show arrow from P along sV
void arrow(pt P, vec V) {show(P,V);  float n=n(V); float s=max(min(0.2,20./n),6./n);  // show arrow from P along V
  pt Q=T(P,V); vec U = S(-s,V); vec W = R(S(.3,U)); beginShape(); v(T(T(Q,U),W)); v(Q); v(T(T(Q,U),-1,W)); endShape(CLOSE);}; 

float triArea(pt A, pt B, pt C){
  float a=A.disTo(B);
  float b=B.disTo(C);
  float c=C.disTo(A);
  float s=0.5*(a+b+c);
  float area = sqrt(s*(s-a)*(s-b)*(s-c));
  return area;
}
//************************************************************************
//**** ANGLES
//************************************************************************
float angle(vec V) {return(atan2(V.y,V.x)); };
float angle(vec U, vec V) {return(atan2(dot(R(U),V),dot(U,V))); };
float mPItoPIangle(float a) { if(a>PI) return(mPItoPIangle(a-2*PI)); if(a<-PI) return(mPItoPIangle(a+2*PI)); return(a);};
float toDeg(float a) {return(a*180/PI);}
float toRad(float a) {return(a*PI/180);}
//************************************************************************
//**** POINTS
//************************************************************************
class pt { float x=0,y=0; 
  // CREATE
  pt () {}
  pt (float px, float py) {x = px; y = py;};
  pt (pt P) {x = P.x; y = P.y;};
  pt (pt P, vec V) {x = P.x+V.x; y = P.y+V.y;};
  pt (pt P, float s, vec V) {x = P.x+s*V.x; y = P.y+s*V.y;};
  pt (pt A, float s, pt B) {x = A.x+s*(B.x-A.x); y = A.y+s*(B.y-A.y);};

  // MODIFY
  void setTo(float px, float py) {x = px; y = py;};  
  void setTo(pt P) {x = P.x; y = P.y;}; 
  void setToMouse() { x = mouseX; y = mouseY; }; 
  void trackMouse() { x += mouseX-pmouseX; y += mouseY-pmouseY; }; 
  void translateToTrack(float s, pt P) {setTo(T(P,s,V(P,this)));};
  void track(float s, pt P) {setTo(T(P,s,V(P,this)));};
  void scaleBy(float f) {x*=f; y*=f;};
  void scaleBy(float u, float v) {x*=u; y*=v;};
  void add(vec V) {x += V.x; y += V.y;};   
  void add(float s, vec V) {x += s*V.x; y += s*V.y;};   
  void translateBy(vec V) {x += V.x; y += V.y;};   
  void translateBy(float s, vec V) {x += s*V.x; y += s*V.y;};   
  void translateBy(float u, float v) {x += u; y += v;};
  void translateTowards(float s, pt P) {x+=s*(P.x-x);  y+=s*(P.y-y); };
  void translateTowardsBy(float s, pt P) {vec V = this.makeVecTo(P); V.normalize(); this.translateBy(s,V); };
  void addPt(pt P) {x += P.x; y += P.y;};        // incorrect notation, but useful for computing weighted averages
  void addScaledPt(float s, pt P) {x += s*P.x; y += s*P.y;};        // incorrect notation, but useful for computing weighted averages
  void rotateBy(float a) {float dx=x, dy=y, c=cos(a), s=sin(a); x=c*dx-s*dy; y=+s*dx+c*dy; };     // around origin
  void rotateBy(float a, pt P) {float dx=x-P.x, dy=y-P.y, c=cos(a), s=sin(a); x=P.x+c*dx-s*dy; y=P.y+s*dx+c*dy; };   // around point P
  void rotateBy(float s, float t, pt P) {float dx=x-P.x, dy=y-P.y; dx-=dy*t; dy+=dx*s; dx-=dy*t; x=P.x+dx; y=P.y+dy; };   // s=sin(a); t=tan(a/2);
  void clipToWindow() {x=max(x,0); y=max(y,0); x=min(x,height); y=min(y,height); }
  
  // OUTPUT POINT
  pt clone() {return new pt(x,y); };
  pt makeClone() {return( new pt(x,y)); };
  pt makeTranslatedBy(vec V) {return(new pt(x + V.x, y + V.y));};
  pt makeTranslatedBy(float s, vec V) {return(new pt(x + s*V.x, y + s*V.y));};
  pt makeTransaltedTowards(float s, pt P) {return(new pt(x + s*(P.x-x), y + s*(P.y-y)));};
  pt makeTranslatedBy(float u, float v) {return(new pt(x + u, y + v));};
  pt makeRotatedBy(float a, pt P) {float dx=x-P.x, dy=y-P.y, c=cos(a), s=sin(a); return(new pt(P.x+c*dx-s*dy, P.y+s*dx+c*dy)); };
  pt makeRotatedBy(float a) {float dx=x, dy=y, c=cos(a), s=sin(a); return(new pt(c*dx-s*dy, s*dx+c*dy)); };
  pt makeProjectionOnLine(pt P, pt Q) {float a=dot(P.makeVecTo(this),P.makeVecTo(Q)), b=dot(P.makeVecTo(Q),P.makeVecTo(Q)); return(P.makeTransaltedTowards(a/b,Q)); };
  pt weightedSum(float a, pt A, float b, pt B) {pt P =  A.makeClone(); P.scaleBy(a); P.addScaledPt(b,B); return(P);}
  pt weightedSum(float a, pt A, float b, pt B, float c, pt C) {pt P =  A.makeClone(); P.scaleBy(a); P.addScaledPt(b,B);  P.addScaledPt(c,C); return(P);}
  pt weightedSum(float a, pt A, float b, pt B, float c, pt C, float d, pt D) 
        {pt P =  A.makeClone(); P.scaleBy(a); P.addScaledPt(b,B); P.addScaledPt(c,C); P.addScaledPt(d,D); return(P);}
  pt makeOffset(pt P, pt Q, float r) {
    float a = angle(vecTo(P),vecTo(Q))/2;
    float h = r/tan(a); 
    vec T = vecTo(P); T.normalize(); vec N = T.left();
    pt R = new pt(x,y); R.translateBy(h,T); R.translateBy(r,N);
    return R; };

   // OUTPUT VEC
  vec vecTo(pt P) {return(new vec(P.x-x,P.y-y)); };
  vec makeVecTo(pt P) {return(new vec(P.x-x,P.y-y)); };
  vec makeVecToCenter () {return(new vec(x-height/2.,y-height/2.)); };
  vec makeVecToAverage (pt P, pt Q) {return(new vec((P.x+Q.x)/2.0-x,(P.y+Q.y)/2.0-y)); };
  vec makeVecToAverage (pt P, pt Q, pt R) {return(new vec((P.x+Q.x+R.x)/3.0-x,(P.y+Q.y+R.x)/3.0-y)); };
  vec makeVecToMouse () {return(new vec(mouseX-x,mouseY-y)); };
  vec makeVecToBisectProjection (pt P, pt Q) {float a=disTo(P), b=disTo(Q);  return makeVecTo(L(P,a/(a+b),Q)); };
  vec makeVecToNormalProjection (pt P, pt Q) {float a=dot(V(P,this),V(P,Q)), b=d2(P,Q); return V(this,L(P,a/b,Q)); };
 // vec makeVecTowards(pt P, float d) {vec V = makeVecTo(P); float n = V.norm(); V.normalize(); V.scaleBy(d-n); return V; };
 
  // OUTPUT TEST OR MEASURE

 
  float disTo(pt P) {return(sqrt(sq(P.x-x)+sq(P.y-y))); };
  float disToMouse() {return(sqrt(sq(x-mouseX)+sq(y-mouseY))); };
  boolean isInWindow() {return(((x>0)&&(x<height)&&(y>0)&&(y<height)));};
  boolean projectsBetween(pt P, pt Q) {float a=dot(P.makeVecTo(this),P.makeVecTo(Q)), b=dot(P.makeVecTo(Q),P.makeVecTo(Q)); return((0<a)&&(a<b)); };
  float ratioOfProjectionBetween(pt P, pt Q) {float a=dot(P.makeVecTo(this),P.makeVecTo(Q)), b=dot(P.makeVecTo(Q),P.makeVecTo(Q)); return(a/b); };
  float disToLine(pt P, pt Q) {float a=dot(P.makeVecTo(this),P.makeVecTo(Q).makeUnit().makeTurnedLeft()); return(abs(a)); };
  boolean isLeftOf(pt P, pt Q) {boolean l=dot(P.makeVecTo(this),P.makeVecTo(Q).makeTurnedLeft())>0; return(l);  };
  boolean isInTriangle(pt A, pt B, pt C) { boolean a = this.isLeftOf(B,C); boolean b = this.isLeftOf(C,A); boolean c = this.isLeftOf(A,B); return((a&&b&&c)||(!a&&!b&&!c));};
  boolean isInCircle(pt C, float r) {return d(this,C)<r; }  // returns true if point is in circle C,r
  float disToLineSeg(pt P, pt Q){
    if(this.projectsBetween(P,Q)){
      return(this.disToLine(P,Q));
    }else{
      float a = this.disTo(P); float b = this.disTo(Q);
      return(min(a,b));
    }
  }
      
  
  // DRAW , PRINT
  void show() {ellipse(x, y, height/200, height/200); }; // shows point as small dot
  void show(float r) {ellipse(x, y, 2*r, 2*r); }; // shows point as disk of radius r
  void showCross(float r) {line(x-r,y,x+r,y); line(x,y-r,x,y+r);}; 
  void v() {vertex(x,y);};  // used for drawing polygons between beginShape(); and endShape();
  void write() {println("("+x+","+y+")");};  // writes point coordinates in text window
  void showLabel(String s, vec D) {text(s, x+D.x,y+D.y);  };  // show string displaced by vector D from point
  void showLabel(String s) {text(s, x+5,y+4);  };
  void showLabel(int i) {text(str(i), x+5,y+4);  };  // shows integer number next to point
  void showLabel(String s, float u, float v) {text(s, x+u, y+v);  };
  void showSegmentTo (pt P) {line(x,y,P.x,P.y); }; // draws edge to another point
  void to (pt P) {line(x,y,P.x,P.y); }; // draws edge to another point

  } // end of pt class


//************************************************************************
//**** VECTORS
//************************************************************************
class vec { float x=0,y=0; 
 // CREATE
  vec () {};
  vec (vec V) {x = V.x; y = V.y;};
  vec (float s, vec V) {x = s*V.x; y = s*V.y;};
  vec (float px, float py) {x = px; y = py;};
  vec (pt P, pt Q) {x = Q.x-P.x; y = Q.y-P.y;};
 
 // MODIFY
  void setTo(float px, float py) {x = px; y = py;}; 
  void setTo(pt P, pt Q) {x = Q.x-P.x; y = Q.y-P.y;}; 
  void setTo(vec V) {x = V.x; y = V.y;}; 
  void scaleBy(float f) {x*=f; y*=f;};
  void back() {x=-x; y=-y;};
  void mul(float f) {x*=f; y*=f;};
  void div(float f) {x/=f; y/=f;};
  void scaleBy(float u, float v) {x*=u; y*=v;};
  void normalize() {float n=sqrt(sq(x)+sq(y)); if (n>0.000001) {x/=n; y/=n;};};
  void add(vec V) {x += V.x; y += V.y;};   
  void add(float s, vec V) {x += s*V.x; y += s*V.y;};   
  void add(float u, float v) {x += u; y += v;};
  void turn() {float w=x; x=-y; y=w;};
  void turnLeft() {float w=x; x=-y; y=w;};


  void rotateBy (float a) {float xx=x, yy=y; x=xx*cos(a)-yy*sin(a); y=xx*sin(a)+yy*cos(a); };
  
  // OUTPUT VEC
  vec makeClone() {return(new vec(x,y));}; 
  vec makeUnit() {float n=sqrt(sq(x)+sq(y)); if (n<0.000001) n=1; return(new vec(x/n,y/n));}; 
  vec unit() {float n=sqrt(sq(x)+sq(y)); if (n<0.000001) n=1; return(new vec(x/n,y/n));}; 
  vec makeScaledBy(float s) {return(new vec(x*s, y*s));};
  vec makeTurnedLeft() {return(new vec(-y,x));};
  vec left() {return(new vec(-y,x));};
  vec makeOffsetVec(vec V) {return(new vec(x + V.x, y + V.y));};
  vec makeOffsetVec(float s, vec V) {return(new vec(x + s*V.x, y + s*V.y));};
  vec makeOffsetVec(float u, float v) {return(new vec(x + u, y + v));};
  vec makeRotatedBy(float a) {return(new vec(x*cos(a)-y*sin(a),x*sin(a)+y*cos(a))); };
  vec makeReflectedVec(vec N) { return makeOffsetVec(-2.*dot(this,N),N);};

  // OUTPUT TEST MEASURE
  float norm() {return(sqrt(sq(x)+sq(y)));}
  boolean isNull() {return((abs(x)+abs(y)<0.000001));}
  float angle() {return(atan2(y,x)); }

  // DRAW, PRINT
  void write() {println("("+x+","+y+")");};
  void show (pt P) {line(P.x,P.y,P.x+x,P.y+y); }; 
  void showAt (pt P) {line(P.x,P.y,P.x+x,P.y+y); }; 
  void showArrowAt (pt P) {line(P.x,P.y,P.x+x,P.y+y); 
      float n=min(this.norm()/10.,height/50.); 
      pt Q=P.makeTranslatedBy(this); 
      vec U = this.makeUnit().makeScaledBy(-n);
      vec W = U.makeTurnedLeft().makeScaledBy(0.3);
      beginShape(); Q.makeTranslatedBy(U).makeTranslatedBy(W).v(); Q.v(); 
                    W.scaleBy(-1); Q.makeTranslatedBy(U).makeTranslatedBy(W).v(); endShape(CLOSE); }; 
  void showLabel(String s, pt P) {pt Q = P.makeTranslatedBy(0.5,this); 
           vec N = makeUnit().makeTurnedLeft(); Q.makeTranslatedBy(3,N).showLabel(s); };
  } // end vec class
 
 
 // fitting
 vec proVec (pt B, pt C, pt D) { return L(V(C,B), d(C,B)/(d(C,B)+d(C,D)),V(C,D)); }
 vec vecToCubic (pt A, pt B, pt C, pt D, pt E) {return V( (-A.x+4*B.x-6*C.x+4*D.x-E.x)/6, (-A.y+4*B.y-6*C.y+4*D.y-E.y)/6  );}
// CURVES
void drawCubicBezier(pt A, pt B, pt C, pt D) {bezier(A.x,A.y,B.x,B.y,C.x,C.y,D.x,D.y);}
pt B(pt A, pt B, pt C, float s) {return( L(L(B,s/4.,A),0.5,L(B,s/4.,C))); };                            // returns a tucked B towards its neighbors
pt F(pt A, pt B, pt C, pt D, float s) {return( L(L(A,1.+(1.-s)/8.,B) ,0.5, L(D,1.+(1.-s)/8.,C))); };    // returns a bulged mid-edge point 
pt limit(pt A, pt B, pt C, pt D, pt E, float s, int r) {
  if (r==0) return C.clone();
  else return limit(B(A,B,C,s),F(A,B,C,D,s),B(B,C,D,s),F(B,C,D,E,s),B(C,D,E,s),s,r-1);
  }

