//----------------------------------------------------------------------------// // // // S y s t e m I n f o // // // //----------------------------------------------------------------------------// // <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.sheet; import omr.check.CheckSuite; import omr.glyph.CompoundBuilder; import omr.glyph.CompoundBuilder.CompoundAdapter; import omr.glyph.GlyphInspector; import omr.glyph.Glyphs; import omr.glyph.GlyphsBuilder; import omr.glyph.facets.Glyph; import omr.glyph.pattern.PatternsChecker; import omr.glyph.pattern.SlurInspector; import omr.grid.BarAlignment; import omr.grid.BarInfo; import omr.grid.LineInfo; import omr.grid.StaffInfo; import omr.grid.StaffManager; import omr.lag.Section; import omr.math.GeoPath; import omr.score.SystemTranslator; import omr.score.entity.ScoreSystem; import omr.score.entity.Staff; import omr.score.entity.SystemPart; import omr.step.StepException; import omr.text.TextBuilder; import omr.text.TextLine; import omr.util.HorizontalSide; import static omr.util.HorizontalSide.*; import omr.util.Navigable; import omr.util.Predicate; import omr.util.VerticalSide; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.Dimension; import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.ConcurrentSkipListSet; /** * Class {@code SystemInfo} gathers information from the original * picture about a retrieved system. * Most of the physical processing is done in parallel at system level, and * thus is handled from this SystemInfo object. * * <p>Many processing tasks are actually handled by companion classes, but * SystemInfo is the interface of choice, with delegation to the proper * companion. * * @author Hervé Bitteur */ public class SystemInfo implements Comparable<SystemInfo> { //~ Static fields/initializers --------------------------------------------- /** Usual logger utility */ private static final Logger logger = LoggerFactory.getLogger(SystemInfo.class); //~ Instance fields -------------------------------------------------------- /** Related sheet */ @Navigable(false) private final Sheet sheet; /** Dedicated measure builder */ private final MeasuresBuilder measuresBuilder; /** Dedicated text builder */ private final TextBuilder textBuilder; /** Dedicated glyph builder */ private final GlyphsBuilder glyphsBuilder; /** Dedicated compound builder */ private final CompoundBuilder compoundBuilder; /** Dedicated verticals builder */ private final VerticalsBuilder verticalsBuilder; /** Dedicated horizontals builder */ private final HorizontalsBuilder horizontalsBuilder; /** Dedicated glyph inspector */ private final GlyphInspector glyphInspector; /** Dedicated slur inspector */ private final SlurInspector slurInspector; /** Dedicated system translator */ private final SystemTranslator translator; /** Staves of this system */ private List<StaffInfo> staves = new ArrayList<>(); /** Parts in this system */ private final List<PartInfo> parts = new ArrayList<>(); /** Related System in Score hierarchy */ private ScoreSystem scoreSystem; /** Left system bar, if any */ private BarInfo leftBar; /** Right system bar, if any */ private BarInfo rightBar; /** Left system limit (a filament or a straight line) */ private Object leftLimit; /** Right system limit (a filament or a straight line) */ private Object rightLimit; /** Bar alignments for this system */ private List<BarAlignment> barAlignments; /// HORIZONTALS //////////////////////////////////////////////////////// /** Horizontal sections, assigned once for all to this system */ private final List<Section> hSections = new ArrayList<>(); /** Unmodifiable view of the horizontal section collection */ private final Collection<Section> hSectionsView = Collections. unmodifiableCollection( hSections); /** Retrieved tenuto signs in this system */ private final List<Glyph> tenutos = new ArrayList<>(); /** Retrieved endings in this system */ private final List<Glyph> endings = new ArrayList<>(); /// VERTICALS ////////////////////////////////////////////////////////// /** Vertical sections, assigned once for all to this system */ private final List<Section> vSections = new ArrayList<>(); /** Unmodifiable view of the vertical section collection */ private final Collection<Section> vSectionsView = Collections. unmodifiableCollection( vSections); /** Collection of (active?) glyphs in this system */ private final SortedSet<Glyph> glyphs = new ConcurrentSkipListSet<>( Glyph.byAbscissa); /** Unmodifiable view of the glyphs collection */ private final SortedSet<Glyph> glyphsView = Collections. unmodifiableSortedSet( glyphs); /** Set of sentence made of text glyphs */ private Set<TextLine> sentences = new LinkedHashSet<>(); /** Used to assign a unique ID to system sentences */ private int sentenceCount = 0; //////////////////////////////////////////////////////////////////////////// /** Unique Id for this system (in the sheet) */ private final int id; /** Boundary that encloses all items of this system. */ private SystemBoundary boundary; /** Ordinate of bottom of last staff of the system. */ private int bottom; /** Delta ordinate between first line of first staff & first line of * last staff. */ private int deltaY; /** Abscissa of beginning of system. */ private int left; /** Ordinate of top of first staff of the system. */ private int top; /** Width of the system. */ private int width = -1; //~ Constructors ----------------------------------------------------------- //------------// // SystemInfo // //------------// /** * Create a SystemInfo entity, to register the provided parameters. * * @param id the unique identity * @param sheet the containing sheet * @param staves the (initial) sequence of staves */ public SystemInfo (int id, Sheet sheet, List<StaffInfo> staves) { this.id = id; this.sheet = sheet; this.staves = staves; updateCoordinates(); measuresBuilder = new MeasuresBuilder(this); textBuilder = new TextBuilder(this); glyphsBuilder = new GlyphsBuilder(this); compoundBuilder = new CompoundBuilder(this); verticalsBuilder = new VerticalsBuilder(this); horizontalsBuilder = new HorizontalsBuilder(this); glyphInspector = new GlyphInspector(this); slurInspector = new SlurInspector(this); translator = new SystemTranslator(this); } //~ Methods ---------------------------------------------------------------- //----------// // addGlyph // //----------// /** * Add a brand new glyph as an active glyph in proper system * (and nest). * If the glyph is a compound, its parts are made pointing back to it and * are made no longer active glyphs. To just register a glyph (without * impacting its sections), use {@link #registerGlyph} instead. * * <p><b>Note</b>: The caller must use the returned glyph since it may be * different from the provided glyph (this happens when an original glyph * with same signature existed before this one) * * @param glyph the brand new glyph * @return the original glyph as inserted in the glyph nest. Use this entity * instead of the provided one. * @see #registerGlyph */ public Glyph addGlyph (Glyph glyph) { return glyphsBuilder.addGlyph(glyph); } //---------// // addPart // //---------// /** * Add a part (set of staves) in this system. * * @param partInfo the part to add */ public void addPart (PartInfo partInfo) { parts.add(partInfo); } //-----------------------// // addToGlyphsCollection // //-----------------------// /** * This is a private entry meant for GlyphsBuilder only. * The standard entry is {@link #addGlyph} * * @param glyph the glyph to add to the system glyph collection */ public void addToGlyphsCollection (Glyph glyph) { glyphs.add(glyph); } //------------------------// // allocateScoreStructure // //------------------------// /** * Build the corresponding ScoreSystem entity with all its * depending Parts and Staves. */ public void allocateScoreStructure () { // Allocate the score system scoreSystem = new ScoreSystem( this, sheet.getPage(), new Point(getLeft(), getTop()), new Dimension(getWidth(), getDeltaY())); // Allocate the parts in the system int partId = 0; for (PartInfo partInfo : getParts()) { SystemPart part = new SystemPart(scoreSystem, partInfo); part.setId(--partId); // Temporary id // Allocate the staves in this part for (StaffInfo staffInfo : partInfo.getStaves()) { LineInfo firstLine = staffInfo.getFirstLine(); LineInfo lastLine = staffInfo.getLastLine(); new Staff( staffInfo, part, new Point(left, firstLine.yAt(left)), (int) Math.rint(staffInfo.getAbscissa(RIGHT) - left), lastLine.yAt(left) - firstLine.yAt(left)); } } } //---------------// // buildCompound // //---------------// public Glyph buildCompound (Glyph seed, boolean includeSeed, Collection<Glyph> suitables, CompoundAdapter adapter) { return compoundBuilder.buildCompound( seed, includeSeed, suitables, adapter); } //---------------// // buildCompound // //---------------// public Glyph buildCompound (Collection<Glyph> parts) { return compoundBuilder.buildCompound(parts); } //------------// // buildGlyph // //------------// /** * Build a glyph from a collection of sections, and make the * sections point back to the glyph. * * @param sections the provided members of the future glyph * @return the newly built glyph */ public Glyph buildGlyph (Collection<Section> sections) { return glyphsBuilder.buildGlyph(sections); } //---------------// // buildMeasures // //---------------// /** * Based on barlines found, build, check and cleanup score measures. */ public void buildMeasures () { measuresBuilder.buildMeasures(); } //------------------------// // buildTransientCompound // //------------------------// /** * Make a new glyph out of a collection of (sub) glyphs, * by merging all their member sections. * This compound is transient, since until it is properly inserted by use * of {@link #addGlyph}, this building has no impact on either the * containing nest, the containing system, nor the contained sections * themselves. * * <p>If the newly built compound duplicates an original glyph, the original * glyph is used in place of the compound. Finally, the glyph features are * computed before the compound is returned.</p> * * @param parts the collection of (sub) glyphs * @return the brand new (compound) glyph */ public Glyph buildTransientCompound (Collection<Glyph> parts) { return glyphsBuilder.buildTransientCompound(parts); } //---------------------// // buildTransientGlyph // //---------------------// /** * Make a new glyph out of a collection of sections. * This glyph is transient, since until it is properly inserted by use of * {@link #addGlyph}, this building has no impact on either the containing * nest, the containing system, nor the contained sections themselves. * * <p>If the newly built compound duplicates an original glyph, the original * glyph is used in place of the compound. Finally, the glyph features are * computed before the compound is returned.</p> * * @param sections the collection of sections * @return the brand new transient glyph */ public Glyph buildTransientGlyph (Collection<Section> sections) { return glyphsBuilder.buildTransientGlyph(sections); } //-----------------// // checkBoundaries // //-----------------// /** * Check this system for glyphs that cross the system boundaries. */ public void checkBoundaries () { glyphsBuilder.retrieveGlyphs(false); } //-------------// // clearGlyphs // //-------------// /** * Empty the system glyph collection. */ public void clearGlyphs () { glyphs.clear(); } //-----------// // compareTo // //-----------// /** * Needed to implement natural SystemInfo sorting, based on system id. * * @param o the other system to compare to * @return the comparison result */ @Override public int compareTo (SystemInfo o) { return Integer.signum(id - o.id); } //----------------------// // computeGlyphFeatures // //----------------------// /** * Compute all the features that will be used to recognize the * glyph at hand (a mix of moments plus a few other characteristics). * * @param glyph the glyph at hand */ public void computeGlyphFeatures (Glyph glyph) { glyphsBuilder.computeGlyphFeatures(glyph); } //----------------------// // createStemCheckSuite // //----------------------// /** * Build a check suite for stem retrievals. * * @param isShort are we looking for short (vs standard) stems? * @return the newly built check suite */ public CheckSuite<Glyph> createStemCheckSuite (boolean isShort) throws StepException { return verticalsBuilder.createStemCheckSuite(isShort); } //------------// // dumpGlyphs // //------------// /** * Dump all glyphs handled by this system. */ public void dumpGlyphs () { dumpGlyphs(null); } //------------// // dumpGlyphs // //------------// /** * Dump the glyphs handled by this system and that are contained * by the provided rectangle. * * @param rect the region of interest */ public void dumpGlyphs (Rectangle rect) { for (Glyph glyph : getGlyphs()) { if ((rect == null) || (rect.contains(glyph.getBounds()))) { System.out.println( (glyph.isActive() ? "active " : " ") + (glyph.isKnown() ? "known " : " ") + (glyph.isWellKnown() ? "wellKnown " : " ") + glyph.toString()); } } } //--------------// // dumpSections // //--------------// /** * Dump all (vertical) sections handled by this system. */ public void dumpSections () { dumpSections(null); } //--------------// // dumpSections // //--------------// /** * Dump the (vertical) sections handled by this system and that are * contained by the provided rectangle. * * @param rect the region of interest */ public void dumpSections (Rectangle rect) { for (Section section : getVerticalSections()) { if ((rect == null) || (rect.contains(section.getBounds()))) { System.out.println( (section.isKnown() ? "known " : " ") + section.toString()); } } } //------------------// // extractNewGlyphs // //------------------// /** * In the specified system, build new glyphs from unknown sections * (sections not linked to a known glyph). */ public void extractNewGlyphs () { removeInactiveGlyphs(); retrieveGlyphs(); } //--------// // getBar // //--------// /** * Report the system barline on the provided side. * * @param side proper horizontal side * @return the system bar on this side, or null */ public BarInfo getBar (HorizontalSide side) { if (side == HorizontalSide.LEFT) { return leftBar; } else { return rightBar; } } //------------------// // getBarAlignments // //------------------// /** * Report the system bar alignments. * * @return the barAlignments */ public List<BarAlignment> getBarAlignments () { return barAlignments; } //-----------// // getBottom // //-----------// /** * Report the ordinate of the bottom of the system, which is the * ordinate of the last line of the last staff of this system. * * @return the system bottom, in pixels */ public int getBottom () { return bottom; } //-------------// // getBoundary // //-------------// /** * Report the precise boundary of this system. * * @return the precise system boundary */ public SystemBoundary getBoundary () { return boundary; } //-----------// // getBounds // //-----------// /** * Report the rectangular bounds that enclose this system. * * @return the system rectangular bounds */ public Rectangle getBounds () { if (boundary != null) { return new Rectangle(boundary.getBounds()); } else { return null; } } //--------------------// // getCompoundBuilder // //--------------------// /** * @return the compoundBuilder */ public CompoundBuilder getCompoundBuilder () { return compoundBuilder; } //-----------// // getDeltaY // //-----------// /** * Report the deltaY of the system, that is the difference in * ordinate between first and last staves of the system. * This deltaY is of course 0 for a one-staff system. * * @return the deltaY value, expressed in pixels */ public int getDeltaY () { return deltaY; } //------------// // getEndings // //------------// /** * Report the collection of endings found. * * @return the endings collection */ public List<Glyph> getEndings () { return endings; } //---------------// // getFirstStaff // //---------------// /** * Report the first staff of the system. * * @return the first staff */ public StaffInfo getFirstStaff () { return staves.get(0); } //-----------// // getGlyphs // //-----------// /** * Report the unmodifiable collection of glyphs within the system * area. * * @return the unmodifiable collection of glyphs */ public SortedSet<Glyph> getGlyphs () { return glyphsView; } //-----------------------// // getHorizontalSections // //-----------------------// /** * Report the (unmodifiable) collection of horizontal sections in * the system related area. * * @return the area horizontal sections */ public Collection<Section> getHorizontalSections () { return hSectionsView; } //-----------------------// // getHorizontalsBuilder // //-----------------------// public HorizontalsBuilder getHorizontalsBuilder () { return horizontalsBuilder; } //-------// // getId // //-------// /** * Report the id (debugging info) of the system info. * * @return the id */ public int getId () { return id; } //--------------// // getLastStaff // //--------------// /** * @return the lastStaff */ public StaffInfo getLastStaff () { return staves.get(staves.size() - 1); } //---------// // getLeft // //---------// /** * Report the left abscissa. * * @return the left abscissa value, expressed in pixels */ public int getLeft () { return left; } //----------// // getLimit // //----------// /** * Report the system limit on the provided side. * * @param side proper horizontal side * @return the leftBar */ public Object getLimit (HorizontalSide side) { if (side == HorizontalSide.LEFT) { return leftLimit; } else { return rightLimit; } } //--------------// // getLogPrefix // //--------------// /** * Report the proper prefix to use when logging a message. * * @return the proper prefix */ public String getLogPrefix () { StringBuilder sb = new StringBuilder(sheet.getLogPrefix()); if (sb.length() > 1) { sb.insert(sb.length() - 2, "-S" + id); } else { sb.append("S").append(id).append(" "); } return sb.toString(); } //------------------------------// // getMutableHorizontalSections // //------------------------------// /** * Report the (modifiable) collection of horizontal sections in the * system related area. * * @return the area vertical sections */ public Collection<Section> getMutableHorizontalSections () { return hSections; } //----------------------------// // getMutableVerticalSections // //----------------------------// /** * Report the (modifiable) collection of vertical sections in the * system related area. * * @return the area vertical sections */ public Collection<Section> getMutableVerticalSections () { return vSections; } //------------------// // getNewSentenceId // //------------------// /** * Report the id for a new sentence. * * @return the next id */ public int getNewSentenceId () { return ++sentenceCount; } //----------------// // getNoteStaffAt // //----------------// /** * Given a note, retrieve the proper related staff within the * system, using ledgers if any. * * @param point the center of the provided note entity * @return the proper note position (staff & pitch) */ public NotePosition getNoteStaffAt (Point point) { StaffInfo staff = getStaffAt(point); NotePosition pos = staff.getNotePosition(point); logger.debug("{} -> {}", point, pos); double pitch = pos.getPitchPosition(); if ((Math.abs(pitch) > 5) && (pos.getLedger() == null)) { // Delta pitch from reference line double dp = Math.abs(pitch) - 4; // Check with the other staff, if any int index = staves.indexOf(staff); StaffInfo otherStaff = null; if ((pitch < 0) && (index > 0)) { otherStaff = staves.get(index - 1); } else if ((pitch > 0) && (index < (staves.size() - 1))) { otherStaff = staves.get(index + 1); } if (otherStaff != null) { NotePosition otherPos = otherStaff.getNotePosition(point); if (otherPos.getLedger() != null) { // Delta pitch from closest reference ledger double otherDp = Math.abs( otherPos.getPitchPosition() - StaffInfo.getLedgerPitchPosition( otherPos.getLedger().index)); if (otherDp < dp) { logger.debug(" otherPos: {}", pos); pos = otherPos; } } } } return pos; } //----------// // getParts // //----------// /** * Reports the parts of this system. * * @return the parts (non-null) */ public List<PartInfo> getParts () { return parts; } //----------// // getRight // //----------// /** * Report the abscissa of the end of the system. * * @return the right abscissa, expressed in pixels */ public int getRight () { return left + width; } //----------------// // getScoreSystem // //----------------// /** * Report the related logical score system. * * @return the logical score System counterpart */ public ScoreSystem getScoreSystem () { return scoreSystem; } //--------------// // getSentences // //--------------// /** * Report the various sentences retrieved in this system. * * @return the (perhaps empty) collection of sentences found */ public Set<TextLine> getSentences () { return sentences; } //----------// // getSheet // //----------// /** * Report the sheet this system belongs to. * * @return the containing sheet */ public Sheet getSheet () { return sheet; } //---------// // getSkew // //---------// /** * Report the system specific skew (or the global sheet skew). * * @return the related skew */ public Skew getSkew () { return sheet.getSkew(); } //------------------// // getSlurInspector // //------------------// public SlurInspector getSlurInspector () { return slurInspector; } //------------// // getStaffAt // //------------// /** * Retrieve the staff, <b>within</b> the system, whose area * contains the provided point. * * @param point the provided point * @return the "containing" staff */ public StaffInfo getStaffAt (Point2D point) { return StaffManager.getStaffAt(point, staves); } //-----------// // getStaves // //-----------// /** * Report the list of staves that compose this system. * * @return the staves */ public List<StaffInfo> getStaves () { return staves; } //------------// // getTenutos // //------------// /** * Report the collection of tenutos found. * * @return the tenutos collection */ public List<Glyph> getTenutos () { return tenutos; } //--------// // getTop // //--------// /** * Report the ordinate of the top of this system. * * @return the top ordinate, expressed in pixels */ public int getTop () { return top; } //---------------------// // getVerticalSections // //---------------------// /** * Report the (unmodifiable) collection of vertical sections in * the system related area. * * @return the area vertical sections */ public Collection<Section> getVerticalSections () { return vSectionsView; } //----------// // getWidth // //----------// /** * Report the width of the system. * * @return the width value, expressed in pixels */ public int getWidth () { return width; } //----------// // idString // //----------// /** * Convenient way to report a small system reference. * * @return system reference */ public String idString () { return "system#" + id; } //---------------// // inspectGlyphs // //---------------// /** * Process the given system, by retrieving unassigned glyphs, * evaluating and assigning them if OK, or trying compounds * otherwise. * * @param minGrade the minimum acceptable grade for this processing * @param wide flag for extra wide compound box */ public void inspectGlyphs (double minGrade, boolean wide) { glyphInspector.inspectGlyphs(minGrade, wide); } //-----------------------// // lookupContainedGlyphs // //-----------------------// /** * Look up in system glyphs for the glyphs contained by a * provided rectangle. * * @param rect the coordinates rectangle, in pixels * @return the glyphs found, which may be an empty list */ public List<Glyph> lookupContainedGlyphs (Rectangle rect) { List<Glyph> found = new ArrayList<>(); for (Glyph glyph : getGlyphs()) { if (rect.contains(glyph.getBounds())) { found.add(glyph); } } return found; } //-------------------------// // lookupIntersectedGlyphs // //-------------------------// /** * Look up in system glyphs for <b>all</b> glyphs, apart from the * excluded glyphs, intersected by a provided rectangle. * * @param rect the coordinates rectangle, in pixels * @param excluded the glyphs to be excluded * @return the glyphs found, which may be an empty list */ public List<Glyph> lookupIntersectedGlyphs (Rectangle rect, Glyph... excluded) { List<Glyph> exc = Arrays.asList(excluded); List<Glyph> found = new ArrayList<>(); for (Glyph glyph : getGlyphs()) { if (!exc.contains(glyph) && glyph.intersects(rect)) { found.add(glyph); } } return found; } //---------------// // registerGlyph // //---------------// /** * Just register this glyph (as inactive) in order to persist glyph * info such as TextInfo. * Use {@link #addGlyph} to fully add the glpyh as active. * * @param glyph the glyph to just register * @return the proper (original) glyph * @see #addGlyph */ public Glyph registerGlyph (Glyph glyph) { return glyphsBuilder.registerGlyph(glyph); } //----------------------------// // removeFromGlyphsCollection // //----------------------------// /** * Meant for access by GlyphsBuilder only, * since standard entry is {@link #removeGlyph}. * * @param glyph the glyph to remove * @return true if the glyph was registered */ public boolean removeFromGlyphsCollection (Glyph glyph) { return glyphs.remove(glyph); } //-------------// // removeGlyph // //-------------// /** * Remove a glyph from the containing system glyph list, and make * it inactive by cutting the link from its member sections. * * @param glyph the glyph to remove */ public void removeGlyph (Glyph glyph) { glyphsBuilder.removeGlyph(glyph); } //----------------------// // removeInactiveGlyphs // //----------------------// /** * On a specified system, look for all inactive glyphs and remove * them from its glyphs collection (but leave them in the * containing nest). * Purpose is to prepare room for a new glyph extraction */ public void removeInactiveGlyphs () { // To avoid concurrent modifs exception Collection<Glyph> toRemove = new ArrayList<>(); for (Glyph glyph : getGlyphs()) { if (!glyph.isActive()) { toRemove.add(glyph); } } if (logger.isDebugEnabled()) { logger.debug("removeInactiveGlyphs: {} {}", toRemove.size(), Glyphs.toString(toRemove)); } for (Glyph glyph : toRemove) { // Remove glyph from system & cut sections links to it removeGlyph(glyph); } } //----------------// // resetSentences // //----------------// public void resetSentences () { sentences.clear(); sentenceCount = 0; } //----------------// // retrieveGlyphs // //----------------// /** * In a given system area, browse through all sections not assigned * to known glyphs, and build new glyphs out of connected sections. */ public void retrieveGlyphs () { glyphsBuilder.retrieveGlyphs(true); } //---------------------// // retrieveHorizontals // //---------------------// /** * Retrieve ledgers (and tenuto, and horizontal endings). */ public void retrieveHorizontals () throws StepException { try { horizontalsBuilder.buildInfo(); } catch (Exception ex) { logger.warn("Error in retrieveHorizontals", ex); } } //-------------------// // retrieveVerticals // //-------------------// /** * Retrieve stems (and vertical endings). * * @return the number of glyphs built */ public int retrieveVerticals () throws StepException { return verticalsBuilder.retrieveVerticals(); } //-------------// // runPatterns // //-------------// /** * Run the series of glyphs patterns. * * @return true if some progress has been made */ public boolean runPatterns () { return new PatternsChecker(this).runPatterns(); } //---------------------// // segmentGlyphOnStems // //---------------------// /** * Process a glyph to retrieve its internal potential stems and * leaves. * * @param glyph the glyph to segment along stems * @param isShort should we look for short (rather than standard) stems? */ public void segmentGlyphOnStems (Glyph glyph, boolean isShort) { verticalsBuilder.segmentGlyphOnStems(glyph, isShort); } //--------------// // selectGlyphs // //--------------// /** * Select glyphs out of a provided collection of glyphs,for which * the provided predicate holds true. * * @param glyphs the provided collection of glyphs candidates, or the * full * system collection if null * @param predicate the condition to be fulfilled to get selected * @return the sorted set of selected glyphs */ public SortedSet<Glyph> selectGlyphs (Collection<Glyph> glyphs, Predicate<Glyph> predicate) { SortedSet<Glyph> selected = new TreeSet<>(); if (glyphs == null) { glyphs = getGlyphs(); } for (Glyph glyph : glyphs) { if (predicate.check((glyph))) { selected.add(glyph); } } return selected; } //--------// // setBar // //--------// /** * Assign system barline on the provided side. * * @param side proper horizontal side * @param bar the bar to set */ public void setBar (HorizontalSide side, BarInfo bar) { if (side == HorizontalSide.LEFT) { this.leftBar = bar; } else { this.rightBar = bar; } } //------------------// // setBarAlignments // //------------------// /** * Record the various bar alignments for this system. * * @param barAlignments the barAlignments to set */ public void setBarAlignments (List<BarAlignment> barAlignments) { this.barAlignments = barAlignments; } //-------------// // setBoundary // //-------------// /** * Define the precise boundary of this system. * * @param boundary the (new) boundary */ public void setBoundary (SystemBoundary boundary) { logger.debug("{} setBoundary {}", idString(), boundary); this.boundary = boundary; updateBoundary(); } //----------------// // updateBoundary // //----------------// /** * We have a new (or modified) system boundary. * So let's update the system boundary polygon as well as the limits of * the first and last staves. */ public void updateBoundary () { // Reset the system polygon boundary.update(); // Update top limit of first staff GeoPath topPath = boundary.getLimit(VerticalSide.TOP).toGeoPath(); getFirstStaff().setLimit(VerticalSide.TOP, topPath); // Update bottom limit of last staff GeoPath bottomPath = boundary.getLimit(VerticalSide.BOTTOM).toGeoPath(); getLastStaff().setLimit(VerticalSide.BOTTOM, bottomPath); } //----------// // setLimit // //----------// /** * Record the system limit on the provided side. * * @param side proper horizontal side * @param limit the limit to set */ public void setLimit (HorizontalSide side, Object limit) { if (side == HorizontalSide.LEFT) { this.leftLimit = limit; } else { this.rightLimit = limit; } } //-----------// // setStaves // //-----------// /** * @param staves the range of staves */ public void setStaves (List<StaffInfo> staves) { this.staves = staves; updateCoordinates(); } //-----------// // stemBoxOf // //-----------// /** * Report a enlarged box of a given (stem) glyph. * * @param stem the stem * @return the enlarged stem box */ public Rectangle stemBoxOf (Glyph stem) { return glyphsBuilder.stemBoxOf(stem); } //----------// // toString // //----------// /** * Convenient method, to build a string with just the ids of the * system collection. * * @param systems the collection of glysystemsphs * @return the string built */ public static String toString (Collection<SystemInfo> systems) { if (systems == null) { return ""; } StringBuilder sb = new StringBuilder(); sb.append(" systems["); for (SystemInfo system : systems) { sb.append("#").append(system.getId()); } sb.append("]"); return sb.toString(); } //----------// // toString // //----------// /** * Report a readable description. * * @return a description based on staff indices */ @Override public String toString () { StringBuilder sb = new StringBuilder(); sb.append("{SystemInfo#").append(id); sb.append(" T").append(getFirstStaff().getId()); if (staves.size() > 1) { sb.append("..T").append(getLastStaff().getId()); } if (leftBar != null) { sb.append(" leftBar:").append(leftBar); } if (rightBar != null) { sb.append(" rightBar:").append(rightBar); } if (leftLimit != null) { sb.append(" leftLimit:").append(leftLimit); } if (rightLimit != null) { sb.append(" rightLimit:").append(rightLimit); } sb.append("}"); return sb.toString(); } //----------------// // translateFinal // //----------------// /** * Launch from this system the final processing of impacted systems * to translate them to score entities. */ public void translateFinal () { translator.translateFinal(); } //-----------------// // translateSystem // //-----------------// /** * Translate the physical Sheet system data into Score system * entities. */ public void translateSystem () { translator.translateSystem(); } //----------// // trimSlur // //----------// /** * Rebuild true Slur from some underlying sections. * * @param slur the spurious slur * @return the extracted slur glyph, if any */ public Glyph trimSlur (Glyph slur) { return slurInspector.trimSlur(slur); } //-------------------// // updateCoordinates // //-------------------// public final void updateCoordinates () { StaffInfo firstStaff = getFirstStaff(); LineInfo firstLine = firstStaff.getFirstLine(); Point2D topLeft = firstLine.getEndPoint(LEFT); Point2D topRight = firstLine.getEndPoint(RIGHT); StaffInfo lastStaff = getLastStaff(); LineInfo lastLine = lastStaff.getLastLine(); Point2D botLeft = lastLine.getEndPoint(LEFT); left = (int) Math.rint(topLeft.getX()); top = (int) Math.rint(topLeft.getY()); width = (int) Math.rint(topRight.getX() - topLeft.getX()); deltaY = (int) Math.rint( lastStaff.getFirstLine().getEndPoint(LEFT).getY() - topLeft. getY()); bottom = (int) Math.rint(botLeft.getY()); } //----------------// // getTextBuilder // //----------------// public TextBuilder getTextBuilder () { return textBuilder; } }