class MyShape
{
  void render()
  {
  }
}

class Man extends MyShape
{ 
  DMPath facePath;
  DMPath bodyPath;
  DMPath llegPath;
  DMPath rlegPath;
  
  Man()
  {
    // edit the locations here, might be a good idea to grab a piece of graph paper
    facePath = new DMPath();
    facePath.moveToPoint(-25, -25);
    facePath.curveToPoint(-25, -37.5, -12.5, -50, 0, -50);
    facePath.curveToPoint(12.5, -50, 25, -37.5, 25, -25);
    facePath.curveToPoint(25, -12.5, 12.5, 0, 0, 0);
    facePath.curveToPoint(-12.5, 0, -25, -12.5, -25, -25);
    
    bodyPath= new DMPath();
    bodyPath.moveToPoint(0,0);
    bodyPath.curveToPoint(35,10, 35,25,  35,60);
    bodyPath.lineToPoint(35,100);
    bodyPath.lineToPoint(-35,100);
    bodyPath.lineToPoint(-35,60);
    bodyPath.curveToPoint(-35,25, -35,5,  0,0);
    
    
     llegPath= new DMPath();
    llegPath.moveToPoint(-15,100);
    llegPath.lineToPoint(-15,100);
    llegPath.lineToPoint(-15,140);
    
    rlegPath= new DMPath();
    rlegPath.moveToPoint(15,100);
    rlegPath.lineToPoint(15,100);
    rlegPath.lineToPoint(15,140);
    
  }
  
  void render()
  {
    DMPath randomFace = randomizePath(facePath);
    DMPath randomBody = randomizePath(bodyPath);
    DMPath randomLleg = randomizePath(llegPath);
     DMPath randomRleg = randomizePath(rlegPath);
    
    // randomize the face
    strokeWeight(2);
   // stroke(255,0,0);

    randomFace.strokePath();
    strokeWeight(2);
    randomBody.strokePath();
     strokeWeight(2);
    randomRleg.strokePath();
    strokeWeight(2);
    randomLleg.strokePath();
  }
}





class Man2 extends MyShape  
{ 
  DMPath facePath;
  DMPath bodyPath;
  DMPath llegPath;
  DMPath rlegPath;
  
  Man2()
  {
    // edit the locations here, might be a good idea to grab a piece of graph paper
    facePath = new DMPath();
   facePath.moveToPoint(-4, -4);
    facePath.curveToPoint(-4, -7.5, -2.5, -8, 0, -8);
    facePath.curveToPoint(2.5, -8, 4, -7.5, 4, -4);
    facePath.curveToPoint(4, -2.5, 2.5, 0, 0, 0);
    facePath.curveToPoint(-2.5, 0, -4, -2.5, -4, -4);
    
    bodyPath= new DMPath();
    bodyPath.moveToPoint(0,0);
    bodyPath.curveToPoint(5,2, 5,7,  5,11);
    bodyPath.lineToPoint(5,20);
    bodyPath.lineToPoint(-5,20);
    bodyPath.lineToPoint(-5,11);
    bodyPath.curveToPoint(-5,7, -5,2,  0,0);
    
    
     llegPath= new DMPath();
    llegPath.moveToPoint(-2.5,20);
    llegPath.lineToPoint(-2.5,20);
    llegPath.lineToPoint(-2.5,30);
    
    rlegPath= new DMPath();
    rlegPath.moveToPoint(2.5,20);
    rlegPath.lineToPoint(2.5,20);
    rlegPath.lineToPoint(2.5,30);
    
  }
  
  void render()
  {
    DMPath randomFace = randomizePath(facePath);
    DMPath randomBody = randomizePath(bodyPath);
    DMPath randomLleg = randomizePath(llegPath);
     DMPath randomRleg = randomizePath(rlegPath);
    
    // randomize the face
  
    strokeWeight(1);
    randomFace.strokePath();
    strokeWeight(1);
    randomBody.strokePath();
     strokeWeight(1);
    randomRleg.strokePath();
    strokeWeight(1);
    randomLleg.strokePath();
    
  }
}
class MyShape2
{
  void render()
  {
  }
}

