//----------------------------------------------------------------------------// // // // L e d g e r P a t t e r n // // // //----------------------------------------------------------------------------// // <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.pattern; import omr.constant.ConstantSet; import omr.glyph.CompoundBuilder; import omr.glyph.Evaluation; import omr.glyph.Grades; import omr.glyph.Shape; import omr.glyph.ShapeSet; import omr.glyph.facets.Glyph; import omr.grid.StaffInfo; import omr.lag.Section; import omr.run.Orientation; import omr.sheet.HorizontalsBuilder; import omr.sheet.Scale; import omr.sheet.SystemInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.Rectangle; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.EnumSet; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.SortedSet; /** * Class {@code LedgerPattern} checks the related system for invalid ledgers. * * @author Hervé Bitteur */ public class LedgerPattern extends GlyphPattern { //~ Static fields/initializers --------------------------------------------- /** Specific application parameters */ private static final Constants constants = new Constants(); /** Usual logger utility */ private static final Logger logger = LoggerFactory.getLogger(LedgerPattern.class); /** Shapes acceptable for a ledger neighbor */ public static final EnumSet<Shape> ledgerNeighbors = EnumSet.noneOf( Shape.class); static { // ledgerNeighbors.add(Shape.GRACE_NOTE_SLASH); // ledgerNeighbors.add(Shape.GRACE_NOTE_NO_SLASH); ledgerNeighbors.addAll(ShapeSet.Notes.getShapes()); ledgerNeighbors.addAll(ShapeSet.NoteHeads.getShapes()); } //~ Instance fields -------------------------------------------------------- /** Companion in charge of building ledgers */ private final HorizontalsBuilder builder; /** Scale-dependent parameters */ final int interChunkDx; final int interChunkDy; //~ Constructors ----------------------------------------------------------- //---------------// // LedgerPattern // //---------------// /** * Creates a new LedgerPattern object. * * @param system the related system */ public LedgerPattern (SystemInfo system) { super("Ledger", system); builder = system.getHorizontalsBuilder(); interChunkDx = scale.toPixels(constants.interChunkDx); interChunkDy = scale.toPixels(constants.interChunkDy); } //~ Methods ---------------------------------------------------------------- //------------// // runPattern // //------------// @Override public int runPattern () { int nb = 0; for (StaffInfo staff : system.getStaves()) { Map<Integer, SortedSet<Glyph>> ledgerMap = staff.getLedgerMap(); for (Iterator<Entry<Integer, SortedSet<Glyph>>> iter = ledgerMap. entrySet().iterator(); iter.hasNext();) { Entry<Integer, SortedSet<Glyph>> entry = iter.next(); SortedSet<Glyph> ledgerSet = entry.getValue(); List<Glyph> ledgerGlyphs = new ArrayList<>(); for (Glyph ledger : ledgerSet) { ledgerGlyphs.add(ledger); } // Process for (Iterator<Glyph> it = ledgerSet.iterator(); it.hasNext();) { Glyph ledger = it.next(); Set<Glyph> neighbors = new HashSet<>(); if (isInvalid(ledger, neighbors)) { // Check if we can forge a ledger-compatible neighbor Glyph compound = system.buildCompound( ledger, false, system.getGlyphs(), new LedgerAdapter( system, Grades.ledgerNoteMinGrade, ledgerNeighbors, ledgerGlyphs)); if (compound == null) { // Here, we have not found any convincing neighbor // Let's invalid this pseudo ledger logger.debug("Invalid ledger {}", ledger); ledger.setShape(null); it.remove(); nb++; } } } if (ledgerSet.isEmpty()) { iter.remove(); } } } return nb; } //-----------// // isInvalid // //-----------// private boolean isInvalid (Glyph ledgerGlyph, Set<Glyph> neighborGlyphs) { // A short ledger must be stuck to either a note head or a stem // (or a grace note) List<Section> allSections = new ArrayList<>(); for (Section section : ledgerGlyph.getMembers()) { allSections.addAll(section.getSources()); allSections.addAll(section.getTargets()); allSections.addAll(section.getOppositeSections()); } for (Section sct : allSections) { Glyph g = sct.getGlyph(); if ((g != null) && (g != ledgerGlyph)) { neighborGlyphs.add(g); } } for (Glyph glyph : neighborGlyphs) { Shape shape = glyph.getShape(); if ((shape == Shape.STEM) || ledgerNeighbors.contains(shape)) { return false; } } // If this a long ledger, check farther from the staff for a note with // a ledger (full or chunk) if (builder.isFullLedger(ledgerGlyph)) { return false; } return true; } //~ Inner Classes ---------------------------------------------------------- //-----------// // Constants // //-----------// private static final class Constants extends ConstantSet { //~ Instance fields ---------------------------------------------------- Scale.Fraction interChunkDx = new Scale.Fraction( 1.5, "Max horizontal distance between ledger chunks"); // Scale.Fraction interChunkDy = new Scale.Fraction( 0.2, "Max vertical distance between ledger chunks"); } //---------------// // LedgerAdapter // //---------------// /** * Adapter to actively search a ledger-compatible entity near the ledger * chunk. */ private final class LedgerAdapter extends CompoundBuilder.TopShapeAdapter { //~ Instance fields ---------------------------------------------------- private final List<Glyph> ledgerGlyphs; //~ Constructors ------------------------------------------------------- public LedgerAdapter (SystemInfo system, double minGrade, EnumSet<Shape> desiredShapes, List<Glyph> ledgerGlyphs) { super(system, minGrade, desiredShapes); this.ledgerGlyphs = ledgerGlyphs; } //~ Methods ------------------------------------------------------------ @Override public Rectangle computeReferenceBox () { Point2D stop = seed.getStopPoint(Orientation.HORIZONTAL); Rectangle rect = new Rectangle( (int) Math.rint(stop.getX()), (int) Math.rint(stop.getY()), interChunkDx, 0); rect.grow(0, interChunkDy); seed.addAttachment("-", rect); return rect; } @Override public Evaluation getChosenEvaluation () { return new Evaluation(chosenEvaluation.shape, Evaluation.ALGORITHM); } @Override public boolean isCandidateSuitable (Glyph glyph) { return !ledgerGlyphs.contains(glyph); } } }