QuimpDataConverter.java

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

import java.awt.Point;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.scijava.vecmath.Point2d;
import org.scijava.vecmath.Tuple2d;

import com.github.celldynamics.quimp.BoaException;
import com.github.celldynamics.quimp.Outline;
import com.github.celldynamics.quimp.Shape;
import com.github.celldynamics.quimp.Snake;

import ij.util.Tools;

/**
 * Perform conversions among Snake, List and xc, yc arrays.
 * 
 * <p>As this object returns references to arrays and list, any modification done "in place" on
 * returned data will affect future conversions done by calling accessor methods.
 * 
 * <p>The base format are two arrays xc and yc. All other inputs are converted to arrays
 * first.
 * Conversion e.g. Snake->Snake causes that output Snake is not reference of input one because input
 * has been converted to arrays first.
 * 
 * @author p.baniukiewicz
 * 
 */
public class QuimpDataConverter {

  private double[] xc; // extracted x coords from Vec2d
  private double[] yc; // extracted y coords from Vec2d

  /**
   * Default constructor.
   */
  public QuimpDataConverter() {
    xc = new double[0];
    yc = new double[0];
  }

  /**
   * Default constructor if Node list is in form of List.
   * 
   * @param input list of vertices. If input is null xc and yc are set to 0 length arrays,
   *        Snake is
   *        null then
   */
  public QuimpDataConverter(final List<? extends Tuple2d> input) {
    this();
    if (input != null) {
      toArrays(input);
    }
  }

  /**
   * Default constructor collection of awt.Point.
   * 
   * @param input list of vertices. If input is null xc and yc are set to 0 length arrays,
   *        Snake is null then
   */
  public QuimpDataConverter(final Collection<? extends Point2D> input) {
    this();
    if (input != null) {
      toArrays(input);
    }
  }

  /**
   * Default if Node list is in form of two arrays with coordinates.
   * 
   * @param x input list of vertices
   * @param y input list of vertices
   */
  public QuimpDataConverter(final double[] x, final double[] y) {
    this();
    if (x.length != y.length) {
      throw new IllegalArgumentException("Arrays have different lengths");
    }
    this.xc = x;
    this.yc = y;
  }

  /**
   * Default if Node list is in form of two arrays with coordinates.
   * 
   * @param x input list of vertices
   * @param y input list of vertices
   */
  public QuimpDataConverter(final float[] x, final float[] y) {
    this();
    if (x.length != y.length) {
      throw new IllegalArgumentException("Arrays have different lengths");
    }
    this.xc = Tools.toDouble(x);
    this.yc = Tools.toDouble(y);
  }

  /**
   * Default constructor if Node list is in form of Snake object.
   * 
   * @param s Shape to be converted. If null xc and yc are set to 0 length arrays, List is
   *        also 0 length.
   */
  public QuimpDataConverter(final Shape<?> s) {
    this();
    if (s != null) {
      toArrays(s.asList());
    }
  }

  /**
   * Converts Vector2d to xc and yc arrays storing xc and yc coordinates of
   * Vector2d separately.
   * 
   * @param input List to be converted to arrays
   */
  private void toArrays(final List<? extends Tuple2d> input) {
    int i = 0;
    if (input != null) {
      xc = new double[input.size()];
      yc = new double[input.size()];
      for (Tuple2d el : input) {
        xc[i] = el.getX();
        yc[i] = el.getY();
        i++;
      }
    } else {
      xc = new double[0];
      yc = new double[0];
    }
  }

  /**
   * Converts awt.Point to xc and yc arrays storing xc and yc coordinates of
   * Vector2d separately.
   * 
   * @param input List to be converted to arrays
   */
  private void toArrays(final Collection<? extends Point2D> input) {
    int i = 0;
    if (input != null) {
      xc = new double[input.size()];
      yc = new double[input.size()];
      for (Point2D el : input) {
        xc[i] = el.getX();
        yc[i] = el.getY();
        i++;
      }
    } else {
      xc = new double[0];
      yc = new double[0];
    }
  }

  /**
   * Data accessor.
   * 
   * @return Array with ordered xc coordinates of input list. Array can have 0 length.
   */
  public double[] getX() {
    return xc;
  }

