package cz.cuni.lf1.lge.ThunderSTORM.util;
import static cz.cuni.lf1.lge.ThunderSTORM.util.MathProxy.round;
import static cz.cuni.lf1.lge.ThunderSTORM.util.MathProxy.ceil;
import ij.gui.Roi;
import java.util.Comparator;
import java.util.List;
/**
* The class encapsulates X,Y representation of a single point and its
* intensity.
*
* @param <T> is a Number subclass, often Integer or Double
*/
public class Point<T extends Number> {
/**
* X coordinate
*/
public T x;
/**
* Y coordinate
*/
public T y;
/**
* Intensity
*/
public T val;
/**
* X, Y, and intenstity are set to null.
*
*/
public Point() {
this.x = null;
this.y = null;
this.val = null;
}
/**
* @param x X coordinate
* @param y Y coordinate
* @param val Intensity
*/
public Point(T x, T y, T val) {
this.x = x;
this.y = y;
this.val = val;
}
/**
* @param x X coordinate
* @param y Y coordinate
*/
public Point(T x, T y) {
this.x = x;
this.y = y;
this.val = null;
}
/**
* Returns X coordinate.
*
* @return X coordinate
*/
public T getX() {
return x;
}
/**
* Returns Y coordinate.
*
* @return Y coordinate
*/
public T getY() {
return y;
}
/**
* Returns intensity.
*
* @return Intensity
*/
public T getVal() {
return val;
}
/**
* Sets X coordinate.
*
* @param x X coordinate
*/
public void setX(T x) {
this.x = x;
}
/**
* Sets Y coordinate.
*
* @param y Y coordinate
*/
public void setY(T y) {
this.y = y;
}
/**
* Sets intensity.
*
* @param val Intensity
*/
public void setVal(T val) {
this.val = val;
}
/**
* Sets position (X,Y coordinates) of the point.
*
* @param x X coordinate
* @param y Y coordinate
* @return this
*/
public Point setLocation(T x, T y) {
this.x = x;
this.y = y;
return this;
}
/**
* Sets position (X,Y coordinates) and intensity (value) of the point.
*
* @param x X coordinate
* @param y Y coordinate
* @param val intensity
*/
public void set(T x, T y, T val) {
this.x = x;
this.y = y;
this.val = val;
}
/**
* Returns the point with its X,Y coordinates and intensity rounded to
* Integer.
*
* @return the point with its X,Y coordinates and intensity rounded to
* Integer.
*/
public Point<Integer> roundToInteger() {
assert ((x != null) && (y != null));
if(val == null) {
return new Point<Integer>(round(this.x.floatValue()), round(this.y.floatValue()));
} else {
return new Point<Integer>(round(this.x.floatValue()), round(this.y.floatValue()), round(this.val.floatValue()));
}
}
/**
* Returns the point with its X,Y coordinates and intensity rounded to Long.
*
* @return the point with its X,Y coordinates and intensity rounded to Long.
*/
public Point<Long> roundToLong() {
assert ((x != null) && (y != null));
if(val == null) {
return new Point<Long>(round(this.x.doubleValue()), round(this.y.doubleValue()));
} else {
return new Point<Long>(round(this.x.doubleValue()), round(this.y.doubleValue()), round(this.val.doubleValue()));
}
}
/**
* Returns the point with its X,Y coordinates and intensity as Integer (the
* decimal part is cut off).
*
* @return the point with its X,Y coordinates and intensity rounded to
* Integer.
*/
public Point<Integer> toInteger() {
assert ((x != null) && (y != null));
if(val == null) {
return new Point<Integer>(this.x.intValue(), this.y.intValue());
} else {
return new Point<Integer>(this.x.intValue(), this.y.intValue(), this.val.intValue());
}
}
/**
* Returns the point with its X,Y coordinates and intensity as Float (single
* precision). Clearly there may be a round off when converting from Double
* (double precision).
*
* @return the point with its X,Y coordinates and intensity as Float.
*/
public Point<Float> toFloat() {
assert ((x != null) && (y != null));
if(val == null) {
return new Point<Float>(this.x.floatValue(), this.y.floatValue());
} else {
return new Point<Float>(this.x.floatValue(), this.y.floatValue(), this.val.floatValue());
}
}
/**
* Returns the point with its X,Y coordinates and intensity as Byte (the
* decimal part is cut off). Clearly there may be an overflow or underflow
* when converting from larger type as Integer or Long.
*
* @return the point with its X,Y coordinates and intensity as Byte.
*/
public Point<Byte> toByte() {
assert ((x != null) && (y != null));
if(val == null) {
return new Point<Byte>(this.x.byteValue(), this.y.byteValue());
} else {
return new Point<Byte>(this.x.byteValue(), this.y.byteValue(), this.val.byteValue());
}
}
/**
* Returns the point with its X,Y coordinates and intensity as Double
* (double precision).
*
* @return the point with its X,Y coordinates and intensity as Double.
*/
public Point<Double> toDouble() {
assert ((x != null) && (y != null));
if(val == null) {
return new Point<Double>(this.x.doubleValue(), this.y.doubleValue());
} else {
return new Point<Double>(this.x.doubleValue(), this.y.doubleValue(), this.val.doubleValue());
}
}
/**
* Returns the point with its X,Y coordinates and intensity as Long (the
* decimal part is cut off).
*
* @return the point with its X,Y coordinates and intensity as Long.
*/
public Point<Long> toLong() {
assert ((x != null) && (y != null));
if(val == null) {
return new Point<Long>(this.x.longValue(), this.y.longValue());
} else {
return new Point<Long>(this.x.longValue(), this.y.longValue(), this.val.longValue());
}
}
/**
* Returns the point with its X,Y coordinates and intensity as Short (the
* decimal part is cut off). Clearly there may be an overflow or underflow
* when converting from larger type as Integer or Long.
*
* @return the point with its X,Y coordinates and intensity as Short.
*/
public Point<Short> toShort() {
assert ((x != null) && (y != null));
if(val == null) {
return new Point<Short>(this.x.shortValue(), this.y.shortValue());
} else {
return new Point<Short>(this.x.shortValue(), this.y.shortValue(), this.val.shortValue());
}
}
@Override
public String toString() {
assert ((x != null) && (y != null));
if(val == null) {
return "[" + x.toString() + "," + y.toString() + "]";
} else {
return "[" + x.toString() + "," + y.toString() + "]=" + val.toString();
}
}
@Override
public boolean equals(Object o) {
if(o instanceof Point) {
Point pt = (Point) o;
boolean bx = false, by = false, bval = false;
//
// x
if((x != null) && (pt.x != null)) {
if(x.equals(pt.x)) {
bx = true;
}
} else if((x == null) && (pt.x == null)) {
bx = true;
}
if(bx == false) {
return false;
}
//
// y
if((y != null) && (pt.y != null)) {
if(y.equals(pt.y)) {
by = true;
}
} else if((y == null) && (pt.y == null)) {
by = true;
}
if(by == false) {
return false;
}
//
// val
if((val != null) && (pt.val != null)) {
if(val.equals(pt.val)) {
bval = true;
}
} else if((val == null) && (pt.val == null)) {
bval = true;
}
if(bval == false) {
return false;
}
//
return true;
}
return false;
}
// automatically generated by Netbeans IDE
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + (this.x != null ? this.x.hashCode() : 0);
hash = 79 * hash + (this.y != null ? this.y.hashCode() : 0);
hash = 79 * hash + (this.val != null ? this.val.hashCode() : 0);
return hash;
}
/**
* Scaling the coordinates, e.g., for the purpose of converting between
* pixels and nanometers. The data type T of Point<T> remains the same
* as it was before calling this method.
*
* <ol>
* <li>the X,Y coordinates are converted to Double</li>
* <li>the multiplication {
*
* @mathjax x = x \times factor} and {
* @mathjax y = y \times factor} is performed</li>
* <li>the result is converted back to type T (there may be a round off or
* overflow error)</li>
* </ol>
*
* @param factor scale by which the X,Y coordinates will be multiplied with
*/
public void scaleXY(double factor) {
double nx = x.doubleValue() * factor;
double ny = y.doubleValue() * factor;
if(x instanceof Double) {
x = (T) new Double(nx);
y = (T) new Double(ny);
} else if(x instanceof Float) {
x = (T) new Float(nx);
y = (T) new Float(ny);
} else if(x instanceof Long) {
x = (T) new Long((long) nx);
y = (T) new Long((long) ny);
} else if(x instanceof Integer) {
x = (T) new Integer((int) nx);
y = (T) new Integer((int) ny);
} else if(x instanceof Short) {
x = (T) new Short((short) nx);
y = (T) new Short((short) ny);
} else if(x instanceof Byte) {
x = (T) new Byte((byte) nx);
y = (T) new Byte((byte) ny);
}
}
/**
* Comparator class for sorting the Point<T> instances. This
* comparator uses only X,Y coordinations, <strong>not</strong> the
* intensity. The left-most point will be ordered as first. If two or more
* points have the same X coordinate the point with the lowest value of Y
* coordinate will be ordered as first.
*/
public static class XYComparator implements Comparator<Point> {
@Override
public int compare(Point p1, Point p2) {
double px1 = p1.x.doubleValue(), px2 = p2.x.doubleValue();
double py1 = p1.y.doubleValue(), py2 = p2.y.doubleValue();
if(px1 == px2) {
return (int) ceil(py1 - py2);
}
return (int) ceil(px1 - px2);
}
}
public static List<Point> applyRoiMask(Roi roi, List<Point> detections) {
if(roi == null) {
return detections;
} else {
for(int i = 0; i < detections.size();) {
Point pt = detections.get(i);
if(!roi.contains(pt.x.intValue() + roi.getBounds().x, pt.y.intValue() + roi.getBounds().y)) {
detections.remove(i);
} else {
i++;
}
}
return detections;
}
}
}