//----------------------------------------------------------------------------// // // // G e o P a t h // // // //----------------------------------------------------------------------------// // <editor-fold defaultstate="collapsed" desc="hdr"> // // Copyright © Hervé Bitteur and others 2000-2013. All rights reserved. // // This software is released under the GNU General Public License. // // Goto http://kenai.com/projects/audiveris to report bugs or suggestions. // //----------------------------------------------------------------------------// // </editor-fold> package omr.math; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Path2D; import java.awt.geom.PathIterator; import static java.awt.geom.PathIterator.*; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; /** * Class {@code GeoPath} is a Path2D.Double with some additions * * @author Hervé Bitteur */ public class GeoPath extends Path2D.Double { //~ Constructors ----------------------------------------------------------- //---------// // GeoPath // //---------// /** * Creates a new GeoPath object. */ public GeoPath () { } //---------// // GeoPath // //---------// /** * Creates a new GeoPath object. * * @param s the specified {@code Shape} object */ public GeoPath (Shape s) { this(s, null); } //---------// // GeoPath // //---------// /** * Creates a new GeoPath object. * * @param s the specified {@code Shape} object * @param at the specified {@code AffineTransform} object */ public GeoPath (Shape s, AffineTransform at) { super(s, at); } //~ Methods ---------------------------------------------------------------- //---------// // labelOf // //---------// /** * Report the kind label of a segment. * * @param segmentKind the int-based segment kind * @return the label for the curve */ public static String labelOf (int segmentKind) { switch (segmentKind) { case SEG_MOVETO: return "SEG_MOVETO"; case SEG_LINETO: return "SEG_LINETO"; case SEG_QUADTO: return "SEG_QUADTO"; case SEG_CUBICTO: return "SEG_CUBICTO"; case SEG_CLOSE: return "SEG_CLOSE"; default: throw new RuntimeException("Illegal segmentKind " + segmentKind); } } //------------// // intersects // //------------// /** * Check whether the flattened path intersects the provided rectangle. * * @param rect the provided rectangle to check for intersection * @param flatness maximum distance used for line segment approximation * @return true if intersection found */ public boolean intersects (Rectangle2D rect, double flatness) { final double[] buffer = new double[6]; double x1 = 0; double y1 = 0; for (PathIterator it = getPathIterator(null, flatness); !it.isDone(); it.next()) { int segmentKind = it.currentSegment(buffer); int count = countOf(segmentKind); final double x2 = buffer[count - 2]; final double y2 = buffer[count - 1]; switch (segmentKind) { case SEG_MOVETO: x1 = x2; y1 = y2; break; case SEG_LINETO: if (rect.intersectsLine(x1, y1, x2, y2)) { return true; } break; case SEG_CLOSE: break; default: throw new RuntimeException( "Illegal segmentKind " + segmentKind); } } return false; } //----------// // toString // //----------// @Override public String toString () { StringBuilder sb = new StringBuilder("{"); sb.append(getClass().getSimpleName()); double[] buffer = new double[6]; for (PathIterator it = getPathIterator(null); !it.isDone(); it.next()) { int segmentKind = it.currentSegment(buffer); sb.append(" ") .append(labelOf(segmentKind)) .append("("); int coords = countOf(segmentKind); boolean firstCoord = true; for (int ic = 0; ic < (coords - 1); ic += 2) { if (!firstCoord) { sb.append(","); firstCoord = false; } sb.append("[") .append((float) buffer[ic]) .append(",") .append((float) buffer[ic + 1]) .append("]"); } sb.append(")"); } sb.append("}"); return sb.toString(); } //------// // xAtY // //------// /** * Report the abscissa value of the spline at provided ordinate * (assuming true function) * * @param y the provided ordinate * @return the abscissa value at this ordinate */ public double xAtY (double y) { final double[] buffer = new double[6]; final Point2D.Double p1 = new Point2D.Double(); final Point2D.Double p2 = new Point2D.Double(); final int segmentKind = getYSegment(y, buffer, p1, p2); final double t = (y - p1.y) / (p2.y - p1.y); final double u = 1 - t; switch (segmentKind) { case SEG_LINETO: return p1.x + (t * (p2.x - p1.x)); case SEG_QUADTO: { double cpx = buffer[0]; return (p1.x * u * u) + (2 * cpx * t * u) + (p2.x * t * t); } case SEG_CUBICTO: { double cpx1 = buffer[0]; double cpx2 = buffer[2]; return (p1.x * u * u * u) + (3 * cpx1 * t * u * u) + (3 * cpx2 * t * t * u) + (p2.x * t * t * t); } default: throw new RuntimeException("Illegal segmentKind " + segmentKind); } } //------// // yAtX // //------// /** * Report the ordinate value of the spline at provided abscissa * (assuming true function) * * @param x the provided abscissa * @return the ordinate value at this abscissa */ public double yAtX (double x) { final double[] buffer = new double[6]; final Point2D.Double p1 = new Point2D.Double(); final Point2D.Double p2 = new Point2D.Double(); final int segmentKind = getXSegment(x, buffer, p1, p2); final double t = (x - p1.x) / (p2.x - p1.x); final double u = 1 - t; switch (segmentKind) { case SEG_LINETO: return p1.y + (t * (p2.y - p1.y)); case SEG_QUADTO: { double cpy = buffer[1]; return (p1.y * u * u) + (2 * cpy * t * u) + (p2.y * t * t); } case SEG_CUBICTO: { double cpy1 = buffer[1]; double cpy2 = buffer[3]; return (p1.y * u * u * u) + (3 * cpy1 * t * u * u) + (3 * cpy2 * t * t * u) + (p2.y * t * t * t); } default: throw new RuntimeException("Illegal segmentKind " + segmentKind); } } //---------// // countOf // //---------// /** * Report how many coordinate values a path segment contains. * * @param segmentKind the int-based segment kind * @return the number of coordinates values */ protected static int countOf (int segmentKind) { switch (segmentKind) { case SEG_MOVETO: case SEG_LINETO: return 2; case SEG_QUADTO: return 4; case SEG_CUBICTO: return 6; case SEG_CLOSE: return 0; default: throw new RuntimeException("Illegal segmentKind " + segmentKind); } } //-------------// // getXSegment // //-------------// /** * Retrieve the first segment of the curve that contains the provided * abscissa * * @param x the provided abscissa * @param buffer output * @param p1 output: start of segment * @param p2 output: end of segment * @return the segment kind */ protected int getXSegment (double x, double[] buffer, Point2D.Double p1, Point2D.Double p2) { PathIterator it = getPathIterator(null); double x1 = 0; double y1 = 0; while (!it.isDone()) { final int segmentKind = it.currentSegment(buffer); final int count = countOf(segmentKind); final double x2 = buffer[count - 2]; final double y2 = buffer[count - 1]; if ((segmentKind == SEG_MOVETO) || (segmentKind == SEG_CLOSE) || (x > x2)) { // Move to next segment x1 = x2; y1 = y2; it.next(); } else { p1.x = x1; p1.y = y1; p2.x = x2; p2.y = y2; return segmentKind; } } // Not found throw new RuntimeException("Abscissa not in range: " + x); } //-------------// // getYSegment // //-------------// /** * Retrieve the first segment of the curve that contains the provided * ordinate * * @param y the provided ordinate * @param buffer output * @param p1 output: start of segment * @param p2 output: end of segment * @return the segment kind */ protected int getYSegment (double y, double[] buffer, Point2D.Double p1, Point2D.Double p2) { PathIterator it = getPathIterator(null); double x1 = 0; double y1 = 0; while (!it.isDone()) { final int segmentKind = it.currentSegment(buffer); final int count = countOf(segmentKind); final double x2 = buffer[count - 2]; final double y2 = buffer[count - 1]; if ((segmentKind == SEG_MOVETO) || (segmentKind == SEG_CLOSE) || (y > y2)) { // Move to next segment x1 = x2; y1 = y2; it.next(); } else { p1.x = x1; p1.y = y1; p2.x = x2; p2.y = y2; return segmentKind; } } // Not found throw new RuntimeException("Ordinate not in range: " + y); } }