class Bldg extends MyShape2
{ 
  DMPath bodyPath;
  DMPath windowPath;
  DMPath window2Path;
   DMPath window3Path;
  DMPath window4Path;
  
  
  Bldg()
  {
    // edit the locations here, might be a good idea to grab a piece of graph paper
    bodyPath = new DMPath();
    bodyPath.lineToPoint(0,10);
     bodyPath.lineToPoint(200,10);
      bodyPath.lineToPoint(300,10);
      bodyPath.lineToPoint(400,10);
      bodyPath.lineToPoint(500,10);
      bodyPath.lineToPoint(600,10);
      bodyPath.lineToPoint(640,10);
      
       bodyPath.lineToPoint(640,100);
      bodyPath.lineToPoint(600,100);
      bodyPath.lineToPoint(500,100);
      bodyPath.lineToPoint(400,100);
      bodyPath.lineToPoint(300,100);
      bodyPath.lineToPoint(200,100);
       bodyPath.lineToPoint(0,100);
       
       bodyPath.lineToPoint(0,200);
     bodyPath.lineToPoint(200,200);
      bodyPath.lineToPoint(300,200);
      bodyPath.lineToPoint(400,200);
      bodyPath.lineToPoint(500,200);
      bodyPath.lineToPoint(600,200);
      bodyPath.lineToPoint(640,200);
      
       bodyPath.lineToPoint(640,300);
      bodyPath.lineToPoint(600,300);
      bodyPath.lineToPoint(500,300);
      bodyPath.lineToPoint(400,300);
      bodyPath.lineToPoint(300,300);
      bodyPath.lineToPoint(200,300);
       bodyPath.lineToPoint(0,300);
       
        bodyPath.lineToPoint(0,400);
     bodyPath.lineToPoint(200,400);
      bodyPath.lineToPoint(300,400);
      bodyPath.lineToPoint(400,400);
      bodyPath.lineToPoint(500,400);
      bodyPath.lineToPoint(600,400);
      bodyPath.lineToPoint(640,400);
      bodyPath.lineToPoint(640,480);
      
   
      
      bodyPath.lineToPoint(540,480);
      bodyPath.lineToPoint(540,300);
      bodyPath.lineToPoint(540,200);
      bodyPath.lineToPoint(540,-10);
      
       bodyPath.lineToPoint(340,-10);
       bodyPath.lineToPoint(340,200);
       bodyPath.lineToPoint(340,300);
       bodyPath.lineToPoint(340,480);
       
       bodyPath.lineToPoint(140,480);
       bodyPath.lineToPoint(140,300);
       bodyPath.lineToPoint(140,200);
       bodyPath.lineToPoint(140,-10);
       
      }
  

  
  void render()
  {
   
    DMPath randomBody = randomizePath(bodyPath);

    // randomize the face
    //stroke(127,127,127);
     strokeWeight(1);
    randomBody.strokePath();
     
 
  }
}


DMPath randomizePath(DMPath aPath)
{
  DMPath randomizedPath = aPath.copy();
  
 // println(randomizedPath.lines);
  for(int i = 0; i < randomizedPath.lines.size(); i++)
  {
    DMLine cline = (DMLine)randomizedPath.lines.get(i);
    
    cline.p0.p.x += random(-2, 2);
    cline.p0.p.y += random(-2, 2);
    //cline.p0.cp.x += random(-5, 5);
    //cline.p0.cp.y += random(-5, 5);
    
    cline.p1.p.x += random(-2, 2);
    cline.p1.p.y += random(-2, 2);
    //cline.p1.cp.x += random(-5, 5);
    //cline.p1.cp.y += random(-5, 5);
  }
  
  return randomizedPath;
}

// a simple vector class
class DMVector
{
  float x, y;
  
  DMVector(float _x, float _y)
  {
    x = _x;
    y = _y;
  }
  
