//----------------------------------------------------------------------------// // // // G l y p h s // // // //----------------------------------------------------------------------------// // <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; import omr.glyph.facets.BasicAlignment; import omr.glyph.facets.Glyph; import omr.lag.Section; import omr.math.PointsCollector; import omr.run.Orientation; import omr.sheet.Scale; import omr.util.Predicate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.Polygon; import java.awt.Rectangle; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.EnumSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; /** * Class {@code Glyphs} is a collection of static convenient methods, * providing features related to a collection of glyphs. * * @author Hervé Bitteur */ public class Glyphs { //~ Static fields/initializers --------------------------------------------- /** Usual logger utility */ private static final Logger logger = LoggerFactory.getLogger(Glyphs.class); /** Predicate to check for a manual shape */ public static final Predicate<Glyph> manualPredicate = new Predicate<Glyph>() { @Override public boolean check (Glyph glyph) { return glyph.isManualShape(); } }; /** Predicate to check for a barline shape */ public static final Predicate<Glyph> barPredicate = new Predicate<Glyph>() { @Override public boolean check (Glyph glyph) { return glyph.isBar(); } }; /** A immutable empty set of glyphs */ public static final Set<Glyph> NO_GLYPHS = Collections.emptySet(); //~ Methods ---------------------------------------------------------------- //----------// // contains // //----------// /** * Check whether a collection of glyphs contains at least one glyph * for which the provided predicate holds true. * * @param glyphs the glyph collection to check * @param predicate the predicate to be used * @return true if there is at least one matching glyph */ public static boolean contains (Collection<Glyph> glyphs, Predicate<Glyph> predicate) { return firstOf(glyphs, predicate) != null; } //-----------------// // containsBarline // //-----------------// /** * Check whether the collection of glyphs contains at least one * barline. * * @param glyphs the collection to check * @return true if one or several glyphs are barlines components */ public static boolean containsBarline (Collection<Glyph> glyphs) { return firstOf(glyphs, barPredicate) != null; } //------------// // containsId // //------------// public static boolean containsId (Collection<Glyph> glyphs, int id) { for (Glyph glyph : glyphs) { if (glyph.getId() == id) { return true; } } return false; } //----------------// // containsManual // //----------------// /** * Check whether a collection of glyphs contains at least one glyph * with a manually assigned shape. * * @param glyphs the glyph collection to check * @return true if there is at least one manually assigned shape */ public static boolean containsManual (Collection<Glyph> glyphs) { return contains(glyphs, manualPredicate); } //---------// // firstOf // //---------// /** * Report the first glyph, if any, for which the provided predicate * holds true. * * @param glyphs the glyph collection to check * @param predicate the glyph predicate * @return the first matching glyph found if any, null otherwise */ public static Glyph firstOf (Collection<Glyph> glyphs, Predicate<Glyph> predicate) { for (Glyph glyph : glyphs) { if (predicate.check(glyph)) { return glyph; } } return null; } //-----------// // getBounds // //-----------// /** * Return the display bounding box of a collection of glyphs. * * @param glyphs the provided collection of glyphs * @return the bounding contour */ public static Rectangle getBounds (Collection<Glyph> glyphs) { Rectangle box = null; for (Glyph glyph : glyphs) { if (box == null) { box = new Rectangle(glyph.getBounds()); } else { box.add(glyph.getBounds()); } } return box; } //----------------------------// // getReverseLengthComparator // //----------------------------// /** * For comparing glyph instances on decreasing length. * * @param orientation the desired orientation reference * @return the comparator */ public static Comparator<Glyph> getReverseLengthComparator ( final Orientation orientation) { return new Comparator<Glyph>() { @Override public int compare (Glyph s1, Glyph s2) { return s2.getLength(orientation) - s1.getLength(orientation); } }; } //----------------// // getThicknessAt // //----------------// /** * Report the resulting thickness of the collection of sticks at * the provided coordinate. * * @param coord the desired coordinate * @param orientation the desired orientation reference * @param glyphs glyphs contributing to the resulting thickness * @return the thickness measured, expressed in number of pixels. */ public static double getThicknessAt (double coord, Orientation orientation, Glyph... glyphs) { return getThicknessAt(coord, orientation, null, glyphs); } //----------------// // getThicknessAt // //----------------// /** * Report the resulting thickness of the collection of sticks at * the provided coordinate. * * @param coord the desired coordinate * @param orientation the desired orientation reference * @param section section contributing to the resulting thickness * @param glyphs glyphs contributing to the resulting thickness * @return the thickness measured, expressed in number of pixels. */ public static double getThicknessAt (double coord, Orientation orientation, Section section, Glyph... glyphs) { if (glyphs.length == 0) { if (section == null) { return 0; } else { return section.getMeanThickness(orientation); } } // Retrieve global bounds Rectangle absBox = null; if (section != null) { absBox = section.getBounds(); } for (Glyph g : glyphs) { if (absBox == null) { absBox = g.getBounds(); } else { absBox.add(g.getBounds()); } } Rectangle oBox = orientation.oriented(absBox); int intCoord = (int) Math.floor(coord); if ((intCoord < oBox.x) || (intCoord >= (oBox.x + oBox.width))) { return 0; } // Use a large-enough collector final Rectangle oRoi = new Rectangle(intCoord, oBox.y, 0, oBox.height); final Scale scale = new Scale(glyphs[0].getInterline()); final int probeHalfWidth = scale.toPixels( BasicAlignment.getProbeWidth()) / 2; oRoi.grow(probeHalfWidth, 0); PointsCollector collector = new PointsCollector( orientation.absolute(oRoi)); // Collect sections contribution for (Glyph g : glyphs) { for (Section sct : g.getMembers()) { sct.cumulate(collector); } } // Contributing section, if any if (section != null) { section.cumulate(collector); } // Case of no pixels found if (collector.getSize() == 0) { return 0; } // Analyze range of Y values int minVal = Integer.MAX_VALUE; int maxVal = Integer.MIN_VALUE; int[] vals = (orientation == Orientation.HORIZONTAL) ? collector.getYValues() : collector.getXValues(); for (int i = 0, iBreak = collector.getSize(); i < iBreak; i++) { int val = vals[i]; minVal = Math.min(minVal, val); maxVal = Math.max(maxVal, val); } return maxVal - minVal + 1; } //----------// // glyphsOf // //----------// /** * Report the set of glyphs that are pointed back by the provided * collection of sections. * * @param sections the provided sections * @return the set of active containing glyphs */ public static Set<Glyph> glyphsOf (Collection<Section> sections) { Set<Glyph> glyphs = new LinkedHashSet<>(); for (Section section : sections) { Glyph glyph = section.getGlyph(); if (glyph != null) { glyphs.add(glyph); } } return glyphs; } //--------------// // lookupGlyphs // //--------------// /** * Look up in a collection of glyphs for <b>all</b> glyphs * contained in a provided rectangle. * * @param collection the collection of glyphs to be browsed * @param rect the coordinates rectangle * @return the glyphs found, which may be an empty list */ public static Set<Glyph> lookupGlyphs ( Collection<? extends Glyph> collection, Rectangle rect) { Set<Glyph> set = new LinkedHashSet<>(); for (Glyph glyph : collection) { if (rect.contains(glyph.getBounds())) { set.add(glyph); } } return set; } //--------------// // lookupGlyphs // //--------------// /** * Look up in a collection of glyphs for <b>all</b> glyphs * contained in a provided polygon. * * @param collection the collection of glyphs to be browsed * @param polygon the containing polygon * @return the glyphs found, which may be an empty list */ public static Set<Glyph> lookupGlyphs ( Collection<? extends Glyph> collection, Polygon polygon) { Set<Glyph> set = new LinkedHashSet<>(); for (Glyph glyph : collection) { if (polygon.contains(glyph.getBounds())) { set.add(glyph); } } return set; } //--------------// // lookupGlyphs // //--------------// /** * Look up in a collection of glyphs for <b>all</b> glyphs * compatible with a provided predicate. * * @param collection the collection of glyphs to be browsed * @param predicate the predicate to apply to each candidate (a null * predicate will accept all candidates) * @return the glyphs found, which may be an empty list */ public static Set<Glyph> lookupGlyphs ( Collection<? extends Glyph> collection, Predicate<Glyph> predicate) { Set<Glyph> set = new LinkedHashSet<>(); for (Glyph glyph : collection) { if ((predicate == null) || predicate.check(glyph)) { set.add(glyph); } } return set; } //-------------------------// // lookupIntersectedGlyphs // //-------------------------// /** * Look up in a collection of glyphs for <b>all</b> glyphs * intersected by a provided rectangle. * * @param collection the collection of glyphs to be browsed * @param rect the coordinates rectangle * @return the glyphs found, which may be an empty list */ public static Set<Glyph> lookupIntersectedGlyphs ( Collection<? extends Glyph> collection, Rectangle rect) { Set<Glyph> set = new LinkedHashSet<>(); for (Glyph glyph : collection) { if (rect.intersects(glyph.getBounds())) { set.add(glyph); } } return set; } //-------// // purge // //-------// /** * Purge a collection of glyphs of those which match the given * predicate. * * @param glyphs the glyph collection to purge * @param predicate the predicate to detect glyphs to purge */ public static void purge (Collection<Glyph> glyphs, Predicate<Glyph> predicate) { if (predicate == null) { return; } for (Iterator<Glyph> it = glyphs.iterator(); it.hasNext();) { Glyph glyph = it.next(); if (predicate.check(glyph)) { it.remove(); } } } //--------------// // purgeManuals // //--------------// /** * Purge a collection of glyphs of those which exhibit a manually * assigned shape. * * @param glyphs the glyph collection to purge */ public static void purgeManuals (Collection<Glyph> glyphs) { for (Iterator<Glyph> it = glyphs.iterator(); it.hasNext();) { Glyph glyph = it.next(); if (glyph.isManualShape()) { it.remove(); } } } //------------// // sectionsOf // //------------// /** * Report the set of sections contained by the provided collection * of glyphs. * * @param glyphs the provided glyphs * @return the set of all member sections */ public static Set<Section> sectionsOf (Collection<Glyph> glyphs) { Set<Section> sections = new TreeSet<>(); for (Glyph glyph : glyphs) { sections.addAll(glyph.getMembers()); } return sections; } //----------// // shapesOf // //----------// /** * Report the set of shapes that appear in at least one of the * provided glyphs. * * @param glyphs the provided collection of glyphs * @return the shapes assigned among these glyphs */ public static Set<Shape> shapesOf (Collection<Glyph> glyphs) { EnumSet<Shape> shapes = EnumSet.noneOf(Shape.class); if (glyphs != null) { for (Glyph glyph : glyphs) { if (glyph.getShape() != null) { shapes.add(glyph.getShape()); } } } return shapes; } //-----------// // sortedSet // //-----------// /** * Build a mutable set with the provided glyphs. * * @param glyphs the provided glyphs * @return a mutable sorted set composed of these glyphs */ public static SortedSet<Glyph> sortedSet (Glyph... glyphs) { SortedSet<Glyph> set = new TreeSet<>(Glyph.byAbscissa); if (glyphs.length > 0) { set.addAll(Arrays.asList(glyphs)); } return set; } //-----------// // sortedSet // //-----------// /** * Build a mutable set with the provided glyphs. * * @param glyphs the provided glyphs * @return a mutable sorted set composed of these glyphs */ public static SortedSet<Glyph> sortedSet (Collection<Glyph> glyphs) { SortedSet<Glyph> set = new TreeSet<>(Glyph.byAbscissa); set.addAll(glyphs); return set; } //----------// // toString // //----------// /** * Build a string with just the ids of the glyph collection, * introduced by the provided label. * * @param label the string that introduces the list of IDs * @param glyphs the collection of glyphs * @return the string built */ public static String toString (String label, Collection<? extends Glyph> glyphs) { if (glyphs == null) { return ""; } StringBuilder sb = new StringBuilder(); sb.append(label) .append("["); for (Glyph glyph : glyphs) { sb.append("#") .append(glyph.getId()); } sb.append("]"); return sb.toString(); } //----------// // toString // //----------// /** * Build a string with just the ids of the glyph array, introduced * by the provided label. * * @param label the string that introduces the list of IDs * @param glyphs the array of glyphs * @return the string built */ public static String toString (String label, Glyph... glyphs) { return toString(label, Arrays.asList(glyphs)); } //----------// // toString // //----------// /** * Build a string with just the ids of the glyph collection, * introduced by the label "glyphs". * * @param glyphs the collection of glyphs * @return the string built */ public static String toString (Collection<? extends Glyph> glyphs) { return toString("glyphs", glyphs); } //----------// // toString // //----------// /** * Build a string with just the ids of the glyph array, introduced * by the label "glyphs". * * @param glyphs the array of glyphs * @return the string built */ public static String toString (Glyph... glyphs) { return toString("glyphs", glyphs); } private Glyphs () { } }