/*
 * DrawableRigidBody2D.java
 *
 * modified on 14.12.2004
 */
package JSciDraw.physics;

import JSciDraw.interfaces.*;
import JSciDraw.physics.*;
import JSciDraw.physics.forces.constraints.Circle;
import JSci.maths.*;
import JSci.physics.*;
import java.awt.geom.*;
import java.awt.*;
import java.util.*;

/**
 *
 * @author  Przemek
 */

//tutaj wiezami ruszam sam!!! jeszcze nie ma ruchomych wiezow!!!
public class DrawablePendulum implements Drawable2D, MoveableObject{
    public ClassicalParticleV2D point;
    public ClassicalParticleV2D joint = new ClassicalParticleV2D();
    private Circle con;
    protected final double len;
    public boolean leaveTrace=false;
    public DrawablePendulum nextPendulum;
    public DrawablePendulum parentPendulum;    
    protected boolean isReturning;
    public CycloForce addF = new CycloForce();
    public AbstractDoubleVector addV;
    public ParentForce parentF = new ParentForce();
    public ArrayList additionalForces;
    private class CycloForce extends JSci.physics.Force
    {
       public CycloForce(){           
       }
       public AbstractDoubleVector getVector(double t) {
           AbstractDoubleVector vel=joint.getVelocity();
           AbstractDoubleVector pos=joint.getPosition();
           AbstractDoubleVector tang=joint.constr.getTangent(pos);
           AbstractDoubleVector rad=new Double2Vector(tang.getComponent(1)*-1.,tang.getComponent(0));
           AbstractDoubleVector ret=rad.scalarMultiply(Math.pow((tang.scalarMultiply(tang.scalarProduct(vel)).norm())*joint.getMass()/len,2));
             ret=ret.add(rad.scalarMultiply(rad.scalarProduct(joint.countFreeForce())));
           if (nextPendulum!=null) {
              ret=ret.add(rad.scalarMultiply(rad.scalarProduct(nextPendulum.addV)));
           }
              return ret;
       }
    }
    private class ParentForce extends JSci.physics.Force
    {
       public ParentForce(){           
       }
       public AbstractDoubleVector getVector(double t) {
          
          if (point.onConstraints){
            AbstractDoubleVector tan = point.constr.getTangent(point.getPosition());   
            return tan.scalarMultiply(-1.*tan.scalarProduct(point.getForce())*joint.getMass()/point.getMass());
          } else return point.getForce().scalarMultiply(-1.*joint.getMass()/point.getMass());

       }
    }
    /** Creates a new instance of DrawablePendulum */
    public DrawablePendulum(ClassicalParticleV2D p,double l, double degrees, double m,boolean returning)
     {
        point=p;
        len = l;
        con = new Circle(len,point.getPosition());
        joint.applyConstraints(con);
        joint.setPosition(con.setAngPosition(degrees));
        joint.setMass(m); joint.setDrawRadius(8);
        joint.setVelocity(new Double2Vector(0,0));
        isReturning=returning;
        point.moveableObjects.add(this);
    }
    //only for adding next pendulum
    protected DrawablePendulum(ClassicalParticleV2D p,double l, double degrees, double m,DrawablePendulum par)
    {
        point=p;
        len = l;
        con = new Circle(len,point.getPosition());
        joint.applyConstraints(con);
        joint.setPosition(con.setAngPosition(degrees));
        joint.setMass(m); joint.setDrawRadius(8);
        joint.setVelocity(new Double2Vector(0,0));
        parentPendulum=par;
        isReturning=true;
    }
    
    public void translateBranch(AbstractDoubleVector t)
    {
      if (nextPendulum!=null)
       nextPendulum.translateBranch(t);
      AbstractDoubleVector temp=joint.getPosition();
      joint.setPosition(temp.add(t));
    }
    private void resetConstraints()
    {
      joint.constr.setPosition(point.getPosition());
      if (nextPendulum!=null){
       nextPendulum.resetConstraints();
      }
    }
    private void mv (double dt) {
        if (nextPendulum!=null)
            nextPendulum.mv(dt);
        joint.move2D(dt);
        AbstractDoubleVector move=joint.getPosition().subtract(new Double2Vector(joint.xOl,joint.yOl));
        translateBranch(move);
    }
    public void moveBranch(double dt){
        mv(dt);
        resetConstraints();
    }

    public void draw2D(Graphics2D g2D) {
     g2D.setColor(joint.getColor());
     joint.draw2D(g2D);
     g2D.drawLine((int)point.getPosition().getComponent(0),(int)point.getPosition().getComponent(1),
     (int)joint.getPosition().getComponent(0),(int)joint.getPosition().getComponent(1));
     if (nextPendulum!=null) nextPendulum.draw2D(g2D);
   }

   public void erase2D(Graphics2D g2D) {
        g2D.setColor(Color.white);
        joint.erase2D(g2D);
        g2D.drawLine((int)point.xOl,(int)point.yOl,(int)joint.xOl,(int)joint.yOl);
     if (leaveTrace){
        g2D.setColor(Color.black);
        //pozostaw slad
     }
     if (nextPendulum!=null) nextPendulum.erase2D(g2D);
    }
   public Color getColor() { 
         return joint.getColor(); 
    }
   public void setColor(Color c) { 
        joint.setColor(c); 
    }
   private void calculateDownwards(double dt){
        joint.calculateForces(dt);   
        AbstractDoubleVector temp = parentF.getVector(1);
        joint.addToSumForce(temp);
        if (nextPendulum!=null) 
           nextPendulum.calculateDownwards(dt);
   }
  private void calculateUpwards(double dt){
        if (nextPendulum!=null) 
           nextPendulum.calculateUpwards(dt);
        addV=addF.getVector(1);
        if ((parentPendulum!=null)||(isReturning)) point.addToSumForce(addV);
   }
   public void calculateForces(double dt) {
       //recursing into the list
       calculateDownwards(dt);
       calculateUpwards(dt);
   }
   
   public void addNextPendulum(double l, double begAng, double mass){
       nextPendulum = new DrawablePendulum(joint,l,begAng,mass,this);
   }
   
   public void applyForceField(ForceField p) {
       joint.addExtForce(p.getForce(joint));
       if (nextPendulum!=null) 
           nextPendulum.applyForceField(p);
   }
   
}
