Shape.java

package com.github.celldynamics.quimp;

import java.awt.Polygon;
import java.awt.geom.Rectangle2D;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

import org.scijava.vecmath.Point2d;
import org.scijava.vecmath.Tuple2d;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.celldynamics.quimp.filesystem.IQuimpSerialize;
import com.github.celldynamics.quimp.geom.ExtendedVector2d;

import ij.gui.PolygonRoi;
import ij.gui.Roi;

/**
 * Form abstract shape from bidirectional list of points.
 * 
 * <p>This abstract class keeps head point of Shape and control number of points in Shape, allows
 * for inserting points to the Shape. Generally assumes that Shape is closed, so PointsList is
 * looped
 * 
 * @author p.baniukiewicz
 *
 * @param <T> Type of point, currently can be Node or Vert
 */
public abstract class Shape<T extends PointsList<T>> implements IQuimpSerialize, Iterable<T> {

  /**
   * Threshold value for choosing new head as next or previous element on list if old head is
   * deleted.
   * 
   * <p>This should be used for determining the new head node only in tests, accessed by reflection.
   * 
   * <pre>
   * <code>
   * Field f = Shape.class.getDeclaredField("threshold");
   * f.setAccessible(true);
   * f.setDouble(Shape.class, 1.0);
   * </code>
   * </pre>
   * 
   * @see #removePoint(PointsList, boolean)
   */
  private static double threshold = 0.5;
  /**
   * The Constant LOGGER.
   */
  static final Logger LOGGER = LoggerFactory.getLogger(Shape.class.getName());
  /**
   * Next node ID's. Initialised in constructor, changed during modification of shape.
   */
  protected int nextTrackNumber = 1;
  /**
   * first node in double linked list, always maintained, initialised in constructor.
   */
  protected T head;
  /**
   * number of points. Initialised in constructor, changed on Shape modification.
   */
  protected int POINTS;
  /**
   * Centroid point of the Shape. Updated by {@link #calcCentroid()} called. after change of the
   * Shape and also in {@link #afterSerialize()} and {@link #beforeSerialize()}
   */
  protected ExtendedVector2d centroid = null;

  /**
   * Max number of nodes allowed in Shape.
   */
  public static final int MAX_NODES = 10000;
  /**
   * List is correct: has head, looped.
   * 
   * @see #validateShape()
   */
  public static final int LIST_OK = 0;
  /**
   * List contains node marked as head but without proper flag or null.
   * 
   * @see #validateShape()
   */
  public static final int BAD_HEAD = 1;
  /**
   * No head found among first {@value #MAX_NODES}.
   * {@link #validateShape()}
   */
  public static final int NO_HEAD = 2;
  /**
   * List is not closed. At least one nod has not previous or next neighbour.
   * {@link #validateShape()}
   */
  public static final int BAD_LINKING = 4;
  /**
   * Number of list elements does match to the number stored in {@link #POINTS}.
   * 
   * <p>This can happen when {@link #Shape(PointsList, int)} is used. See {@link #validateShape()}.
   */
  public static final int BAD_NUM_POINTS = 8;
  /**
   * Elements of Shape as List. Initialised on Serialise. Temporary array to store linked list as
   * array to allow serialisation
   */
  private ArrayList<T> Elements = null;

  /**
   * Default constructor, creates empty Shape.
   */
  public Shape() {
    POINTS = 0;
    head = null;
  }

  /**
   * Create Shape from existing list of points (can be one point as well).
   * 
   * <p>List of points must be looped.
   * 
   * @param h head point of the list
   * @param n number of points in the list
   * @see #validateShape()
   */
  public Shape(T h, int n) {
    head = h;
    T he = checkIsHead(); // can override head
    if (he == null) {
      LOGGER.info("No head in list. Selecting " + h + "as head");
      h.setHead(true);
      head = h;
    } else {
      if (he != h) {
        LOGGER.info("Head at different position than given initial element. Selecting " + he
                + "as head");
      }
      head = he;
    }

    POINTS = n;
    nextTrackNumber = n + 1;
    calcCentroid();
    setPositions();
  }

