//----------------------------------------------------------------------------// // // // S c o r e S y s t e m // // // //----------------------------------------------------------------------------// // <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.score.visitor.ScoreVisitor; import omr.sheet.SystemInfo; import omr.text.TextRole; import omr.util.TreeNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.Dimension; import java.awt.Point; import java.awt.Rectangle; import java.util.Collections; import java.util.List; /** * Class {@code ScoreSystem} encapsulates a system in a score. * * <p>A system contains only one kind of direct children : SystemPart instances * * @author Hervé Bitteur */ public class ScoreSystem extends SystemNode { //~ Static fields/initializers --------------------------------------------- /** Usual logger utility */ private static final Logger logger = LoggerFactory.getLogger( ScoreSystem.class); //~ Instance fields -------------------------------------------------------- /** Id for debug */ private final int id; /** * Top left corner of the system in the containing page. * This points to the first real staff, and does not count the preceding * dummy staves if any. */ private final Point topLeft; /** Related info from sheet analysis */ private final SystemInfo info; //~ Constructors ----------------------------------------------------------- //-------------// // ScoreSystem // //-------------// /** * Create a system with all needed parameters. * * @param info the physical information retrieved from the sheet * @param page the containing page * @param topLeft the coordinates of the upper left point of the system * in its containing page * @param dimension the dimension of the system */ public ScoreSystem (SystemInfo info, Page page, Point topLeft, Dimension dimension) { super(page); this.info = info; this.topLeft = topLeft; id = 1 + getChildIndex(); setBox( new Rectangle( topLeft.x, topLeft.y, dimension.width, dimension.height)); getCenter(); } //~ Methods ---------------------------------------------------------------- //--------// // accept // //--------// @Override public boolean accept (ScoreVisitor visitor) { return visitor.visit(this); } //-------------------------// // connectPageInitialSlurs // //-------------------------// /** * For this system, retrieve the connections between the (orphan) * slurs at the beginning of this page and the (orphan) slurs at the * end of the previous page. */ public void connectPageInitialSlurs () { // Safer: check we are the very first system in page if (getChildIndex() != 0) { throw new IllegalArgumentException( "connectSlursAcrossPages called for non-first system"); } // If very first page, we are done if (getPage() .getChildIndex() == 0) { return; } ScoreSystem precedingSystem = getPage() .getPrecedingInScore() .getLastSystem(); if (precedingSystem != null) { // Examine every part in sequence for (TreeNode pNode : getParts()) { SystemPart part = (SystemPart) pNode; // Find out the proper preceding part (across pages) SystemPart precedingPart = precedingSystem.getPart( part.getId()); // Ending orphans in preceding system/part (if such part exists) part.connectSlursWith(precedingPart); } } } //---------------------------// // connectSystemInitialSlurs // //---------------------------// /** * Retrieve the connections between the (orphan) slurs at the * beginning of this system and the (orphan) slurs at the end of the * previous system. */ public void connectSystemInitialSlurs () { if (getPreviousSibling() != null) { // Examine every part in sequence for (TreeNode pNode : getParts()) { SystemPart part = (SystemPart) pNode; // Ending orphans in preceding system/part (if such part exists) part.connectSlursWith(part.getPrecedingInPage()); } } } //-------------------// // connectTiedVoices // //-------------------// /** * Make sure that notes tied across measures keep the same voice. * This is performed for all measures in this system. */ public void connectTiedVoices () { for (TreeNode node : getParts()) { SystemPart part = (SystemPart) node; part.connectTiedVoices(); } } //------------------// // fillMissingParts // //------------------// /** * Check for missing parts in this system, and if needed create * dummy parts filled with whole rests. */ public void fillMissingParts () { // Check we have all the defined parts in this system for (ScorePart scorePart : getPage() .getPartList()) { if (getPart(scorePart.getId()) == null) { getFirstRealPart() .createDummyPart(scorePart.getId()); Collections.sort(getParts(), SystemPart.idComparator); } } } //--------------// // getDimension // //--------------// /** * Report the system dimension. * <p>Width is the distance, in pixels, between left edge and right edge. * <p>Height is the distance, in pixels, from top of first staff, down to * top (and not bottom) of last staff. * Nota: It does not count the height of the last staff * * @return the system dimension */ @Override public Dimension getDimension () { return super.getDimension(); } //--------------// // getFirstPart // //--------------// /** * Report the very first part in this system, which may be a dummy * one. * Use {@link #getFirstRealPart} instead to point to the first real part. * * @return the first part entity * @see #getFirstRealPart() */ public SystemPart getFirstPart () { return (SystemPart) getParts() .get(0); } //------------------// // getFirstRealPart // //------------------// /** * Report the first non dummy part in this system. * * @return the real first part entity * @see #getFirstPart() */ public SystemPart getFirstRealPart () { for (TreeNode node : getParts()) { SystemPart part = (SystemPart) node; if (!part.isDummy()) { return part; } } return null; } //-------// // getId // //-------// public int getId () { return id; } //---------// // getInfo // //---------// /** * Report the physical information retrieved from the sheet for this * system. * * @return the information entity */ public SystemInfo getInfo () { return info; } //-------------// // getLastPart // //-------------// /** * Report the last part in this system. * * @return the last part entity */ public SystemPart getLastPart () { return (SystemPart) getParts() .get(getParts().size() - 1); } //---------// // getPart // //---------// /** * Report the part with the provided id, if any. * * @param id the id of the desired part * @return the part found or null */ public SystemPart getPart (int id) { for (TreeNode node : getParts()) { SystemPart part = (SystemPart) node; if (part.getId() == id) { return part; } } logger.debug("{} No part {} found", getContextString(), id); return null; } //--------------// // getPartAbove // //--------------// /** * Determine the part which is above the given point. * * @param point the given point * @return the part above */ public SystemPart getPartAbove (Point point) { Staff staff = getStaffAbove(point); if (staff == null) { return getFirstRealPart(); } else { return staff.getPart(); } } //-----------// // getPartAt // //-----------// /** * Determine the part which relates to the given point. * * @param point the given point * @return the containing part */ public SystemPart getPartAt (Point point) { Staff staff = getStaffAt(point); if (staff == null) { return getFirstPart(); } else { return staff.getPart(); } } //----------// // getParts // //----------// /** * Report the parts for this system. * * @return the ordered parts */ public List<TreeNode> getParts () { return getChildren(); } //---------------// // getStaffAbove // //---------------// /** * Determine the real staff which is just above the given point. * * @param sysPt the given point * @return the staff above */ public Staff getStaffAbove (Point sysPt) { Staff best = null; for (TreeNode node : getParts()) { SystemPart part = (SystemPart) node; if (!part.isDummy()) { for (TreeNode n : part.getStaves()) { Staff staff = (Staff) n; if (staff.getCenter().y < sysPt.y) { best = staff; } } } } return best; } //------------// // getStaffAt // //------------// /** * Determine the real staff which is closest to the given point. * * @param sysPt the given point * @return the closest staff */ public Staff getStaffAt (Point sysPt) { int bestDy = Integer.MAX_VALUE; Staff best = null; for (TreeNode node : getParts()) { SystemPart part = (SystemPart) node; if (!part.isDummy()) { for (TreeNode n : part.getStaves()) { Staff staff = (Staff) n; int dy = Math.abs(sysPt.y - staff.getCenter().y); if (dy < bestDy) { bestDy = dy; best = staff; } } } } return best; } //---------------// // getStaffBelow // //---------------// /** * Determine the staff which is just below the given system point. * * @param sysPt the given system point * @return the staff below */ public Staff getStaffBelow (Point sysPt) { for (TreeNode node : getParts()) { SystemPart part = (SystemPart) node; for (TreeNode n : part.getStaves()) { Staff staff = (Staff) n; if (staff.getCenter().y > sysPt.y) { return staff; } } } return null; } //------------------// // getStaffPosition // //------------------// /** * Report the vertical position of the provided point with respect * to the system staves. * * @param point the point whose ordinate is to be checked * @return the StaffPosition value */ public StaffPosition getStaffPosition (Point point) { Staff firstStaff = getFirstRealPart() .getFirstStaff(); if (point.y < firstStaff.getTopLeft().y) { return StaffPosition.ABOVE_STAVES; } Staff lastStaff = getLastPart() .getLastStaff(); if (point.y > (lastStaff.getTopLeft().y + lastStaff.getHeight())) { return StaffPosition.BELOW_STAVES; } else { return StaffPosition.WITHIN_STAVES; } } //--------------// // getTextStaff // //--------------// /** * Report the related staff for a text at the provided point, * since some texts (direction, lyrics) are preferably assigned to * the staff above if any. * * @param role the precise role of text glyph * @param point the provided point * @return the preferred staff */ public Staff getTextStaff (TextRole role, Point point) { Staff staff = null; if ((role == TextRole.Direction) || (role == TextRole.Lyrics)) { staff = getStaffAbove(point); } if (staff == null) { staff = getStaffAt(point); } return staff; } //------------// // getTopLeft // //------------// /** * Report the coordinates of the upper left corner of this system * in its containing score (not counting preceding dummy staves if * any). * * @return the top left corner */ public Point getTopLeft () { return topLeft; } //----------------// // isLeftOfStaves // //----------------// /** * Report whether the provided system point is on the left side of * the staves (on left of the starting barline). * * @param sysPt the system point to check * @return true if on left */ public boolean isLeftOfStaves (Point sysPt) { return sysPt.x < topLeft.x; } //----------------------// // refineLyricSyllables // //----------------------// public void refineLyricSyllables () { for (TreeNode node : getParts()) { SystemPart part = (SystemPart) node; part.refineLyricSyllables(); } } //----------// // setWidth // //----------// /** * Set the system width. * * @param unitWidth the system width, in units */ public void setWidth (int unitWidth) { Rectangle newBox = getBox(); reset(); newBox.width = unitWidth; setBox(newBox); getCenter(); } //----------// // toString // //----------// @Override public String toString () { StringBuilder sb = new StringBuilder(); sb.append("{System#") .append(id); if (topLeft != null) { sb.append(" topLeft=[") .append(topLeft.x) .append(",") .append(topLeft.y) .append("]"); } sb.append(" dimension=") .append(getBox().width) .append("x") .append(getBox().height); sb.append("}"); return sb.toString(); } }