/*
* $Id$
*
* Copyright 2009 Glencoe Software, Inc. All rights reserved.
* Use is subject to license terms supplied in LICENSE.txt
*
*/
package omero.model;
import static omero.rtypes.rdouble;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.apache.batik.parser.AWTPathProducer;
import org.apache.batik.parser.DefaultPointsHandler;
import org.apache.batik.parser.PathParser;
import org.apache.batik.parser.PointsHandler;
import org.apache.batik.parser.PointsParser;
import org.apache.xerces.impl.xpath.regex.ParseException;
/**
* Orthogonal interface hierarchy of types for working with the
* {@link omero.model.Shape} hierarchy.
*
* @since Beta4.1
*/
public interface SmartShape {
/**
* Utility class used as a mixin by all of the {@link SmartShape}
* implementations. The inheritance hierarchy of Ice-generated objects
* doesn't allow for simply subclassing .
*/
public static class Util {
/**
* Used from assert statements of the form:
*
* <pre>
* assert Util.checkNonNull(points) : "Null points in " + this;
* </pre>
*
* in all the implementations of {@link SmartShape#asPoints()}.
*
* @param points the points
* @return false if iterating through the points list and dereferencing
* the cx and cy fields would cause a
* {@link NullPointerException}
*/
public static boolean checkNonNull(List<Point> points) {
if (points == null) {
return false;
}
for (Point point : points) {
if (point == null || point.cx == null || point.cy == null) {
return false;
}
}
return true;
}
public static void appendDbPoint(StringBuilder sb, Point p) {
appendDbPoint(sb, p.cx.getValue(), p.cy.getValue());
}
public static void appendDbPoint(StringBuilder sb, double cx, double cy) {
sb.append("(");
sb.append(cx);
sb.append(",");
sb.append(cy);
sb.append(")");
}
public static void appendSvgPoint(StringBuilder sb, Point p) {
appendSvgPoint(sb, p.cx.getValue(), p.cy.getValue());
}
public static void appendSvgPoint(StringBuilder sb, double cx, double cy) {
sb.append(cx);
sb.append(",");
sb.append(cy);
sb.append(" ");
}
public static boolean appendSegement(StringBuilder sb, boolean first,
double cx, double cy) {
if (first) {
sb.append("M ");
first = false;
} else {
sb.append("L ");
}
sb.append(cx);
sb.append(" ");
sb.append(cy);
sb.append(" ");
return first;
}
public static String pointsToPath(List<Point> points, boolean close) {
StringBuilder sb = new StringBuilder(points.size() * 16);
boolean first = true;
for (Point point : points) {
double cx = point.getCx().getValue();
double cy = point.getCy().getValue();
first = appendSegement(sb, first, cx, cy);
}
if (close) {
sb.append("Z");
}
String path = sb.toString();
return path;
}
public static String parsePointsToPath(String str, boolean close) {
final StringBuilder sb = new StringBuilder();
final boolean[] first = new boolean[] { true };
PointsParser pp = new PointsParser();
PointsHandler ph = new DefaultPointsHandler() {
public void point(float x, float y) throws ParseException {
first[0] = appendSegement(sb, first[0], x, y);
}
};
pp.setPointsHandler(ph);
pp.parse(str);
return sb.toString();
}
public static Shape parseAwtPath(String str) {
PathParser pp = new PathParser();
AWTPathProducer ph = new AWTPathProducer();
pp.setPathHandler(ph);
pp.parse(str);
return ph.getShape();
}
public static List<Point> parsePoints(String str) {
final List<Point> points = new ArrayList<Point>();
PointsParser pp = new PointsParser();
PointsHandler ph = new DefaultPointsHandler() {
public void point(float x, float y) throws ParseException {
SmartPointI sp = new SmartPointI();
sp.setCx(rdouble(x));
sp.setCy(rdouble(y));
points.add(sp);
}
};
pp.setPointsHandler(ph);
pp.parse(str);
return points;
}
/**
* Returns the four corner points of a rectangle
*
* @param x
* the top-left corner's x coordinate (the lowest x)
* @param y
* the top-left corner's y coordinate (the lowest y)
* @param w
* width of the rectangle so that x+w gives the highest x
* @param h
* height of the rectange so taht y+h gives the highest y
* @return a list of points of the form: (x,y),(x+w,y),(x+w,y+h),(x,y+h)
*/
public static List<Point> points(double x, double y, double w, double h) {
omero.RDouble x0 = rdouble(x);
omero.RDouble y0 = rdouble(y);
omero.RDouble x1 = rdouble(x + w);
omero.RDouble y1 = rdouble(y + h);
List<Point> points = new ArrayList<Point>();
SmartPointI tl = new SmartPointI();
tl.setCx(x0);
tl.setCy(y0);
points.add(tl);
SmartPointI tr = new SmartPointI();
tr.setCx(x1);
tr.setCy(y0);
points.add(tr);
SmartPointI br = new SmartPointI();
br.setCx(x1);
br.setCy(y1);
points.add(br);
SmartPointI bl = new SmartPointI();
bl.setCx(x0);
bl.setCy(y1);
points.add(bl);
return points;
}
public static void pointsByBoundingBox(Shape s, Rectangle2D r,
PointCallback cb) {
double xEnd = (r.getX() + r.getWidth());
double yEnd = (r.getY() + r.getHeight());
double startX = r.getX();
double startY = r.getY();
for (double y = startY; y < yEnd; ++y) {
for (double x = startX; x < xEnd; ++x) {
if (s.intersects(x, y, 0.001, 0.001)) {
cb.handle((int) x, (int) y);
}
}
}
}
}
/**
* Callback interface passed every point which is within the area of this
* shape. This prevents having all the points in memory at the same time. An
* implementation that would like to collect all the points can do something
* like:
*
* <pre>
* final List<Integer> xs = ...;
* final List<Integer> ys = ...;
* PointCallback cb = new PointCallback(){
* void handle(int x, int y) {
* xs.add(x);
* ys.add(y);
* };
* };
* </pre>
*/
public interface PointCallback {
void handle(int x, int y);
}
/**
* Calls the {@link PointCallback} with all of the x/y coordinates which are
* within the shape.
* @param action the callback to call
*/
void areaPoints(PointCallback action);
/**
* Converts the current {@link SmartShape} to a {@link java.awt.Shape}. This
* is useful for determining paths and included points.
* @return the AWT shape
*/
java.awt.Shape asAwtShape();
/**
* Provides some, possibly lossy, bounding polygon of this
* {@link SmartShape} via points.
* @return the bounding polygon
*/
List<Point> asPoints();
/**
* Initializes this shape with completely random data.
* @param random a random number generator
*/
void randomize(Random random);
}