//----------------------------------------------------------------------------// // // // B a r l i n e // // // //----------------------------------------------------------------------------// // <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.score.entity; import omr.glyph.Glyphs; import omr.glyph.Shape; import omr.glyph.facets.Glyph; import omr.run.Orientation; import omr.score.visitor.ScoreVisitor; import omr.util.Predicate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.BasicStroke; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.Stroke; import java.util.Collection; import java.util.HashMap; import java.util.Map; /** * Class {@code Barline} encapsulates a logical bar line, that may be * composed of several physical components : repeat dots, thin and * thick bars. * * @author Hervé Bitteur */ public class Barline extends PartNode { //~ Static fields/initializers --------------------------------------------- /** Usual logger utility */ private static final Logger logger = LoggerFactory.getLogger(Barline.class); /** Predicate to detect a barline glyph (not a repeat dot) */ public static final Predicate<Glyph> linePredicate = new Predicate<Glyph>() { @Override public boolean check (Glyph glyph) { Shape shape = glyph.getShape(); return (shape == Shape.PART_DEFINING_BARLINE) || (shape == Shape.THIN_BARLINE) || (shape == Shape.THICK_BARLINE); } }; //~ Instance fields -------------------------------------------------------- /** Precise bar line shape */ private Shape shape; /** Signature of this bar line, as inferred from its components */ private String signature; //~ Constructors ----------------------------------------------------------- //---------// // Barline // //---------// /** * Create a bar line, in a containing measure * * @param measure the containing measure */ public Barline (Measure measure) { super(measure); } //~ Methods ---------------------------------------------------------------- //--------// // accept // //--------// @Override public boolean accept (ScoreVisitor visitor) { return visitor.visit(this); } //------------// // forceShape // //------------// /** * Normally, shape should be inferred from the signature of stick * combination that compose the bar line, so this method is provided * only for the (rare) cases when we want to force the barline shape * * @param shape the forced shape */ public void forceShape (Shape shape) { this.shape = shape; } //----------// // getLeftX // //----------// /** * Report the abscissa of the left side of the bar line * * @return abscissa of the left side */ public int getLeftX () { Rectangle box = getBox(); int middleY = box.y + box.height / 2; for (Glyph glyph : getGlyphs()) { if (linePredicate.check(glyph)) { int x = glyph.getLine().xAtY(middleY); return x; } } // No usable stick addError("No usable stick to compute barline abscissa"); return 0; } //-----------// // getRightX // //-----------// /** * Report the abscissa of the right side of the bar line * * @return abscissa of the right side */ public int getRightX () { int right = 0; Rectangle box = getBox(); int middleY = box.y + box.height / 2; for (Glyph glyph : getGlyphs()) { if (glyph.isBar()) { int x = glyph.getLine().xAtY(middleY); if (x > right) { right = x; } } } return right; } //----------// // getShape // //----------// /** * Report the shape of this bar line * * @return the (lazily determined) shape */ public Shape getShape () { if (shape == null) { // Use the map of signatures shape = Signatures.map.get(getSignature()); } return shape; } //----------------// // joinsAllStaves // //----------------// /** * Check whether all staves are physically connected by the sticks of the * barline * * @param staves the collection of staves to check * @return true if the barline touches all staves, false otherwise */ public boolean joinsAllStaves (Collection<Staff> staves) { // We check that the barline box intersects each staff box Rectangle barBox = getBox(); for (Staff staff : staves) { if (!barBox.intersects(staff.getBox())) { return false; } } return true; } //-----------// // mergeWith // //-----------// /** * Merge into this bar line the components of another bar line * * @param other the other (merged) stick */ public void mergeWith (Barline other) { for (Glyph glyph : other.getGlyphs()) { addGlyph(glyph); } } //--------// // render // //--------// /** * Render the bar contour, with proper strokes according to the * thickness of each barline component * * @param g the graphics context */ public void render (Graphics2D g) { Stroke oldStroke = g.getStroke(); for (Glyph glyph : getGlyphs()) { if (glyph.isBar()) { float thickness = (float) glyph.getWeight() / glyph.getLength( Orientation.VERTICAL); g.setStroke(new BasicStroke(thickness)); glyph.renderLine(g); } } g.setStroke(oldStroke); } //------------// // renderLine // //------------// /** * Render the axis of each component of the bar * * @param g the graphics context */ public void renderLine (Graphics2D g) { for (Glyph glyph : getGlyphs()) { if (glyph.isBar()) { glyph.renderLine(g); } } } //-------// // reset // //-------// /** * Invalidate cached data, so that it gets lazily recomputed when needed */ @Override public void reset () { super.reset(); signature = null; shape = null; } //----------// // toString // //----------// /** * Report a readable description * * @return a string based on main members */ @Override public String toString () { StringBuilder sb = new StringBuilder(); sb.append("{Barline"); try { sb.append(" ").append(getShape()).append(" center=").append( getCenter()).append(" sig=").append(getSignature()).append(Glyphs. toString(" glyphs", glyphs)); } catch (NullPointerException e) { sb.append(" INVALID"); } sb.append("}"); return sb.toString(); } //-----------------// // translateGlyphs // //-----------------// /** * Make all barline glyphs point to it as the translated entity */ public void translateGlyphs () { for (Glyph glyph : getGlyphs()) { glyph.setTranslation(this); } } //----------// // getChars // //----------// /** * Report the sequence of chars that describes the provided shape * * @param shape the provided shape * @return a sequence of chars */ private String getChars (Shape shape) { if (shape == null) { logger.warn("Barline. getChars() for null shape"); return null; } switch (shape) { case THICK_BARLINE: return "K"; case THIN_BARLINE: case PART_DEFINING_BARLINE: return "N"; case DOUBLE_BARLINE: return "NN"; case FINAL_BARLINE: return "NK"; case REVERSE_FINAL_BARLINE: return "KN"; case LEFT_REPEAT_SIGN: return "KNO"; case RIGHT_REPEAT_SIGN: return "ONK"; case BACK_TO_BACK_REPEAT_SIGN: return "ONKNO"; case DOT_set: case REPEAT_DOT: return "O"; // Capital o (not zero) default: addError("Unknown bar component : " + shape); return null; } } //--------------// // getSignature // //--------------// /** * Compute a signature for this barline, based on the composing sticks. * We elaborate this signature for first staff of the part only, to get rid * of sticks roughly one above the other */ private String getSignature () { if (signature == null) { final Measure measure = (Measure) getParent(); final ScoreSystem system = measure.getSystem(); final StringBuilder sb = new StringBuilder(); final Staff staffRef = measure.getPart().getFirstStaff(); final int topStaff = staffRef.getTopLeft().y; final int botStaff = topStaff + staffRef.getHeight(); String last = null; // Last stick for (Glyph glyph : getGlyphs()) { String chars = getChars(glyph.getShape()); if (chars != null) { if (chars.equals("O")) { // DOT_set Staff staff = system.getStaffAt(glyph.getLocation()); if (staff != staffRef) { continue; } } else { // BAR : Check overlap with staff reference Rectangle box = glyph.getBounds(); if (Math.max(box.y, topStaff) > Math.min( box.y + box.height, botStaff)) { continue; } } if (last == null) { sb.append(chars); } else { if (last.equals(chars)) { if (chars.equals("N")) { sb.append(chars); } } else { sb.append(chars); } } last = chars; } } signature = sb.toString(); logger.debug("sig={}", sb); } return signature; } //~ Inner Classes ---------------------------------------------------------- //------------// // Signatures // //------------// private static class Signatures { //~ Static fields/initializers ----------------------------------------- public static final Map<String, Shape> map = new HashMap<>(); static { map.put("N", Shape.THIN_BARLINE); map.put("NN", Shape.DOUBLE_BARLINE); map.put("NK", Shape.FINAL_BARLINE); map.put("KN", Shape.REVERSE_FINAL_BARLINE); map.put("ONK", Shape.RIGHT_REPEAT_SIGN); map.put("KNO", Shape.LEFT_REPEAT_SIGN); map.put("ONKNO", Shape.BACK_TO_BACK_REPEAT_SIGN); map.put("NKNO", Shape.BACK_TO_BACK_REPEAT_SIGN); // For convenience map.put("ONKN", Shape.BACK_TO_BACK_REPEAT_SIGN); // For convenience map.put("NKN", Shape.BACK_TO_BACK_REPEAT_SIGN); // For convenience } private Signatures () { } } }