  function Vector(x, y)
  {
    this.x = x;
    this.y = y;

    this.equal = function(v)
    {
      return this.x == v.getX() && this.y == v.getY();
    }
    this.getX = function()
    {
      return this.x;
    }
    this.getY = function()
    {
      return this.y;
    }
    this.setX = function(x)
    {
      this.x = x;
    }
    this.setY = function(y)
    {
      this.y = y;
    }
    this.addX = function(x)
    {
      this.x += x;
    }
    this.addY = function(y)
    {
      this.y += y;
    }
    this.set = function(v)
    {
      this.x = v.getX();
      this.y = v.getY();
    }
    this.add = function(v)
    {
      this.x += v.getX();
      this.y += v.getY();
    }
    this.sub = function(v)
    {
      this.x -= v.getX();
      this.y -= v.getY();
    }
    this.dotProd = function(v)
    {
      return this.x * v.getX() + this.y * v.getY();
    }
    this.dist = function(v)
    {
      return Math.sqrt((this.x - v.getX()) * (this.x - v.getX()) + (this.y - v.getY()) * (this.y - v.getY()));
    }
    this.length = function()
    {
      return Math.sqrt(this.x * this.x + this.y * this.y);
    }
    this.scale = function(scaleFactor)
    {
      this.x *= scaleFactor;
      this.y *= scaleFactor;
    }
    this.normalize = function()
    {
      var l = this.length();
      this.x /= l;
      this.y /= l;
    }
    this.toString = function()
    {
      return " X: " + this.x + " Y: " + this.y;
    }
  }

  function Environment(x, y, w, h)
  {
    this.left = x;
    this.right = x + w;
    this.top = y;
    this.buttom = y + h;
    this.r = new Vector(0.0, 0.0);

    this.collision = function(curPos, prevPos)
    {
      var collide = false;
      var i;

      if(curPos.getX() < this.left)
      {
        prevPos.setX(curPos.getX());
        curPos.setX(this.left);
        collide = true;
      }
      else if(curPos.getX() > this.right)
      {
        prevPos.setX(curPos.getX());
        curPos.setX(this.right);
        collide = true;
      }
      if(curPos.getY() < this.top)
      {
        prevPos.setY(curPos.getY());
        curPos.setY(this.top);
        collide = true;
      }
      else if(curPos.getY() > this.buttom)
      {
        prevPos.setY(curPos.getY());
        curPos.setY(this.buttom);
        collide = true;
      }
      return collide;
    }
    this.draw = function(ctx, scaleFactor)
    {
      ctx.lineWidth = 2;
      ctx.strokeStyle = '#cccccc';
      ctx.beginPath();
      ctx.moveTo(this.left * scaleFactor, this.top * scaleFactor);
      ctx.lineTo(this.right * scaleFactor, this.top * scaleFactor);
      ctx.lineTo(this.right * scaleFactor, this.buttom * scaleFactor);
      ctx.lineTo(this.left * scaleFactor, this.buttom * scaleFactor);
      ctx.closePath();
      ctx.stroke();
    }
  }

  function PointMass(cx, cy, mass)
  {
    this.origpos = new Vector(cx, cy);
    this.cur = new Vector(cx, cy);
    this.prev = new Vector(cx, cy);
    this.mass = mass;
    this.force = new Vector(0.0, 0.0);
    this.result = new Vector(0.0, 0.0);
    this.friction = 0.01;
    this.isSelected = false;
    this.isAnchor = false;
    this.isPaused = true;

    this.getXPos = function()
    {
      return this.cur.getX();
    }
    this.getYPos = function()
    {
      return this.cur.getY();
    }
    this.getPos = function()
    {
      return this.cur;
    }
    this.getXPrevPos = function()
    {
      return this.prev.getX();
    }
    this.getYPrevPos = function()
    {
      return this.prev.getY();
    }
    this.getPrevPos = function()
    {
      return this.prev;
    }
    this.addXPos = function(dx)
    {
      this.cur.addX(dx);
    }
    this.addYPos = function(dy)
    {
      this.cur.addY(dy);
    }
    this.setForce = function(force)
    {
      this.force.set(force);
    }
    this.addForce = function(force)
    {
      this.force.add(force);
    }
    this.getMass = function()
    {
      return this.mass;
    }
    this.setMass = function(mass)
    {
      this.mass = mass;
    }
    this.setPos = function(x, y)
    {
      this.cur.setX(x);
      this.cur.setY(y);
    }
    this.move = function(dt)
    {
      if(this.isAnchor)
        return;

      var t, a, c, dtdt;

      dtdt = dt * dt;

      a = this.force.getX() / this.mass;
      c = this.cur.getX();
      t = (2.0 - this.friction) * c - (1.0 - this.friction) * this.prev.getX() + a * dtdt;
      this.prev.setX(c);
      this.cur.setX(t);

      a = this.force.getY() / this.mass;
      c = this.cur.getY();
      t = (2.0 - this.friction) * c - (1.0 - this.friction) * this.prev.getY() + a * dtdt;
      this.prev.setY(c);
      this.cur.setY(t);
    }
    this.setFriction = function(friction)
    {
      this.friction = friction;
    }
    this.getVelocity = function()
    {
      var cXpX, cYpY;

      cXpX = this.cur.getX() - this.prev.getX();
      cYpY = this.cur.getY() - this.prev.getY();

      return cXpX * cXpX + cYpY * cYpY;
    }
    this.draw = function(ctx, scaleFactor)
    {
      ctx.lineWidth = 5;
      ctx.fillStyle = '#000000';
      ctx.strokeStyle = '#000000';
      ctx.beginPath();
      ctx.arc(this.cur.getX() * scaleFactor,
              this.cur.getY() * scaleFactor,
              6.0, 0.0, Math.PI * 2.0, true);
      ctx.fill();

      if(this.isSelected)
      {
        ctx.lineWidth = 1;
        ctx.strokeStyle = '#ff0000';
        ctx.beginPath();
        ctx.arc(this.cur.getX() * scaleFactor,
                this.cur.getY() * scaleFactor,
                8.0, 0.0, Math.PI * 2.0, true);
        ctx.stroke();
      }
      if(this.isAnchor && this.isPaused)
      {
        ctx.lineWidth = 1;
        ctx.fillStyle = '#ff0000';
        ctx.beginPath();
        ctx.arc(this.cur.getX() * scaleFactor,
                this.cur.getY() * scaleFactor,
                2.0, 0.0, Math.PI * 2.0, true);
        ctx.fill();
      }
    }
    this.select = function()
    {
      this.isSelected = true;
    }
    this.unselect = function()
    {
      this.isSelected = false;
    }
    this.moveToOrigPos = function()
    {
      this.cur.setX(this.origpos.getX());
      this.cur.setY(this.origpos.getY());
      this.prev.setX(this.origpos.getX());
      this.prev.setY(this.origpos.getY());
    }
    this.toggleAnchor = function()
    {
      this.isAnchor = !this.isAnchor;
    }
    this.getAnchor = function()
    {
      return this.isAnchor;
    }
    this.setPaused = function(paused)
    {
      this.isPaused = paused;
    }
  }

  // this is what is refered to as a constraint in the article
  // it's a bit 'optimized' in comparssion with the artcile code
  // and contain some additional features but does essentialy the same
  function Joint(pointMassA, pointMassB, shortConst, longConst)
  {
    this.pointMassA = pointMassA;
    this.pointMassB = pointMassB;
    this.delta = new Vector(0.0, 0.0);
    this.pointMassAPos = pointMassA.getPos();
    this.pointMassBPos = pointMassB.getPos();
    this.drawState = 1;
    this.prevDrawState = 1;
    this.isVisible = true;
    this.isPaused = true;
    this.isSelectable = false;

    this.delta.set(this.pointMassBPos);
    this.delta.sub(this.pointMassAPos);

    this.shortConst = this.delta.length() * shortConst;
    this.longConst = this.delta.length() * longConst;
    this.scSquared = this.shortConst * this.shortConst;
    this.lcSquared = this.longConst * this.longConst;

    this.middle = new Vector();
    this.t = new Vector();
    this.k = new Vector();

    this.setDist = function(shortConst, longConst)
    {
      this.shortConst = shortConst;
      this.longConst = longConst;
      this.scSquared = this.shortConst * this.shortConst;
      this.lcSquared = this.longConst * this.longConst;
    }
    this.scale = function(scaleFactor)
    {
      this.shortConst = this.shortConst * scaleFactor;
      this.longConst = this.longConst * scaleFactor;
      this.scSquared = this.shortConst * this.shortConst;
      this.lcSquared = this.longConst * this.longConst;
    }
    this.sc = function()
    {
      this.delta.set(this.pointMassBPos);
      this.delta.sub(this.pointMassAPos);

      var dp = this.delta.dotProd(this.delta);
      if(this.scSquared != 0.0 && dp < this.scSquared)
      {
        var scaleFactor;
        scaleFactor = this.scSquared / (dp + this.scSquared) - 0.5;
        this.delta.scale(scaleFactor);
        if(!this.pointMassA.getAnchor())
          this.pointMassAPos.sub(this.delta);
        if(!this.pointMassB.getAnchor())
          this.pointMassBPos.add(this.delta);
      }
      else if(this.lcSquared != 0.0 && dp > this.lcSquared)
      {
        var scaleFactor;
        scaleFactor = this.lcSquared / (dp + this.lcSquared) - 0.5;
        this.delta.scale(scaleFactor);
        if(!this.pointMassA.getAnchor())
          this.pointMassAPos.sub(this.delta);
        if(!this.pointMassB.getAnchor())
          this.pointMassBPos.add(this.delta);
      }
    }
    this.draw = function(ctx, scaleFactor)
    {
      if(!this.isPaused && !this.isVisible)
        return;

      ctx.lineWidth = 2;
      if(this.isPaused && this.isVisible)
        ctx.strokeStyle = '#000000';
      if(!this.isPaused && this.isVisible)
        ctx.strokeStyle = '#000000';
      if(this.isPaused && !this.isVisible)
        ctx.strokeStyle = '#999999';
      ctx.beginPath();
      ctx.moveTo(this.pointMassA.getPos().getX() * scaleFactor, this.pointMassA.getPos().getY() * scaleFactor);
      ctx.lineTo(this.pointMassB.getPos().getX() * scaleFactor, this.pointMassB.getPos().getY() * scaleFactor);
      ctx.stroke();

      if(this.isSelectable)
      {
        this.middle = this.getMiddle();

        ctx.lineWidth = 5;
        ctx.fillStyle = '#ff0000';
        ctx.strokeStyle = '#ff0000';
        ctx.beginPath();
        ctx.arc(this.middle.getX() * scaleFactor,
                this.middle.getY() * scaleFactor,
                4.0, 0.0, Math.PI * 2.0, true);
        ctx.fill();
      }

      /*
      else if(this.drawState == 2) // full tjosan mode
      {
        this.middle = this.getMiddle();

        this.t.set(this.middle);
        this.t.sub(this.pointMassA.getPos());

        ctx.lineWidth = 2;

        // long const bar
        this.t.normalize();
        this.t.scale(this.longConst * 0.5);
        var t1 = this.middle.getX() + this.t.getX();
        var t2 = this.middle.getY() + this.t.getY();
        var t3 = this.middle.getX() - this.t.getX();
        var t4 = this.middle.getY() - this.t.getY();

        ctx.strokeStyle = '#aaaaaa';
        ctx.beginPath();
        ctx.moveTo(t1 * scaleFactor, t2 * scaleFactor);
        ctx.lineTo(t3 * scaleFactor, t4 * scaleFactor);
        ctx.stroke();

        this.k.set(this.t);
        this.k.normalize();
        this.k.scale(0.03);
        var x = -this.k.getY();
        var y = this.k.getX();
        ctx.strokeStyle = '#0000ff';
        ctx.beginPath();
        ctx.moveTo((t1 + x) * scaleFactor, (t2 + y) * scaleFactor);
        ctx.lineTo((t1 - x) * scaleFactor, (t2 - y) * scaleFactor);
        ctx.stroke();
        ctx.beginPath();
        ctx.moveTo((t3 + x) * scaleFactor, (t4 + y) * scaleFactor);
        ctx.lineTo((t3 - x) * scaleFactor, (t4 - y) * scaleFactor);
        ctx.stroke();

        // short const bar
        this.t.normalize();
        this.t.scale(this.shortConst * 0.5);

        t1 = this.middle.getX() + this.t.getX();
        t2 = this.middle.getY() + this.t.getY();
        t3 = this.middle.getX() - this.t.getX();
        t4 = this.middle.getY() - this.t.getY();

        ctx.strokeStyle = '#ff0000';
        ctx.beginPath();
        ctx.moveTo((t1 + x) * scaleFactor, (t2 + y) * scaleFactor);
        ctx.lineTo((t1 - x) * scaleFactor, (t2 - y) * scaleFactor);
        ctx.stroke();
        ctx.beginPath();
        ctx.moveTo((t3 + x) * scaleFactor, (t4 + y) * scaleFactor);
        ctx.lineTo((t3 - x) * scaleFactor, (t4 - y) * scaleFactor);
        ctx.stroke();
      }
      else if(this.drawState == 3) // delete mode
      {
        ctx.lineWidth = 2;
        if(this.isVisible)
          ctx.strokeStyle = '#000000';
        else
          ctx.strokeStyle = '#999999';
        ctx.beginPath();
        ctx.moveTo(this.pointMassA.getPos().getX() * scaleFactor, this.pointMassA.getPos().getY() * scaleFactor);
        ctx.lineTo(this.pointMassB.getPos().getX() * scaleFactor, this.pointMassB.getPos().getY() * scaleFactor);
        ctx.stroke();

        this.middle = this.getMiddle();

        ctx.lineWidth = 5;
        ctx.fillStyle = '#ff0000';
        ctx.strokeStyle = '#ff0000';
        ctx.beginPath();
        ctx.arc(this.middle.getX() * scaleFactor,
                this.middle.getY() * scaleFactor,
                4.0, 0.0, Math.PI * 2.0, true);
        ctx.fill();
      }
      */
    }
    /*
    this.setDrawState = function(drawState)
    {
      this.prevDrawState = this.drawState;
      this.drawState = drawState;
    }
    this.setDrawStatePrevious = function()
    {
      this.drawState = this.prevDrawState;
    }
    */
    /*
    this.hide = function()
    {
      this.isVisible = false;
    }
    this.show = function()
    {
      this.isVisible = true;
    }
    */
    this.setVisible = function(visible)
    {
      this.isVisible = visible;
    }
    this.getVisibility = function()
    {
      return this.isVisible;
    }
    this.setPaused = function(paused)
    {
      this.isPaused = paused;
    }
    this.setSelectable = function(selectable)
    {
      this.isSelectable = selectable;
    }
    this.getMiddle = function()
    {
        this.middle.set(this.pointMassA.getPos());
        this.t.set(this.pointMassB.getPos());
        this.t.sub(this.middle);
        this.t.scale(0.5);
        this.middle.add(this.t);

        return this.middle;
    }
    this.getPointmassA = function()
    {
      return this.pointMassA;
    }
    this.getPointmassB = function()
    {
      return this.pointMassB;
    }
  }