  /**
   * Data accessor.
   * 
   * @return Array with ordered yc coordinates of input list. Array can have 0 length.
   */
  public double[] getY() {
    return yc;
  }

  /**
   * Data accessor.
   * 
   * <p><b>Warning</b>
   * 
   * <p>If user modifies this list this object loses its consistency
   * 
   * <p>To convert {@link Shape} to list use {@link Shape#asList()}.
   * 
   * @return List of Point2d from stored objects
   */
  public List<Point2d> getList() {
    ArrayList<Point2d> list = new ArrayList<>();
    for (int i = 0; i < xc.length; i++) {
      list.add(new Point2d(xc[i], yc[i]));
    }
    return list;
  }

  /**
   * Data accessor.
   * 
   * <p><b>Warning</b>
   * 
   * <p>If user modifies this list this object loses its consistency
   * 
   * <p>To convert {@link Shape} to list use {@link Shape#asList()}.
   * 
   * @return List of awt.Points from stored objects. Note that points are converted to int
   */
  public List<Point2D> getListofIntPoints() {
    ArrayList<Point2D> list = new ArrayList<>();
    for (int i = 0; i < xc.length; i++) {
      list.add(new Point((int) Math.round(xc[i]), (int) Math.round(yc[i])));
    }
    return list;
  }

  /**
   * Data accessor.
   * 
   * <p><b>Warning</b>
   * 
   * <p>If user modifies this list this object loses its consistency
   * 
   * <p>To convert {@link Shape} to list use {@link Shape#asList()}.
   * 
   * @return List of awt.Points from stored objects. Note that points are converted to int
   */
  public List<Point2D> getListofDoublePoints() {
    ArrayList<Point2D> list = new ArrayList<>();
    for (int i = 0; i < xc.length; i++) {
      list.add(new Point2D.Double(xc[i], yc[i]));
    }
    return list;
  }

  /**
   * Data accessor.
   * 
   * @return Array with ordered xc coordinates of input list as float
   */
  public float[] getFloatX() {
    float[] xf = new float[xc.length];
    for (int i = 0; i < xc.length; i++) {
      xf[i] = (float) xc[i];
    }
    return xf;
  }

  /**
   * Data accessor.
   * 
   * @return Array with ordered yc coordinates of input list as float
   */
  public float[] getFloatY() {
    float[] yf = new float[yc.length];
    for (int i = 0; i < yc.length; i++) {
      yf[i] = (float) yc[i];
    }
    return yf;
  }

  /**
   * Data accessor.
   * 
   * @return Length of input list
   */
  public int size() {
    return xc.length;
  }

  /**
   * Return Snake created from stored data.
   * 
   * <p>Head node is first point from list. Snake has centroid, linear coordinates and boundaries
   * calculated already.
   * Normals are set according to global BOAState.SegParam#expandSnake.
   * 
   * @param id new Id of snake
   * @return Snake object with Nodes in order of data given on input. Can be null. Normals depend
   *         on BOA_.qState.segParam.expandSnake
   * @throws BoaException when there is less than 3 nodes.
   * @see com.github.celldynamics.quimp.Snake#Snake(double[], double[], int)
   * @see com.github.celldynamics.quimp.Snake#removeNode(com.github.celldynamics.quimp.Node)
   * @see Shape#updateNormals(boolean)
   */
  public Snake getSnake(int id) throws BoaException {
    Snake ret = null;
    if (xc.length == 0 || yc.length == 0) {
      return ret;
    } else {
      ret = new Snake(xc, yc, id);
      return ret;
    }
  }

  /**
   * Return Outline created from stored data.
   * 
   * <p>Snake has centroid, local curvature and linear coordinates calculated already.
   * Normals are set <tt>true</tt>
   * 
   * @return Outline object with Nodes in order of data given on input. Can be null. Normals are
   *         set outwards. Head node is first point from list.
   * @see com.github.celldynamics.quimp.Snake#Snake(double[], double[], int)
   * @see com.github.celldynamics.quimp.Snake#removeNode(com.github.celldynamics.quimp.Node)
   * @see Shape#updateNormals(boolean)
   */
  public Outline getOutline() {
    Outline ret = null;
    if (xc.length == 0 || yc.length == 0) {
      return ret;
    } else {
      ret = new Outline(xc, yc);
      return ret;
    }
  }

}