  // vector addition
  DMVector add(DMVector other)
  {
    return new DMVector(x+other.x, y+other.y);
  }
  
  // vector subtraction
  DMVector sub(DMVector other)
  {
    return new DMVector(x-other.x, y-other.y);
  }
  
  // vector multiplication
  DMVector mult(float scalar)
  {
    return new DMVector(x*scalar, y*scalar);
  }
  
  // if the vector close to zero?
  boolean isZero()
  {
    return (x == 0 && y == 0);
  }
  
  // it's important to copying an object like
  // DMVector in assignments to avoid weird
  // side effects
  DMVector copy()
  {
    return new DMVector(x, y);
  }
  
  // by providing a toString method
  // Java can properly provide a description
  // of your object in string concatenations
  String toString()
  {
    return "<" + x + ", " + y + ">";
  }
}

// convenience, returns a zero vector
DMVector ZeroVector()
{
  return new DMVector(0, 0);
}

// a bezier curve point, holds a point and a control point
// a lines are represented as bezier points, regular lines
// simply have start point whose control point matches
// and an end point whose control point mathces
class DMBPoint
{
  DMVector p;
  DMVector cp;
  
  // constructor with two float
  DMBPoint(float x, float y)
  {
    p = new DMVector(x, y);
    cp = p.copy();
  }
  
  // constructor with DMVector
  DMBPoint(DMVector _p)
  {
    p = _p.copy();
    cp = new DMVector(_p.x, _p.y);
  }
  
  // constructor with four floats
  DMBPoint(float x, float y, float cpx, float cpy)
  {
    p = new DMVector(x, y);
    cp = new DMVector(cpx, cpy);
  }
  
  // constructor with 2 DMVectors
  DMBPoint(DMVector _p, DMVector _cp)
  {
    p = _p.copy();
    cp = _cp.copy();
  }
  
  // return true of the control point is different from the point
  boolean hasControlPoint()
  {
    return !(p.sub(cp).isZero());
  }
  
  // it's important to copy an object
  // we don't want weird side effects
  DMBPoint copy()
  {
    return new DMBPoint(p.copy(), cp.copy());
  }
  
  String toString()
  {
    return "(" + p + ", " + cp + ")";
  }
}

// convenience class
class DMLine
{
  DMBPoint p0, p1;
  
  DMLine(DMVector _p0, DMVector _p1)
  {
    p0 = new DMBPoint(_p0);
    p1 = new DMBPoint(_p1);
  }
  
  DMLine(DMBPoint _p0, DMBPoint _p1)
  {
    p0 = _p0.copy();
    p1 = _p1.copy();
  }
  
  DMLine copy()
  {
    return new DMLine(p0, p1);
  }
  
  String toString()
  {
    return "[" + p0 + ", " + p1 + "]";
  }
}

// our path class
class DMPath
{
  DMVector   firstPoint;
  ArrayList  lines;
  
  // simple constructor
  DMPath()
  {
    // set first point to the zero vector
    firstPoint = ZeroVector();
    // create a new ArrayList object
    lines = new ArrayList();
  }
  
  // simple constructor
  DMPath(DMVector _firstPoint, ArrayList _lines)
  {
    // set first point to the zero vector
    firstPoint = _firstPoint.copy();
    
    // copy all the lines
    lines = new ArrayList();
    for(int i = 0; i < _lines.size(); i++)
    {
      lines.add(((DMLine)_lines.get(i)).copy());
    }
  }
  
  // return the last end point on the path
  DMVector getLastPoint()
  {
    int nlines = lines.size();
    
    // if no lines yet, just return first point
    if(nlines == 0)
    {
      return firstPoint;
    }
    else
    {
      // other wise return the point value of the end point
      // of the last line stored
      int lastIndex = nlines - 1;
      return ((DMLine)lines.get(lastIndex)).p1.p;
    }
  }
  
  // you should call this function before calling
  // anything else, otherwise the first point will be (0, 0)
  // once called you should not call this again
  void moveToPoint(float x, float y)
  {
    firstPoint = new DMVector(x, y);
  }
  
