/*-
*******************************************************************************
* Copyright (c) 2011, 2014 Diamond Light Source Ltd.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Peter Chang - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.dawnsci.analysis.dataset.roi;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.eclipse.dawnsci.analysis.api.roi.IPolylineROI;
import org.eclipse.dawnsci.analysis.api.roi.IROI;
import org.eclipse.january.dataset.Dataset;
import org.eclipse.january.dataset.DatasetFactory;
/**
* Class for a polyline ROI (really a list of point ROIs)
*/
public class PolylineROI extends ROIBase implements IPolylineROI, Serializable {
protected List<IROI> pts; // first element should point to this if size > 0
/**
* Zero point line
*/
public PolylineROI() {
super();
pts = new ArrayList<IROI>();
}
public PolylineROI(IPolylineROI poly) {
this();
for (IROI p: poly) {
insertPoint(new PointROI(p.getPoint()));
}
name = poly.getName();
plot = poly.isPlot();
}
public PolylineROI(List<PointROI> points) {
this();
if (points.size() > 0) {
pts.addAll(points);
super.setPoint(points.get(0).getPointRef());
}
}
public PolylineROI(double[] start) {
super();
pts = new ArrayList<IROI>();
setPoint(start);
}
public PolylineROI(double x, double y) {
this(new double[] {x, y});
}
@Override
public void downsample(double subFactor) {
spt[0] /= subFactor;
spt[1] /= subFactor;
for (int i = 1, imax = pts.size(); i < imax; i++) { // don't call for first point
pts.get(i).downsample(subFactor);
}
setDirty();
}
/**
*
* @param point
*/
public void setPoint(PointROI point) {
super.setPoint(point.spt);
if (pts.size() == 0) {
pts.add(point);
} else {
pts.set(0, point);
}
}
@Override
public void setPoint(double[] point) {
setPoint(new PointROI(point));
}
/**
* Set point of polyline at index
* @param i index
* @param point
*/
@Override
public void setPoint(int i, IROI point) {
if (i == 0) {
setPoint(point.getPointRef());
} else {
pts.set(i, point instanceof PointROI ? (PointROI) point : new PointROI(point.getPointRef()));
setDirty();
}
}
/**
* Set point of polyline at index
* @param i index
* @param point
*/
public void setPoint(int i, double[] point) {
setPoint(i, new PointROI(point));
}
/**
* Set point of polyline at index
* @param i index
* @param x
* @param y
*/
public void setPoint(int i, double x, double y) {
setPoint(i, new PointROI(x, y));
}
/**
* Add point to polyline
* @param point
*/
@Override
public void insertPoint(IROI point) {
PointROI p = point instanceof PointROI ? (PointROI) point : new PointROI(point.getPointRef());
if (pts.size() == 0) {
setPoint(p);
} else {
pts.add(p);
setDirty();
}
}
/**
* Add point to polyline
* @param point
*/
public void insertPoint(double[] point) {
insertPoint(new PointROI(point));
}
/**
* Add point to polyline
* @param point
*/
public void insertPoint(int[] point) {
insertPoint(new double[] { point[0], point[1] });
}
/**
* Add point to polyline
* @param x
* @param y
*/
public void insertPoint(double x, double y) {
insertPoint(new double[] {x, y});
}
/**
* Insert point to polyline at index
* @param i index
* @param point
*/
public void insertPoint(int i, PointROI point) {
if (i == 0) {
insertPoint(point);
} else {
pts.add(i, point);
setDirty();
}
}
/**
* Insert point to polyline at index
* @param i index
* @param point
*/
public void insertPoint(int i, double[] point) {
insertPoint(i, new PointROI(point));
}
/**
* Insert point to polyline at index
* @param i index
* @param x
* @param y
*/
public void insertPoint(int i, double x, double y) {
insertPoint(i, new double[] {x, y});
}
/**
* @return number of sides
*/
public int getSides() {
final int size = pts.size();
return size == 0 ? 0 : size - 1;
}
@Override
public int getNumberOfPoints() {
return pts.size();
}
/**
* @param i
* @return x value of i-th point
*/
public double getPointX(int i) {
return pts.get(i).getPointRef()[0];
}
/**
* @param i
* @return y value of i-th point
*/
public double getPointY(int i) {
return pts.get(i).getPointRef()[1];
}
@Override
public PointROI getPoint(int i) {
return (PointROI) pts.get(i);
}
@Override
public RectangularROI getBounds() {
if (bounds == null) {
bounds = new RectangularROI();
int imax = pts.size();
if (imax > 0) {
double[] max = new double[] { -Double.MAX_VALUE, -Double.MAX_VALUE };
double[] min = new double[] { Double.MAX_VALUE, Double.MAX_VALUE };
for (int i = 0; i < imax; i++) {
ROIUtils.updateMaxMin(max, min, pts.get(i).getPointRef());
}
bounds.setPoint(min);
bounds.setLengths(max[0] - min[0], max[1] - min[1]);
}
}
return bounds;
}
@Override
public boolean containsPoint(double x, double y) {
return isNearOutline(x, y, Math.max(Math.ulp(x), Math.ulp(y)));
}
@Override
public boolean isNearOutline(double x, double y, double distance) {
if (!super.isNearOutline(x, y, distance))
return false;
if (Math.hypot(spt[0] - x, spt[1] - y) <= distance)
return true;
int imax = pts.size();
if (imax < 2)
return false;
double[] p = pts.get(0).getPointRef();
double ox = p[0];
double oy = p[1];
for (int i = 1; i < imax; i++) {
p = pts.get(i).getPointRef();
double px = p[0];
double py = p[1];
if (ROIUtils.isNearSegment(px - ox, py - oy, x - ox, y - oy, distance))
return true;
ox = px;
oy = py;
}
return false;
}
/**
* @return list of points
*/
@Override
public List<IROI> getPoints() {
return pts;
}
/**
* Replace points in polyline by given list
* @param points
*/
@Override
public void setPoints(List<IROI> points) {
pts.clear();
pts.addAll(points);
setDirty();
}
/**
* @return iterator over points
*/
@Override
public Iterator<IROI> iterator() {
return pts.iterator();
}
@Override
public PolylineROI copy() {
PolylineROI c = new PolylineROI(spt.clone());
for (int i = 1, imax = pts.size(); i < imax; i++)
c.insertPoint(pts.get(i).copy());
c.name = name;
c.plot = plot;
return c;
}
/**
* Remove point at given index
* @param i
*/
public void removePoint(int i) {
pts.remove(i);
setDirty();
}
@Override
public void removeAllPoints() {
pts.clear();
setDirty();
}
@Override
public String toString() {
/**
* You cannot have pts.toString() if the pts contains this! It
* is a recursive call. Fix to defect https://trac/scientific_software/ticket/1943
*/
if (pts.contains(this)) return super.toString();
return pts.toString();
}
@Override
public Dataset[] makeCoordinateDatasets() {
int n = pts.size();
double[] x = new double[n];
double[] y = new double[n];
for (int i = 0; i < n; i++) {
double[] p = pts.get(i).getPointRef();
x[i] = p[0];
y[i] = p[1];
}
return new Dataset[] { DatasetFactory.createFromObject(x), DatasetFactory.createFromObject(y) };
}
protected Set<Double> calculateHorizontalIntersections(final double y) {
double[] xi;
double[] pta;
double[] ptb = null;
Set<Double> values = new TreeSet<Double>();
Iterator<IROI> it = pts.iterator();
ptb = it.next().getPointRef();
while (it.hasNext()) {
pta = ptb;
ptb = it.next().getPointRef();
xi = ROIUtils.findYIntersection(pta, ptb, y);
if (xi != null) {
for (double x : xi)
values.add(x);
}
}
return values;
}
@Override
public double[] findHorizontalIntersections(final double y) {
if (!intersectHorizontal(y))
return null;
if (pts.size() == 1) {
return pts.get(0).findHorizontalIntersections(y);
}
Set<Double> values = calculateHorizontalIntersections(y);
double[] xi = new double[values.size()];
int i = 0;
for (Double d : values) {
xi[i++] = d;
}
return xi;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((pts == null) ? 0 : pts.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
PolylineROI other = (PolylineROI) obj;
if (pts == null) {
if (other.pts != null)
return false;
} else if (!pts.equals(other.pts))
return false;
return true;
}
}