//----------------------------------------------------------------------------// // // // B a s i c A l i g n m e n t // // // //----------------------------------------------------------------------------// // <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.glyph.facets; import omr.constant.ConstantSet; import omr.glyph.Glyphs; import omr.lag.Section; import omr.math.Barycenter; import omr.math.BasicLine; import omr.math.Line; import omr.run.Orientation; import static omr.run.Orientation.*; import omr.run.Run; import omr.sheet.Scale; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.geom.Point2D; /** * Class {@code BasicAlignment} implements a basic handling of * Alignment facet * * @author Hervé Bitteur */ public class BasicAlignment extends BasicFacet implements GlyphAlignment { //~ Static fields/initializers --------------------------------------------- /** Specific application parameters */ private static final Constants constants = new Constants(); /** Usual logger utility */ private static final Logger logger = LoggerFactory.getLogger( BasicAlignment.class); //~ Instance fields -------------------------------------------------------- /** Best (curved or straight) line equation */ protected Line line; /** Absolute slope of the line wrt abscissa axis */ protected Double slope; /** Absolute beginning point */ protected Point2D startPoint; /** Absolute ending point */ protected Point2D stopPoint; //~ Constructors ----------------------------------------------------------- /** * Create a new BasicAlignment object * * @param glyph our glyph */ public BasicAlignment (Glyph glyph) { super(glyph); } //~ Methods ---------------------------------------------------------------- //-----------// // getAspect // //-----------// @Override public final double getAspect (Orientation orientation) { Rectangle box = glyph.getBounds(); if (orientation == HORIZONTAL) { return (double) box.width / (double) box.height; } else { return (double) box.height / (double) box.width; } } //---------------// // getFirstStuck // //---------------// @Override public int getFirstStuck () { int stuck = 0; for (Section section : glyph.getMembers()) { Run sectionRun = section.getFirstRun(); for (Section sct : section.getSources()) { if (!sct.isGlyphMember() || (sct.getGlyph() != glyph)) { stuck += sectionRun.getCommonLength(sct.getLastRun()); } } } return stuck; } //------------------// // getIntPositionAt // //------------------// public int getIntPositionAt (double coord, Orientation orientation) { return (int) Math.rint(getPositionAt(coord, orientation)); } //------------------// // getInvertedSlope // //------------------// @Override public double getInvertedSlope () { checkLine(); return (stopPoint.getX() - startPoint.getX()) / (stopPoint.getY() - startPoint.getY()); } //--------------// // getLastStuck // //--------------// @Override public int getLastStuck () { int stuck = 0; for (Section section : glyph.getMembers()) { Run sectionRun = section.getLastRun(); for (Section sct : section.getTargets()) { if (!sct.isGlyphMember() || (sct.getGlyph() != glyph)) { stuck += sectionRun.getCommonLength(sct.getFirstRun()); } } } return stuck; } //-----------// // getLength // //-----------// @Override public final int getLength (Orientation orientation) { Rectangle box = glyph.getBounds(); if (orientation == HORIZONTAL) { return box.width; } else { return box.height; } } //---------// // getLine // //---------// @Override public Line getLine () { checkLine(); return line; } //-----------------// // getMeanDistance // //-----------------// @Override public double getMeanDistance () { if (line == null) { computeLine(); } return line.getMeanDistance(); } //------------------// // getMeanThickness // //------------------// @Override public double getMeanThickness (Orientation orientation) { return (double) glyph.getWeight() / getLength(orientation); } //-----------// // getMidPos // //-----------// @Override public int getMidPos (Orientation orientation) { if (orientation == VERTICAL) { return (int) Math.rint( (getStartPoint(orientation) .getX() + getStopPoint(orientation) .getX()) / 2.0); } else { return (int) Math.rint( (getStartPoint(orientation) .getY() + getStopPoint(orientation) .getY()) / 2.0); } } //---------------// // getPositionAt // //---------------// @Override public double getPositionAt (double coord, Orientation orientation) { if (orientation == HORIZONTAL) { return getLine() .yAtX(coord); } else { return getLine() .xAtY(coord); } } //----------------------// // getRectangleCentroid // //----------------------// /** * Report the absolute centroid of all the glyph pixels found in the * provided absolute ROI * * @param absRoi the desired absolute region of interest * @return the absolute barycenter of the pixels found */ @Override public Point2D getRectangleCentroid (Rectangle absRoi) { Barycenter barycenter = new Barycenter(); for (Section section : glyph.getMembers()) { section.cumulate(barycenter, absRoi); } if (barycenter.getWeight() != 0) { return new Point2D.Double(barycenter.getX(), barycenter.getY()); } else { return null; } } //---------------// // getStartPoint // //---------------// @Override public Point2D getStartPoint (Orientation orientation) { checkLine(); if (orientation == Orientation.HORIZONTAL) { // Use left side if (startPoint.getX() <= stopPoint.getX()) { return startPoint; } else { return stopPoint; } } else { // Use top side if (startPoint.getY() <= stopPoint.getY()) { return startPoint; } else { return stopPoint; } } } //--------------// // getStopPoint // //--------------// @Override public Point2D getStopPoint (Orientation orientation) { checkLine(); if (orientation == Orientation.HORIZONTAL) { // Use right side if (stopPoint.getX() >= startPoint.getX()) { return stopPoint; } else { return startPoint; } } else { // Use bottom side if (stopPoint.getY() >= startPoint.getY()) { return stopPoint; } else { return startPoint; } } } //--------------// // getThickness // //--------------// @Override public final int getThickness (Orientation orientation) { Rectangle box = glyph.getBounds(); if (orientation == HORIZONTAL) { return box.height; } else { return box.width; } } //---------------// // getProbeWidth // //---------------// /** * Report the width of the window used to determine filament ordinate * * @return the scale-independent probe width */ public static Scale.Fraction getProbeWidth () { return constants.probeWidth; } //--------// // dumpOf // //--------// /** * Report glyph internal data */ @Override public String dumpOf () { StringBuilder sb = new StringBuilder(); if (startPoint != null) { sb.append(String.format(" start=%s%n", startPoint)); } if (stopPoint != null) { sb.append(String.format(" stop=%s%n", stopPoint)); } sb.append(String.format(" line=%s%n", getLine())); return sb.toString(); } //----------// // getSlope // //----------// @Override public double getSlope () { if (slope == null) { checkLine(); slope = (stopPoint.getY() - startPoint.getY()) / (stopPoint.getX() - startPoint.getX()); } return slope; } //----------------// // getThicknessAt // //----------------// @Override public double getThicknessAt (double coord, Orientation orientation) { return Glyphs.getThicknessAt(coord, orientation, glyph); } //-----------------// // invalidateCache // //-----------------// @Override public void invalidateCache () { line = null; slope = null; startPoint = stopPoint = null; } //------------// // renderLine // //------------// @Override public void renderLine (Graphics2D g) { if (!glyph.getBounds() .intersects(g.getClipBounds())) { return; } getLine(); // To make sure the line has been computed g.drawLine( (int) Math.rint(startPoint.getX()), (int) Math.rint(startPoint.getY()), (int) Math.rint(stopPoint.getX()), (int) Math.rint(stopPoint.getY())); } //-----------------// // setEndingPoints // //-----------------// @Override public void setEndingPoints (Point2D pStart, Point2D pStop) { glyph.invalidateCache(); this.startPoint = pStart; this.stopPoint = pStop; computeLine(); // Enlarge contour box if needed Rectangle box = glyph.getBounds(); box.add(pStart); box.add(pStop); glyph.setContourBox(box); } //-----------// // checkLine // //-----------// /** * Make sure an approximating line is available */ protected final void checkLine () { if (line == null) { computeLine(); } } //-------------// // computeLine // //-------------// protected void computeLine () { line = new BasicLine(); for (Section section : glyph.getMembers()) { line.includeLine(section.getAbsoluteLine()); } Rectangle box = glyph.getBounds(); // We have a problem if glyph is just 1 pixel: no computable slope! if (glyph.getWeight() <= 1) { startPoint = stopPoint = box.getLocation(); slope = 0d; // Why not? we just need a value. return; } slope = line.getSlope(); double top = box.y; double bot = (box.y + box.height) - 1; double left = box.x; double right = (box.x + box.width) - 1; if (isRatherVertical()) { // Use line intersections with top & bottom box sides startPoint = new Point2D.Double(line.xAtY(top), top); stopPoint = new Point2D.Double(line.xAtY(bot), bot); if (!line.isVertical()) { Point2D pLeft = new Point2D.Double(left, line.yAtX(left)); Point2D pRight = new Point2D.Double(right, line.yAtX(right)); if (line.getInvertedSlope() > 0) { if (pLeft.getY() > startPoint.getY()) { startPoint = pLeft; } if (pRight.getY() < stopPoint.getY()) { stopPoint = pRight; } } else { if (pRight.getY() > startPoint.getY()) { startPoint = pRight; } if (pLeft.getY() < stopPoint.getY()) { stopPoint = pLeft; } } } } else { // Use line intersections with left & right box sides startPoint = new Point2D.Double(left, line.yAtX(left)); stopPoint = new Point2D.Double(right, line.yAtX(right)); if (!line.isHorizontal()) { Point2D pTop = new Point2D.Double(line.xAtY(top), top); Point2D pBot = new Point2D.Double(line.xAtY(bot), bot); if (slope > 0) { if (pTop.getX() > startPoint.getX()) { startPoint = pTop; } if (pBot.getX() < stopPoint.getX()) { stopPoint = pBot; } } else { if (pBot.getX() > startPoint.getX()) { startPoint = pBot; } if (pTop.getX() < stopPoint.getX()) { stopPoint = pTop; } } } } } //------------------// // isRatherVertical // //------------------// /** * Report whether the angle of the approximating line is outside * the range [-PI/4 - +PI/4]. * * @return true if rather vertical, false for rather horizontal */ private boolean isRatherVertical () { if (slope == null) { computeLine(); } return Math.abs(slope) > (Math.PI / 4); } //~ Inner Classes ---------------------------------------------------------- //-----------// // Constants // //-----------// private static final class Constants extends ConstantSet { //~ Instance fields ---------------------------------------------------- final Scale.Fraction probeWidth = new Scale.Fraction( 0.5, "Width of probing window to retrieve Glyph ordinate"); } }