//----------------------------------------------------------------------------//
// //
// S t a f f M a n a g e r //
// //
//----------------------------------------------------------------------------//
// <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.grid;
import omr.constant.ConstantSet;
import omr.math.GeoPath;
import omr.sheet.Scale;
import omr.sheet.Sheet;
import omr.util.Navigable;
import static omr.util.VerticalSide.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Class {@code StaffManager} handles physical information about all
* the staves of a given sheet.
*
* @author Hervé Bitteur
*/
public class StaffManager
{
//~ Static fields/initializers ---------------------------------------------
/** Specific application parameters */
private static final Constants constants = new Constants();
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(StaffManager.class);
//~ Instance fields --------------------------------------------------------
//
/** The related sheet */
@Navigable(false)
private final Sheet sheet;
/** The sequence of staves, from top to bottom */
private final List<StaffInfo> staves = new ArrayList<>();
/** The systems tops per staff */
private Integer[] systemTops;
/** The parts tops per staff */
private Integer[] partTops;
//~ Constructors -----------------------------------------------------------
//
//--------------//
// StaffManager //
//--------------//
/**
* Creates a new StaffManager object.
*
* @param sheet the related sheet
*/
public StaffManager (Sheet sheet)
{
this.sheet = sheet;
}
//~ Methods ----------------------------------------------------------------
//
//----------//
// addStaff //
//----------//
/**
* Append one staff to the current collection
*
* @param staff the staff to add
*/
public void addStaff (StaffInfo staff)
{
staves.add(staff);
}
//--------------------//
// computeStaffLimits //
//--------------------//
public void computeStaffLimits ()
{
final int width = sheet.getWidth();
final int height = sheet.getHeight();
StaffInfo prevStaff = null;
double samplingDx = sheet.getScale()
.toPixelsDouble(constants.samplingDx);
final int sampleCount = (int) Math.rint(width / samplingDx);
samplingDx = width / sampleCount;
for (StaffInfo staff : staves) {
if (prevStaff == null) {
// Very first staff
staff.setLimit(
TOP,
new GeoPath(new Line2D.Double(0, 0, width, 0)));
} else {
// Define a middle line between last line of previous staff
// and first line of current staff
LineInfo prevLine = prevStaff.getLastLine();
LineInfo nextLine = staff.getFirstLine();
GeoPath middle = new GeoPath();
for (int i = 0; i <= sampleCount; i++) {
int x = (int) Math.rint(i * samplingDx);
double y = (prevLine.yAt(x) + nextLine.yAt(x)) / 2;
if (i == 0) {
middle.moveTo(x, y);
} else {
middle.lineTo(x, y);
}
}
// Point on right side
middle.lineTo(width, (prevLine.yAt(width) + nextLine.yAt(width)) / 2);
prevStaff.setLimit(BOTTOM, middle);
staff.setLimit(TOP, middle);
}
// Remember this staff for next one
prevStaff = staff;
}
// Bottom of last staff
prevStaff.setLimit(
BOTTOM,
new GeoPath(new Line2D.Double(0, height, width, height)));
}
//------------//
// getIndexOf //
//------------//
///TODO @Deprecated
public int getIndexOf (StaffInfo staff)
{
return staves.indexOf(staff);
}
//----------//
// getRange //
//----------//
/**
* Report a view on the range of staves from first to last
* (both inclusive).
*
* @param first the first staff of the range
* @param last the last staff of the range
* @return a view on this range
*/
public List<StaffInfo> getRange (StaffInfo first,
StaffInfo last)
{
return staves.subList(getIndexOf(first), getIndexOf(last) + 1);
}
//----------//
// getStaff //
//----------//
///TODO @Deprecated
public StaffInfo getStaff (int index)
{
return staves.get(index);
}
//------------//
// getStaffAt //
//------------//
/**
* Report the staff, among the sequence provided, whose area
* contains the provided point.
*
* @param point the provided point
* @param theStaves the staves sequence to search
* @return the containing staff, or null if none found
*/
public static StaffInfo getStaffAt (Point2D point,
List<StaffInfo> theStaves)
{
for (StaffInfo staff : theStaves) {
Rectangle2D box = staff.getAreaBounds();
if (point.getY() > box.getMaxY()) {
continue;
}
if (point.getY() < box.getMinY()) {
// Point above first staff, use first staff
// TODO: this decision is questionable
return null; //staff;
}
// If the point is ON the area boundary, it is NOT contained.
// So we use a rectangle of 1x1 pixels
if (staff.getArea()
.intersects(point.getX(), point.getY(), 1, 1)) {
return staff;
}
}
// Point below last staff, use last staff
// TODO: this decision is questionable
return null; //theStaves.get(theStaves.size() - 1);
}
//------------//
// getStaffAt //
//------------//
/**
* Report the staff whose area contains the provided point
*
* @param point the provided point
* @return the nearest staff, or null if none found
*/
public StaffInfo getStaffAt (Point2D point)
{
return getStaffAt(point, staves);
}
//---------------//
// getStaffCount //
//---------------//
/**
* Report the total number of staves, whatever their containing
* systems.
*
* @return the count of staves
*/
public int getStaffCount ()
{
return staves.size();
}
//-----------//
// getStaves //
//-----------//
/**
* Report an unmodifiable view (perhaps empty) of list of current
* staves.
*
* @return a view on staves
*/
public List<StaffInfo> getStaves ()
{
return Collections.unmodifiableList(staves);
}
//--------//
// render //
//--------//
/**
* Paint all the staff lines
*
* @param g the graphics context (including current color and stroke)
*/
public void render (Graphics2D g)
{
for (StaffInfo staff : staves) {
staff.renderAttachments(g);
}
}
//-------------//
// getPartTops //
//-------------//
/**
* @return the partTops
*/
public Integer[] getPartTops ()
{
return partTops;
}
//-------------//
// setPartTops //
//-------------//
/**
* @param partTops the partTops to set
*/
public void setPartTops (Integer[] partTops)
{
this.partTops = partTops;
}
//---------------//
// getSystemTops //
//---------------//
/**
* @return the systemTops
*/
public Integer[] getSystemTops ()
{
return systemTops;
}
//---------------//
// setSystemTops //
//---------------//
/**
* @param systemTops the systemTops to set
*/
public void setSystemTops (Integer[] systemTops)
{
this.systemTops = systemTops;
}
//-------//
// reset //
//-------//
/**
* Empty the whole collection of staves.
*/
public void reset ()
{
staves.clear();
}
//~ Inner Classes ----------------------------------------------------------
//
//-----------//
// Constants //
//-----------//
private static final class Constants
extends ConstantSet
{
//~ Instance fields ----------------------------------------------------
Scale.Fraction samplingDx = new Scale.Fraction(
4d,
"Abscissa sampling to compute top & bottom limits of staff areas");
}
}