Sector.java

package com.github.celldynamics.quimp.plugin.ecmm;

import java.util.ArrayList;

import com.github.celldynamics.quimp.Outline;
import com.github.celldynamics.quimp.Vert;
import com.github.celldynamics.quimp.geom.ExtendedVector2d;

import ij.IJ;
import ij.process.FloatPolygon;

/**
 * A mapping between nodes?.
 * 
 * @author rtyson
 *
 */
class Sector {

  private int id;
  private Vert startO1;
  private Vert startO2;
  public Outline migCharges;
  public Outline tarCharges;
  FloatPolygon chargesPoly;
  FloatPolygon innerPoly;
  FloatPolygon outerPoly; // if no intersects have to use these
  public double lengthO1;
  public double lengthO2;
  public int vertSo1;
  public int vertSo2; // num verts in 01 and o2
  public boolean forwardMap; // mapping forward or reverse?
  /**
   * the cell expanding here. is segment T to the left or right of segment T+1.
   */
  public boolean expansion;
  /**
   * the other direction of the normals of migration charges.
   */
  public double outerNormal;

  /**
   * Constructor of sector.
   * 
   * @param i id
   */
  public Sector(int i) {
    id = i;
  }

  /**
   * Print Sector.
   */
  public void print() {
    IJ.log("Sector " + id + "\nMig charges: ");
    migCharges.print();
    IJ.log("");

    IJ.log("tar charges: ");
    tarCharges.print();
  }

  /**
   * Construct.
   */
  public void construct() {
    // calc lengths, determin expansion, set charges
    calcLengths();
    // left or right? Use the "left" algorithm (sign of triangle area)
    double sectorTriArea = ExtendedVector2d.triangleArea(startO1.getPoint(),
            startO1.getNext().getPoint(), startO2.getNext().getPoint());

    if ((lengthO1 > lengthO2) || ECMp.forceForwardMapping) {
      forwardMap = true;
      migCharges = formCharges(startO1);
      tarCharges = formCharges(startO2);
      if (sectorTriArea > 0) {
        expansion = true; //
        outerNormal = -1.;
      } else {
        expansion = false; //
        outerNormal = 1.;
      }
    } else {
      forwardMap = false; // backward in time
      migCharges = formCharges(startO2);//
      tarCharges = formCharges(startO1);
      if (sectorTriArea > 0) {
        expansion = true; //
        outerNormal = 1.;

      } else {
        expansion = false; //
        outerNormal = -1.;
      }
    }

    Vert v = migCharges.getHead();
    ExtendedVector2d normal;
    do {
      normal = new ExtendedVector2d(v.getNormal().getX(), v.getNormal().getY());
      normal.multiply(outerNormal * ECMp.w);
      v.getPoint().addVec(normal);
      v = v.getNext();
    } while (!v.isHead());

    if (ECMp.chargeDensity != -1) {
      migCharges.setResolution(ECMp.chargeDensity);
      tarCharges.setResolution(ECMp.chargeDensity);
    }

    // create polygon off all charges for cal point inside/outside sector
    chargesPolygon();

  }

  /**
   * constructWhole.
   * 
   * @param area1 area1
   * @param area2 area2
   */
  public void constructWhole(double area1, double area2) {
    Outline innerCharges;
    Outline outerCharges;

    calcLengths();

    if (((lengthO1 > lengthO2) || ECMp.forceForwardMapping || ECMp.ANA)
            && !ECMp.forceBackwardMapping) {
      forwardMap = true;
      migCharges = formCharges(startO1);
      tarCharges = formCharges(startO2);
      if (area1 > area2) {
        expansion = false; // n is migrating from outside in
        outerNormal = 1.;
        innerCharges = tarCharges;
        outerCharges = migCharges;
      } else {
        expansion = true; // n is migrating from inside out
        outerNormal = -1.;
        innerCharges = migCharges;
        outerCharges = tarCharges;
      }
    } else {
      forwardMap = false; // backward in time
      migCharges = formCharges(startO2);//
      tarCharges = formCharges(startO1);
      if (area1 > area2) {
        expansion = true; // n+1 is migrating from inside out, expansion
        outerNormal = -1.;
        innerCharges = migCharges;
        outerCharges = tarCharges;
      } else {
        expansion = false; // n+1 is migrating from outside in, contraction
        outerNormal = 1.;
        innerCharges = tarCharges;
        outerCharges = migCharges;
      }
    }

    Vert v = migCharges.getHead();
    ExtendedVector2d normal;
    do {
      normal = new ExtendedVector2d(v.getNormal().getX(), v.getNormal().getY());
      normal.multiply(outerNormal * ECMp.w);
      v.getPoint().addVec(normal);
      v = v.getNext();
    } while (!v.isHead());

    if (ECMp.chargeDensity != -1) {
      migCharges.setResolution(ECMp.chargeDensity);
      tarCharges.setResolution(ECMp.chargeDensity);
    }

    outerPoly = ioPolygons(outerCharges);
    innerPoly = ioPolygons(innerCharges);

  }