  /**
   * Create Shape from one point, created Shape will be looped. If <tt>h</tt> is a list, only
   * <tt>h</tt> will be maintained and list will be unlinked.
   * 
   * @param h head point of the list
   */
  public Shape(final T h) {
    head = h;
    head.setHead(true);
    head.setNext(head);
    head.setPrev(head);
    nextTrackNumber = head.getTrackNum() + 1;
    POINTS = 1;
    calcCentroid();
    setPositions();
  }

  /**
   * Copy constructor. Calculates centroid as well and positions.
   * 
   * @param src source Shape to copy from
   * @throws RuntimeException when T does no have copy constructor
   */
  public Shape(final Shape<T> src) {
    this(src, src.getHead());
  }

  /**
   * Conversion and copy constructor.
   * 
   * <p>Converts between different types of PointsList. <tt>src</tt> is source Shape of type T to
   * convert to other Shape based on <tt>PointsList</tt> other type (but in general extended from
   * PointsList) Typical approach is to convert Snake to Outline ({@link Node} to
   * {@link Vert}).
   * 
   * <p>Can be used as copy constructor.
   * 
   * @param src input Shape to convert.
   * @param destType object of base node that PointsList is composed from
   * @throws RuntimeException when T does no have copy constructor
   * 
   */
  @SuppressWarnings("unchecked")
  public Shape(final Shape<T> src, T destType) {
    T tmpHead = src.getHead(); // get head as representative object
    Class<?> templateClass = tmpHead.getClass(); // get class under Shape (T)
    LOGGER.trace("Src class: " + templateClass.getName());
    try {
      // Constructor of T as type can not be called directly, use reflection
      // get Constructor of T with one parameter of Type src (conversion constructor)
      Constructor<?> ctor = destType.getClass().getDeclaredConstructor(templateClass);
      // create copy of head
      head = (T) ctor.newInstance(src.getHead());
      T srcn = src.getHead();
      T n = head;
      // iterate over whole list making copies of T elements
      for (srcn = srcn.getNext(); !srcn.isHead(); srcn = srcn.getNext()) {
        T next = (T) ctor.newInstance(srcn);
        n.setNext(next);
        next.setPrev(n);
        n = next;
      }
      // loop list
      n.setNext(head);
      head.setPrev(n);
    } catch (SecurityException | NoSuchMethodException | InstantiationException
            | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
      throw new RuntimeException(e); // change to unchecked exception
    }
    // copy rest of params
    POINTS = src.POINTS;
    nextTrackNumber = src.nextTrackNumber;
    centroid = new ExtendedVector2d(src.centroid);
  }

  /**
   * Create Shape of elements <tt>elInst</tt> with coordinates <tt>p</tt>.
   * 
   * @param p list of coordinates
   * @param elInst instance of requested element type
   * @param inner direction of normales. For Outlines set to true, for snakes to
   *        BOA_.qState.segParam.expandSnake
   */
  @SuppressWarnings("unchecked")
  public Shape(final List<? extends Tuple2d> p, T elInst, boolean inner) {
    try {
      Constructor<?> ctor = elInst.getClass().getDeclaredConstructor(int.class);
      Constructor<?> ctorPoint =
              elInst.getClass().getDeclaredConstructor(double.class, double.class, int.class);
      head = (T) ctor.newInstance(0);
      POINTS = 1;
      head.setPrev(head);
      head.setNext(head);
      head.setHead(true);
      T node;
      for (Tuple2d el : p) {
        node = (T) ctorPoint.newInstance(el.getX(), el.getY(), nextTrackNumber++);
        addPoint(node);
      }
      removePoint(head, inner);
      setHeadClosestTo(new ExtendedVector2d(p.get(0))); // first point from list is head

    } catch (SecurityException | NoSuchMethodException | InstantiationException
            | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
      throw new RuntimeException(e); // change to unchecked exception
    }
    updateNormals(inner);
    calcCentroid();
    setPositions();
  }