  // line the path to the specified point
  void lineToPoint(float x, float y)
  {
    if(firstPoint == null)
    {
      print("Error: No first point set!");
    }
    else
    {
      DMVector p = this.getLastPoint();
      DMVector np = new DMVector(x, y);

      lines.add(new DMLine(p, np));
    }
  }
  
  // add a cubic bezier curve to the path
  void curveToPoint(float cp0x, float cp0y, float cp1x, float cp1y, float p1x, float p1y)
  {
    if(firstPoint == null)
    {
      print("Error: No first point set!");
    }
    else
    {
      // get the last point
      DMVector last = this.getLastPoint();
      DMVector cp0 = new DMVector(cp0x, cp0y);
      DMVector cp1 = new DMVector(cp1x, cp1y);
      DMVector p1 = new DMVector(p1x, p1y);

      lines.add(new DMLine(new DMBPoint(last, cp0), new DMBPoint(p1, cp1)));
    }
  }
  
  // look for any bezier curves and decompose them into the
  // constituent line segments
  ArrayList decompose()
  {
    // we will store all of our real line points here
    ArrayList allPoints = new ArrayList();
    
    // get two points at a time
    for(int i = 0; i < lines.size(); i++)
    {
      DMLine cline = (DMLine)lines.get(i);
      
      // if either point has a control point
      // we need to decompose the bezier
      // into line segments
      if(cline.p0.hasControlPoint() || 
         cline.p1.hasControlPoint())
      {
        // confusing isn't it
        ArrayList bpoints = DMBezier((float)cline.p0.p.x, 
                                     (float)cline.p0.p.y, 
                                     (float)cline.p0.cp.x,
                                     (float)cline.p0.cp.y, 
                                     (float)cline.p1.cp.x, 
                                     (float)cline.p1.cp.y, 
                                     (float)cline.p1.p.x, 
                                     (float)cline.p1.p.y);
        // add all these
        allPoints.addAll(bpoints);
      }
      else
      {
        // otherwise just add the point values
        allPoints.add(cline.p0.p);
        allPoints.add(cline.p1.p);
      }
    }
    
    return allPoints;
  }
  
  // connect the end point to the first point
  // no more commands should be issued to the
  // path object after this
  void close()
  {
    lines.add(new DMLine(this.getLastPoint(), firstPoint));
  }
  
  // render the path
  void strokePath()
  {
    // first decompose all of our paths
    ArrayList renderPoints = this.decompose();
    
    // then render them all
    noFill();
    beginShape();
    for(int i = 0; i < renderPoints.size(); i++)
    {
      DMVector p = (DMVector)renderPoints.get(i);
      vertex(p.x, p.y);
    }
    endShape();
  }
  
  DMPath copy()
  {
    return new DMPath(firstPoint, lines);
  }
  
  void writeToFile(PrintWriter output)
  {
    
  }
}

// Convenience function that returns an ArrayList of the points
// in the bezier curve
ArrayList DMBezier(float p0x, float p0y, float cp0x, float cp0y, float cp1x, float cp1y, float p1x, float p1y)
{
  ArrayList points = new ArrayList();
  
  // B(t) = ((1-t)^3 * P0) + (3t(1-t)^2 * P1) + (3(t^2)(1-t) * P3) + (t^3 * P4)
  float delta = 0.05;
  
  // the starting point
  float cx, cy;
  for(float t = 0; t <= 1.0; t += delta)
  {
    // calc the next point
    cx = (pow(1-t,3)*p0x) + (3*t*pow(1-t,2)*cp0x) + (3*pow(t,2)*(1-t)*cp1x) + (pow(t,3)*p1x); 
    cy = (pow(1-t,3)*p0y) + (3*t*pow(1-t,2)*cp0y) + (3*pow(t,2)*(1-t)*cp1y) + (pow(t,3)*p1y);
    
    points.add(new DMVector(cx, cy));
  }
  
  return points;
}