  public void setStarts(Vert a, Vert b) {
    startO1 = a;
    startO2 = b;
  }

  private void calcLengths() {
    lengthO1 = 0.;
    vertSo1 = 0;
    Vert v = startO1;
    do {
      lengthO1 += ExtendedVector2d.lengthP2P(v.getPoint(), v.getNext().getPoint());
      vertSo1++;
      v = v.getNext();
    } while (!v.isIntPoint());

    lengthO2 = 0.;
    vertSo2 = 0;
    v = startO2;
    do {
      lengthO2 += ExtendedVector2d.lengthP2P(v.getPoint(), v.getNext().getPoint());
      vertSo2++;
      v = v.getNext();
    } while (!v.isIntPoint());

    // double t = lengthO1;
    // lengthO1 = lengthO2;
    // lengthO2 = t;
  }

  private Outline formCharges(Vert s) {
    // create a new outline from the sector starting at s
    Vert newV = new Vert(s.getX(), s.getY(), 1);
    newV.setNormal(s.getNormal().getX(), s.getNormal().getY());
    newV.setIntPoint(true, -1);
    Outline o = new Outline(newV);

    s = s.getNext();
    do {
      newV = o.insertVert(newV);
      newV.setX(s.getX());
      newV.setY(s.getY());
      newV.setNormal(s.getNormal().getX(), s.getNormal().getY());

      if (s.isIntPoint()) {
        newV.setIntPoint(true, -1);
      }

      s = s.getNext();
    } while (!s.getPrev().isIntPoint()); // copy the int point too

    return o;
  }

  public Vert getMigStart() { // NOT CHARGES!
    if (forwardMap) {
      return startO1;
    } else {
      return startO2;
    }
  }

  public Vert getTarStart() { // NOT CHARGES
    if (forwardMap) {
      return startO2;
    } else {
      return startO1;
    }
  }

  public Vert addTempCharge(Vert tv) {
    // inserts a temporary charge into the charged nodes to ensure a
    // migrating node
    // remains within the boubdary of the outline. Have to find where to
    // insert it though.
    Vert v = migCharges.getHead();
    double dis = 99999.;
    double cdis;
    Vert closest = v;
    do {
      cdis = ExtendedVector2d.distPointToSegment(tv.getPoint(), v.getPoint(),
              v.getNext().getPoint());
      if (cdis < dis) {
        closest = v;
        dis = cdis;
      }
      v = v.getNext();
    } while (!v.isHead());

    Vert newVert = migCharges.insertVert(closest);
    newVert.setTrackNum(-35);
    newVert.setX(tv.getX());
    newVert.setY(tv.getY());
    ExtendedVector2d normal = new ExtendedVector2d(tv.getNormal().getX(), tv.getNormal().getY());
    normal.multiply(outerNormal * ECMp.w);
    newVert.getPoint().addVec(normal);
    newVert.updateNormale(true);
    return newVert;
  }

  public void removeTempCharge(Vert v) {
    migCharges.removeVert(v);
  }

  private void chargesPolygon() {
    ArrayList<ExtendedVector2d> points = new ArrayList<ExtendedVector2d>();

    Vert v = migCharges.getHead(); // get charges from head to int point, forward
    do {

      points.add(v.getPoint());
      if (v.isIntPoint() && !v.isHead()) {
        break;
      }
      v = v.getNext();
    } while (!v.isHead());

    // find int point in tar
    v = tarCharges.getHead();
    do {
      v = v.getNext();
    } while (!v.isIntPoint());

    // get tar charges in reverse
    do {
      points.add(v.getPoint());
      v = v.getPrev();
    } while (!v.getNext().isHead());

    // create floats
    float[] x = new float[points.size()];
    float[] y = new float[points.size()];

    ExtendedVector2d p;
    for (int i = 0; i < points.size(); i++) {
      p = (ExtendedVector2d) points.get(i);
      x[i] = (float) p.getX();
      y[i] = (float) p.getY();
    }

    chargesPoly = new FloatPolygon(x, y, x.length);
  }

  private FloatPolygon ioPolygons(Outline charges) { // in and out polygons
    float[] x = new float[charges.getNumPoints()];
    float[] y = new float[charges.getNumPoints()];

    int i = 0;
    Vert v = charges.getHead();
    do {
      x[i] = (float) v.getX();
      y[i] = (float) v.getY();
      i++;
      v = v.getNext();
    } while (!v.isHead());

    return new FloatPolygon(x, y, x.length); // was this
  }

  public boolean insideCharges(ExtendedVector2d p) {
    if (ECMp.numINTS > 1) {
      return chargesPoly.contains(p.getX(), p.getY());
    } else {
      if (outerPoly.contains(p.getX(), p.getY())) {
        if (innerPoly.contains(p.getX(), p.getY())) {
          return false;
        } else {
          return true;
        }
      } else {
        return false;
      }
    }

  }

  public void switchMigDirection() {
    Outline tempO = tarCharges; // swtich charges
    tarCharges = migCharges;
    migCharges = tempO;
    forwardMap = !forwardMap;
  }
}