  /**
   * Create Shape of elements <tt>elInst</tt> with coordinates <tt>x</tt> and <tt>y</tt>.
   * 
   * @param x coordinates
   * @param y coordinates
   * @param elInst instance of requested element type
   * @param inner direction of normals. For Outlines set to true, for snakes to
   *        BOA_.qState.segParam.expandSnake
   */
  @SuppressWarnings("unchecked")
  public Shape(final double[] x, final double[] y, T elInst, boolean inner) {
    try {
      Constructor<?> ctor = elInst.getClass().getDeclaredConstructor(int.class);
      Constructor<?> ctorPoint =
              elInst.getClass().getDeclaredConstructor(double.class, double.class, int.class);
      head = (T) ctor.newInstance(0);
      POINTS = 1;
      head.setPrev(head);
      head.setNext(head);
      head.setHead(true);
      T node;
      for (int i = 0; i < x.length; i++) {
        node = (T) ctorPoint.newInstance(x[i], y[i], nextTrackNumber++);
        addPoint(node);
      }
      removePoint(head, inner);
      setHeadClosestTo(new ExtendedVector2d(x[0], y[0])); // first point from list is head

    } catch (SecurityException | NoSuchMethodException | InstantiationException
            | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
      throw new RuntimeException(e); // change to unchecked exception
    }
    updateNormals(inner);
    calcCentroid();
    setPositions();
  }

  /**
   * Print Shape nodes.
   * 
   * @return String representation of Shape
   */
  @Override
  public String toString() {
    T v = this.head;
    String out = "Coords: ";
    do {
      out = out.concat(" {" + v.getX() + "," + v.getY() + "}");
      v = v.getNext();
    } while (!v.isHead());
    return out;
  }

  /*
   * (non-Javadoc)
   * 
   * @see java.lang.Object#hashCode()
   */
  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + POINTS;
    result = prime * result + ((centroid == null) ? 0 : centroid.hashCode());
    if (head == null) {
      result = prime * result + 0;
    } else { // go through the whole list
      T n = head;
      do {
        result = prime * result + n.hashCode();
        n = n.getNext();
      } while (!n.isHead());
    }
    result = prime * result + nextTrackNumber;
    return result;
  }

  /*
   * (non-Javadoc)
   * 
   * @see java.lang.Object#equals(java.lang.Object)
   */
  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (obj == null) {
      return false;
    }
    if (!(obj instanceof Shape)) {
      return false;
    }
    @SuppressWarnings("unchecked")
    Shape<T> other = (Shape<T>) obj;
    if (POINTS != other.POINTS) {
      return false;
    }
    if (centroid == null) {
      if (other.centroid != null) {
        return false;
      }
    } else if (!centroid.equals(other.centroid)) {
      return false;
    }
    if (head == null) {
      if (other.head != null) {
        return false;
      }
    } else { // iterate over list of nodes compare all
      T n = head;
      T nobj = other.getHead();
      boolean status = true;
      do {
        status &= n.equals(nobj);
        n = n.getNext();
        nobj = nobj.getNext();
      } while (!n.isHead());
      if (!status) {
        return false;
      }
    }
    if (nextTrackNumber != other.nextTrackNumber) {
      return false;
    }
    return true;
  }

  /**
   * Getter for <tt>centroid</tt>.
   * 
   * @return centroid
   */
  public ExtendedVector2d getCentroid() {
    if (centroid == null) {
      calcCentroid();
    }
    return centroid;
  }

  /**
   * Calculate centroid of Shape.
   * 
   * <p>This method modifies internal field centroid.
   */
  public void calcCentroid() {
    if (POINTS == 0) {
      centroid = null;
      return;
    }
    if (POINTS == 1) {
      centroid = new ExtendedVector2d(head.getX(), head.getY());
      return;
    }
    centroid = new ExtendedVector2d(0, 0);
    T v = head;
    double x;
    double y;
    double g;
    do {
      g = (v.getX() * v.getNext().getY()) - (v.getNext().getX() * v.getY());
      x = (v.getX() + v.getNext().getX()) * g;
      y = (v.getY() + v.getNext().getY()) * g;
      centroid.setX(centroid.getX() + x);
      centroid.setY(centroid.getY() + y);
      v = v.getNext();
    } while (!v.isHead());

    double area = this.calcArea();
    if (area != 0) {
      centroid.multiply(1d / (6 * this.calcArea()));
    } else {
      centroid.setX(0);
      centroid.setY(0);
    }
  }

  /**
   * Get bounds of Shape.
   * 
   * @return Bounding box of current Snake object as Double
   */
  public Rectangle2D.Double getDoubleBounds() {
    double minX;
    double minY;
    double maxX;
    double maxY;
    T n = getHead();
    minX = n.getX();
    maxX = n.getX();
    minY = n.getY();
    maxY = n.getY();
    n = n.getNext();
    do {
      if (n.getX() > maxX) {
        maxX = n.getX();
      }
      if (n.getX() < minX) {
        minX = n.getX();
      }
      if (n.getY() > maxY) {
        maxY = n.getY();
      }
      if (n.getY() < minY) {
        minY = n.getY();
      }
      n = n.getNext();
    } while (!n.isHead());
    return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
  }

  /**
   * Calculate area of the Shape.
   * 
   * @return Area
   */
  private double calcArea() {
    double area;
    double sum;
    sum = 0.0;
    T n = head;
    T np1 = n.getNext();
    do {
      sum += (n.getX() * np1.getY()) - (np1.getX() * n.getY());
      n = n.getNext();
      np1 = n.getNext(); // note: n is reset on prev line

    } while (!n.isHead());
    area = 0.5 * sum;
    return area;
  }

  /**
   * Add up lengths between all verts.
   * 
   * @return length of Shape
   */
  public double getLength() {
    T v = head;
    double length = 0.0;
    do {
      length += ExtendedVector2d.lengthP2P(v.getPoint(), v.getNext().getPoint());
      v = v.getNext();
    } while (!v.isHead());
    return length;
  }

  /**
   * Calculate position of Shape element expressed as distance of element on Shape perimeter from
   * <b>head</b>.
   * 
   * <p>First element has position 0, last 1. Element at position 0.5 is in half length of perimeter
   */
  public void setPositions() {
    double length = getLength();
    double d = 0.;

    T v = head;
    do {
      v.position = d / length;
      d = d + ExtendedVector2d.lengthP2P(v.getPoint(), v.getNext().getPoint());
      v = v.getNext();
    } while (!v.isHead());
  }

  /**
   * Update all node normals. Called after modification of Shape nodes.
   * 
   * @param inner Direction of normals. If <tt>false</tt> they are set outwards the shape.
   */
  public void updateNormals(boolean inner) {
    T v = head;
    do {
      v.updateNormale(inner);
      v = v.getNext();
    } while (!v.isHead());
  }

  /**
   * Get head of current Shape.
   * 
   * @return Point representing head of Shape
   */
  public T getHead() {
    return head;
  }

  /**
   * Set head of the shape to given element of the list.
   * 
   * <p>Element must be referenced on list.
   * 
   * @param newHead reference of new head.
   */
  public void setHead(T newHead) {
    T oldHead = getHead();
    T tmp;
    T v = oldHead;
    boolean status = false;
    if (oldHead == newHead) {
      return;
    }
    do {
      tmp = v.getNext();
      if (tmp == newHead) {
        tmp.setHead(true);
        head = tmp;
        oldHead.setHead(false);
        status = true;
        break;
      }
      v = v.getNext();
    } while (!v.isHead());

    if (!status) {
      throw new IllegalArgumentException("Given element has not been found on list");
    }
  }

  /**
   * Assign head to node nodeIndex. Do not change head if nodeIndex is not found or there is no head
   * in list.
   * 
   * @param nodeIndex Index of node of new head
   */
  public void setHead(int nodeIndex) {
    if (checkIsHead() == null) {
      return;
    }
    T n = head;
    T oldhead = n;
    do {
      n = n.getNext();
    } while (n.getTrackNum() != nodeIndex && !n.isHead());
    n.setHead(true);
    if (oldhead != n) {
      oldhead.setHead(false);
    }
    head = n;
    LOGGER.debug("New head is: " + getHead().toString());
  }

  /**
   * Set head to element closest to given coordinates.
   * 
   * @param phead point to set head closest
   */
  public void setHeadClosestTo(ExtendedVector2d phead) {
    double dis;
    double curDis;
    T v = head;
    T closestV = head;
    curDis = ExtendedVector2d.lengthP2P(phead, v.getPoint());

    do {
      dis = ExtendedVector2d.lengthP2P(phead, v.getPoint());
      if (dis < curDis) {
        curDis = dis;
        closestV = v;
      }
      v = v.getNext();
    } while (!v.isHead());

    if (closestV.isHead()) {
      return;
    }

    head.setHead(false);
    closestV.setHead(true);
    head = closestV;
  }

  /**
   * Check if there is a head node.
   * 
   * <p>Traverse along first {@value #MAX_NODES} elements and check if any of them is head.
   * 
   * @return found head or null if not found
   */
  public T checkIsHead() {
    if (head == null) {
      return null;
    }
    T n = head;
    int count = 0;
    do {
      if (count++ > MAX_NODES) {
        LOGGER.warn("Head lost!!!!");
        return null; // list looped but no head node
      }
      T p = n.getPrev(); // prev to current
      n = n.getNext(); // next to current
      if (n == null || p == null) { // each current must have next and previous
        throw new IllegalArgumentException("List is not looped");
      }
    } while (!n.isHead());
    return n;
  }

  /**
   * Validate correctness of the Shape.
   * 
   * <p>Check if Shape has head, it is correct and all nodes have previous and next neighbour.
   * 
   * @return {@value #LIST_OK} or combination of {@value #NO_HEAD}, {@value #BAD_LINKING},
   *         {@value #BAD_HEAD}, {@value #BAD_NUM_POINTS}
   */
  public int validateShape() {
    int ret = LIST_OK;
    if (head == null || head.isHead() == false) {
      ret = ret | BAD_HEAD; // bad head
    }
    try {
      if (checkIsHead() == null) {
        ret = ret | NO_HEAD; // no head
      }
    } catch (IllegalArgumentException e) {
      ret = ret | BAD_LINKING; // bad linking
    }
    if (ret == 0) { // check number of points that can be different if Shape.Shape(T, int) was used
      if (countPoints() != POINTS) {
        ret = ret | BAD_NUM_POINTS;
      }
    }
    return ret;
  }

  /**
   * Add node before head node assuring that list is a closed loop.
   * 
   * <p>If initial list condition is defined in such way:
   * 
   * <pre>
   * {@code
   * head = new Node(0); //make a dummy head node NODES = 1; FROZEN = 0;
   * head.setPrev(head); // link head to itself head.setNext(head);
   * head.setHead(true);
   * }
   * </pre>
   * 
   * <p>The <tt>addPoint</tt> will produce closed bidirectional linked list. From first Node it is
   * possible to reach last one by calling
   * {@link com.github.celldynamics.quimp.PointsList#getNext()}
   * and from the last one, first should be accessible by calling
   * {@link com.github.celldynamics.quimp.PointsList#getPrev()}.
   * 
   * <p>For initialisation only.
   * 
   * @param n Node to be added to list
   * 
   */
  public void addPoint(final T n) {
    T prevNode = head.getPrev();
    n.setPrev(prevNode);
    n.setNext(head);

    head.setPrev(n);
    prevNode.setNext(n);
    POINTS++;
  }

  /**
   * Insert point ne after point n.
   * 
   * @param n reference point
   * @param ne point to be inserted after reference.
   * @return new node
   */
  public T insertPoint(final T n, final T ne) {
    T newNode = ne;
    ne.setTrackNum(nextTrackNumber++);
    newNode.setNext(n.getNext());
    newNode.setPrev(n);
    n.getNext().setPrev(newNode);
    n.setNext(newNode);
    POINTS++;
    return newNode;
  }

  /**
   * Remove selected point from list.
   * 
   * <p>Check if removed point was head and if it was, the new head is randomly selected. Neighbors
   * are linked together. There is no protection here against removing last node at all.
   * 
   * @param n point to remove
   * @param inner direction of normal vectors of Shape
   * @see #Shape(List, PointsList, boolean)
   */
  public void removePoint(final T n, boolean inner) {
    n.getPrev().setNext(n.getNext());
    n.getNext().setPrev(n.getPrev());

    // if removing head randomly assign a neighbour as new head
    if (n.isHead()) {
      if (Math.random() > threshold) {
        LOGGER.trace("removePoint - getNext");
        head = n.getNext();
      } else {
        LOGGER.trace("removePoint - getPrev");
        head = n.getPrev();
      }
      head.setHead(true);
    }

    POINTS--;

    n.getPrev().updateNormale(inner);
    n.getNext().updateNormale(inner);
    // WARN This may have influence to other parts of project. In original n was cleaned
    // n = null;
  }

  /**
   * Get number of points in Shape.
   * 
   * @return Number of points
   */
  public int getNumPoints() {
    return POINTS;
  }

  /**
   * Make Shape anti-clockwise.
   */
  public void makeAntiClockwise() {
    double sum = 0;
    T v = head;
    do {
      sum += (v.getNext().getX() - v.getX()) * (v.getNext().getY() + v.getY());
      v = v.getNext();
    } while (!v.isHead());
    if (sum > 0) {
      LOGGER.trace("Warning. Was clockwise, reversed");
      this.reverseShape();
      this.updateNormals(true); // WARN This was in Outline but not in Snake
    }
  }

  /**
   * Turn Shape back anti clockwise.
   */
  public void reverseShape() {
    T tmp;
    T v = head;
    do {
      tmp = v.getNext();
      v.setNext(v.getPrev());
      v.setPrev(tmp);
      v = v.getNext();
    } while (!v.isHead());
  }

  /**
   * Return current Shape as Java polygon.
   * 
   * @return current Shape as java.awt.Polygon
   */
  public Polygon asPolygon() {
    Polygon pol = new Polygon();
    T n = head;
    do {
      pol.addPoint((int) Math.floor(n.getX() + 0.5), (int) Math.floor(n.getY() + 0.5));
      n = n.getNext();
    } while (!n.isHead());

    return pol;
  }

  /**
   * Returns current Shape as list of points (copy).
   * 
   * @return List of Point2d objects representing coordinates of T
   */
  public List<Point2d> asList() {
    List<Point2d> al = new ArrayList<Point2d>(POINTS);
    // iterate over nodes at Shape
    T n = head;
    do {
      al.add(new Point2d(n.getX(), n.getY()));
      n = n.getNext();
    } while (!n.isHead());
    return al;
  }

  /**
   * Return current Shape as ImageJ float number polygon.
   * 
   * @return current Shape as PolygonRoi
   */
  public Roi asFloatRoi() {
    float[] x = new float[POINTS];
    float[] y = new float[POINTS];

    T n = head;
    int i = 0;
    do {
      x[i] = (float) n.getX();
      y[i] = (float) n.getY();
      i++;
      n = n.getNext();
    } while (!n.isHead());
    return new PolygonRoi(x, y, POINTS, Roi.POLYGON);
  }

  /**
   * Return current Shape as ImageJ ROI object.
   * 
   * @return current Shape as ROI
   */
  public Roi asIntRoi() {
    Polygon p = asPolygon();
    Roi r = new PolygonRoi(p, PolygonRoi.POLYGON);
    return r;
  }

  /**
   * Count number of Points in Shape.
   * 
   * <p>Number of Points is stored in local POINTS field as well. This method can verify if that
   * field contains correct value.
   * 
   * @return number of Points in Shape or negative value if {@value #MAX_NODES} was reached
   */
  public int countPoints() {
    T v = head;
    int c = 0;
    do {
      c++;
      v = v.getNext();
    } while (!v.isHead() && c < MAX_NODES);

    if (c == MAX_NODES) {
      return -1;
    } else {
      return c;
    }
  }

  /**
   * Unfreeze all nodes in Shape.
   */
  public void unfreezeAll() {
    T v = head;
    do {
      v.unfreeze();
      v = v.getNext();
    } while (!v.isHead());
  }

  /**
   * Freeze all nodes in Shape.
   */
  public void freezeAll() {
    T v = head;
    do {
      v.freeze();
      v = v.getNext();
    } while (!v.isHead());
  }

  /**
   * Scale current Shape by <tt>stepSize</tt>. Centroid and normals need to be updated afterwards.
   * 
   * <p>Direction of scaling depends on direction of normals.
   * 
   * @param stepSize increment
   * @see PointsList#updateNormale(boolean)
   * @see #calcCentroid()
   * @see #setPositions()
   * @see PointsList#setClockwise(boolean)
   */
  public void scale(double stepSize) {
    T n;
    n = head;
    do {
      if (!n.isFrozen()) {
        n.setX(n.getX() + stepSize * n.getNormal().getX());
        n.setY(n.getY() + stepSize * n.getNormal().getY());
      }
      n = n.getNext();
    } while (!n.isHead());
  }

  /**
   * Convert coordinate of Shape to array.
   * 
   * @return x-coordinates of Outline as array
   */
  public double[] xtoArr() {
    double[] arry = new double[POINTS];

    T v = head;
    int i = 0;
    do {
      arry[i] = v.getX();
      i++;
      v = v.getNext();
    } while (!v.isHead());
    return arry;
  }

  /**
   * Convert coordinate of Shape to array.
   * 
   * @return y-coordinates of Outline as array
   */
  public double[] ytoArr() {
    double[] arry = new double[POINTS];

    T v = head;
    int i = 0;
    do {
      arry[i] = v.getY();
      i++;
      v = v.getNext();
    } while (!v.isHead());
    return arry;
  }

  /*
   * (non-Javadoc)
   * 
   * @see java.lang.Iterable#iterator()
   */
  @Override
  public Iterator<T> iterator() {
    int ret = validateShape();
    if (ret != 0) {
      LOGGER.warn("Shape is broken, iterator may be unpredicable. Reason: " + ret);
    }
    Iterator<T> it = new Iterator<T>() {
      private transient T nextToReturn = head; // element to return by next()
      private transient boolean traversed = false; // true if we started iterating, used for
      // detection that first head should be returned but second not (second - we approached head
      // from left side)

      @Override
      public boolean hasNext() {
        if (nextToReturn == null || nextToReturn.getNext() == null) {
          return false;
        }
        if (POINTS == 1 && nextToReturn.isHead()) {
          return true; // only head linked to itself
        }
        if (nextToReturn.isHead() && traversed == true) {
          return false; // is head and we started iterating, so approached it from left
        } else {
          return true; // iterating not started - return at least current element (head)
        }
      }

      @Override
      public T next() {
        if (hasNext() == false) {
          throw new NoSuchElementException("No more elements");
        }
        T ret = nextToReturn;
        if (POINTS == 1) { // if only head
          nextToReturn = null;
        } else {
          nextToReturn = nextToReturn.getNext();
        }
        traversed = true;
        return ret;
      }
    };
    return it;
  }

  /*
   * (non-Javadoc)
   * 
   * @see com.github.celldynamics.quimp.filesystem.IQuimpSerialize#beforeSerialize()
   */
  @Override
  public void beforeSerialize() {
    calcCentroid();
    setPositions();
    updateNormals(true);
    makeAntiClockwise();
    Elements = new ArrayList<>();
    T n = getHead().getNext(); // do not store head as it is stored in head variable
    do {
      Elements.add(n);
      n = n.getNext();
    } while (!n.isHead());
  }

  /*
   * (non-Javadoc)
   * 
   * @see com.github.celldynamics.quimp.filesystem.IQuimpSerialize#afterSerialize()
   */
  @Override
  public void afterSerialize() throws Exception {
    if (Elements != null && Elements.size() > 0) {
      // head is saved as non-transitive field, so it is recreated on load and this object
      // exists already
      T first = head; // remember it
      Class<?> templateClass = head.getClass(); // get class name under Shape (T)
      try {
        Constructor<?> ctor = head.getClass().getDeclaredConstructor(templateClass);
        for (int i = 0; i < Elements.size(); i++) { // iterate over list from second position
          @SuppressWarnings("unchecked")
          T next = (T) ctor.newInstance(Elements.get(i));
          head.setNext(next);
          next.setPrev(head);
          head = next;
        }
        head.setNext(first);
        first.setPrev(head);
        head = first;
      } catch (SecurityException | NoSuchMethodException | InstantiationException
              | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
        throw new RuntimeException(e); // change to unchecked exception
      }
    }
    clearElements();
    calcCentroid(); // WARN Updating saved data - may be wrong
    setPositions();
    updateNormals(true);
    makeAntiClockwise();
  }

  /**
   * Clear <tt>Elements</tt> array that stores list of {Node, Snake} in ArrayList form. It is used
   * and initialized on Serialization. This method simply delete this array saving memory.
   * 
   * <p>It should be called after every serialisation.
   */
  public void clearElements() {
    if (Elements != null) {
      Elements.clear();
    }
    Elements = null;
  }

}