//----------------------------------------------------------------------------//
// //
// B a r s C h e c k 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.sheet;
import omr.check.Check;
import omr.check.CheckBoard;
import omr.check.CheckResult;
import omr.check.CheckSuite;
import omr.check.Checkable;
import omr.check.FailureResult;
import omr.check.Result;
import omr.check.SuccessResult;
import omr.constant.Constant;
import omr.constant.ConstantSet;
import omr.glyph.Shape;
import omr.glyph.facets.Glyph;
import omr.grid.Filament;
import omr.grid.StaffInfo;
import omr.grid.StaffManager;
import omr.lag.Section;
import omr.run.Orientation;
import static omr.run.Orientation.*;
import omr.selection.GlyphEvent;
import omr.selection.MouseMovement;
import omr.selection.SelectionService;
import omr.selection.UserEvent;
import omr.sheet.BarsChecker.BarCheckSuite;
import static omr.util.HorizontalSide.*;
import omr.util.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.jcip.annotations.NotThreadSafe;
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* Class {@code BarsChecker} is dedicated to physical checks of vertical
* sticks that are candidates for barlines.
*
* @author Hervé Bitteur
*/
@NotThreadSafe
public class BarsChecker
{
//~ Static fields/initializers ---------------------------------------------
/** Specific application parameters */
private static final Constants constants = new Constants();
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(BarsChecker.class);
/** Successful bar that embraces a whole system or part */
public static final SuccessResult BAR_PART_DEFINING = new SuccessResult(
"Bar-PartDefining");
/** Successful bar that embraces only a portion of a part */
private static final SuccessResult BAR_NOT_PART_DEFINING = new SuccessResult(
"Bar-NotPartDefining");
/** Failure, since bar is shorter than staff height */
private static final FailureResult TOO_SHORT_BAR = new FailureResult(
"Bar-TooShort");
/** Failure, since bar is on left or right side of the staff area */
private static final FailureResult OUTSIDE_STAFF_WIDTH = new FailureResult(
"Bar-OutsideStaffWidth");
/** Failure, since bar goes too high above first staff */
private static final FailureResult TOP_EXCESS = new FailureResult(
"Bar-TooHigh");
/** Failure, since bar top is too low below first staff */
private static final FailureResult LOW_TOP = new FailureResult(
"Bar-LowTop");
/** Failure, since bar bottom is too high above last staff */
private static final FailureResult HIGH_BOTTOM = new FailureResult(
"Bar-HighBottom");
/** Failure, since bar goes too low under last staff */
private static final FailureResult BOTTOM_EXCESS = new FailureResult(
"Bar-TooLow");
/** Failure, since bar has no end aligned with a staff */
private static final FailureResult NOT_STAFF_ANCHORED = new FailureResult(
"Bar-NotStaffAnchored");
/** Failure, since bar has a large chunk stuck on the top left */
private static final FailureResult TOP_LEFT_CHUNK = new FailureResult(
"Bar-TopLeftChunk");
/** Failure, since bar has a large chunk stuck on the top right */
private static final FailureResult TOP_RIGHT_CHUNK = new FailureResult(
"Bar-TopRightChunk");
/** Failure, since bar has a large chunk stuck on the bottom left */
private static final FailureResult BOTTOM_LEFT_CHUNK = new FailureResult(
"Bar-BottomLeftChunk");
/** Failure, since bar has a large chunk stuck on the bottom right */
private static final FailureResult BOTTOM_RIGHT_CHUNK = new FailureResult(
"Bar-BottomRightChunk");
/** Failure, since bar is too far from vertical */
private static final FailureResult NON_VERTICAL = new FailureResult(
"Bar-NonVertical");
/** Failure, since bar is too far from straight line */
private static final FailureResult NON_STRAIGHT = new FailureResult(
"Bar-NonStraight");
//~ Instance fields --------------------------------------------------------
/**
* true for rough tests (when retrieving staff grid),
* false for precise tests (when retrieving measures)
*/
private boolean rough;
/** Related sheet */
private final Sheet sheet;
/** Related scale */
private final Scale scale;
/** Related staves */
private final StaffManager staffManager;
/** Suite of checks to be performed */
private final BarCheckSuite suite;
/** Related context of a bar stick */
private final Map<Glyph, GlyphContext> contexts = new HashMap<>();
//~ Constructors -----------------------------------------------------------
//-------------//
// BarsChecker //
//-------------//
/**
* Prepare a bar checker on the provided sheet
*
* @param sheet the sheet to process
* @param rough true for rough tests (when retrieving staff frames),
* false for precise tests
*/
public BarsChecker (Sheet sheet,
boolean rough)
{
this.sheet = sheet;
this.rough = rough;
scale = sheet.getScale();
suite = new BarCheckSuite();
staffManager = sheet.getStaffManager();
}
//~ Methods ----------------------------------------------------------------
//-----------------//
// checkCandidates //
//-----------------//
/**
* From the list of vertical sticks, this method uses several tests
* to provide the initial collection of good barlines candidates.
*
* @param sticks the collection of candidate sticks
*/
public void checkCandidates (Collection<? extends Glyph> sticks)
{
// // Sort candidates according to their abscissa
// List<Glyph> sortedSticks = new ArrayList<Glyph>(sticks);
// Collections.sort(sortedSticks, Glyph.midPosComparator);
double minResult = constants.minCheckResult.getValue();
// Check each candidate stick in turn
for (Glyph stick : sticks) {
// Allocate the candidate context, and pass the whole check suite
GlyphContext context = new GlyphContext(stick);
double res = suite.pass(context);
if (logger.isDebugEnabled() || stick.isVip()) {
logger.info("suite => {}{} for {}",
(float) res,
(stick.getResult() != null)
? (" " + stick.getResult()) : "",
stick);
}
if ((stick.isBar() && stick.isManualShape()) || res >= minResult) {
// OK, we flag this candidate with proper barline shape
contexts.put(stick, context);
if ((!stick.isBar() || !stick.isManualShape())) {
stick.setShape(
isThickBar(stick)
? Shape.THICK_BARLINE : Shape.THIN_BARLINE);
}
// Additional processing for Bars that define a system or a part
// (they start AND end with precise staves horizontal limits)
if ((context.topStaff != -1) && (context.botStaff != -1)) {
// Here, we have both part & system defining bars
// System bars occur first
// (since glyphs are sorted by increasing abscissa)
stick.setResult(BAR_PART_DEFINING);
logger.debug(
"Part-defining Barline from staff {} to staff {} {}",
context.topStaff, context.botStaff, stick);
} else {
if (logger.isDebugEnabled()) {
logger.debug("Non-Part-defining Bar line {}{}",
(context.topStaff != -1)
? (" topIdx=" + context.topStaff) : "",
(context.botStaff != -1)
? (" botIdx=" + context.botStaff) : "");
}
stick.setResult(BAR_NOT_PART_DEFINING);
}
} else {
if (stick.isBar()) {
if (logger.isDebugEnabled() || stick.isVip()) {
logger.info("Purged {} {}",
stick.idString(), stick.getShape());
}
stick.setShape(null);
}
}
}
}
//---------------//
// getCheckBoard //
//---------------//
public CheckBoard<GlyphContext> getCheckBoard ()
{
return new BarCheckBoard(
getSuite(),
sheet.getNest().getGlyphService(),
new Class<?>[]{GlyphEvent.class});
}
//----------//
// getSuite //
//----------//
/**
* Report the suite currently defined.
* (used by package mate SystemsBuilder)
*
* @return the check suite
*/
public CheckSuite<GlyphContext> getSuite ()
{
return suite;
}
//------------------//
// getAlienPixelsIn //
//------------------//
private int getAlienPixelsIn (Glyph glyph,
Rectangle absRoi)
{
Predicate<Section> predicate = new Predicate<Section>()
{
@Override
public boolean check (Section section)
{
return (section.getGlyph() == null)
|| (section.getGlyph().getShape() != Shape.STAFF_LINE);
}
};
int total = 0;
total += glyph.getAlienPixelsFrom(
sheet.getVerticalLag(),
absRoi,
predicate);
total += glyph.getAlienPixelsFrom(
sheet.getHorizontalLag(),
absRoi,
predicate);
return total;
}
//-------------------//
// initializeContext //
//-------------------//
private void initializeContext (GlyphContext context)
{
Glyph stick = context.stick;
StaffInfo startStaff = staffManager.getStaffAt(
stick.getStartPoint(VERTICAL));
StaffInfo stopStaff = staffManager.getStaffAt(
stick.getStopPoint(VERTICAL));
// Remember top & bottom areas
context.topArea = staffManager.getIndexOf(startStaff);
context.bottomArea = staffManager.getIndexOf(stopStaff);
// Check whether this stick embraces more than one staff
context.isPartDefining = context.topArea != context.bottomArea;
// Remember if this is a thick stick
context.isThick = isThickBar(stick);
}
//------------//
// isThickBar //
//------------//
/**
* Check if the stick/bar is a thick one
*
* @param stick the bar stick to check
*
* @return true if thick
*/
private boolean isThickBar (Glyph stick)
{
// Max width of a thin bar line, otherwise this must be a thick bar
final int maxThinWidth = scale.toPixels(constants.maxThinWidth);
// Average width of the stick
final int meanWidth = (int) Math.rint(
(double) stick.getWeight() / (double) stick.getLength(
Orientation.VERTICAL));
return meanWidth > maxThinWidth;
}
//~ Inner Classes ----------------------------------------------------------
//
//--------------//
// StaffAnchors //
//--------------//
/**
* Record which staves the top and the bottom of a stick are anchored to
*/
public static class StaffAnchors
{
//~ Instance fields ----------------------------------------------------
public final int top;
public final int bot;
//~ Constructors -------------------------------------------------------
public StaffAnchors (int topIdx,
int botIdx)
{
this.top = topIdx;
this.bot = botIdx;
}
}
//---------------//
// BarCheckSuite //
//---------------//
class BarCheckSuite
extends CheckSuite<GlyphContext>
{
//~ Constructors -------------------------------------------------------
public BarCheckSuite ()
{
super("Bar", constants.minCheckResult.getValue());
// Be very careful with check order, because of side-effects
// topArea, bottomArea, isPartDefining, isThick are already set
//
add(1, new VerticalCheck());
add(1, new LeftCheck());
add(1, new RightCheck());
add(1, new HeightDiffCheck());
add(1, new RadiusCheck());
add(1, new TopLeftChunkCheck());
add(1, new TopRightChunkCheck());
add(1, new BottomLeftChunkCheck());
add(1, new BottomRightChunkCheck());
add(1, new TopInterCheck());
add(1, new BottomInterCheck());
if (!rough) {
add(1, new TopCheck()); // May set topStaff
add(1, new BottomCheck()); // May set botStaff
add(1, new AnchorCheck()); // Reads topStaff & botStaff
}
if (logger.isDebugEnabled()) {
dump();
}
// // We don't check that the bar does not start before first staff,
// // this is too restrictive because of alternate endings. We however
// // do check that the bar does not end after last staff of the
// // last part of the system.
// int barAbscissa = bar.getMidPos();
// int systemBottom = scoreSystem.getLastPart()
// .getLastStaff()
// .getInfo()
// .getLastLine()
// .yAt(barAbscissa);
//
// if ((bar.getStop() - systemBottom) > maxDy) {
// if (logger.isDebugEnabled()) {
// logger.debug("Bar stopping too low");
// }
//
// bar.setResult(NOT_WITHIN_SYSTEM);
//
// continue;
// }
}
}
//--------------//
// GlyphContext //
//--------------//
class GlyphContext
implements Checkable
{
//~ Instance fields ----------------------------------------------------
/** The stick being checked */
Glyph stick;
/** Indicates a part-defining stick (embracing more than one staff) */
boolean isPartDefining;
/** Indicates a thick bar stick */
boolean isThick;
/** Nearest staff for top of bar stick */
int topArea = -1;
/** Nearest staff for bottom of bar stick */
int bottomArea = -1;
/** Precise staff for top of bar stick, assigned when OK */
int topStaff = -1;
/** Precise staff for bottom of bar stick, assigned when OK */
int botStaff = -1;
//~ Constructors -------------------------------------------------------
public GlyphContext (Glyph stick)
{
this.stick = stick;
initializeContext(this); // Initialization before any check
}
//~ Methods ------------------------------------------------------------
@Override
public boolean isVip ()
{
return stick.isVip();
}
@Override
public void setResult (Result result)
{
stick.setResult(result);
}
@Override
public void setVip ()
{
stick.setVip();
}
@Override
public String toString ()
{
return "stick#" + stick.getId();
}
}
//-------------//
// AnchorCheck //
//-------------//
private class AnchorCheck
extends Check<GlyphContext>
{
//~ Constructors -------------------------------------------------------
protected AnchorCheck ()
{
super(
"Anchor",
"Check top and bottom alignment of bars (thick/thin) with staff",
constants.booleanThreshold,
constants.booleanThreshold,
true,
NOT_STAFF_ANCHORED);
}
//~ Methods ------------------------------------------------------------
// Make sure that at least top or bottom are staff anchors, and that
// both are staff anchors in the case of thick bars.
@Override
protected double getValue (GlyphContext context)
{
if (rough) {
if (context.isPartDefining) {
return 1;
} else {
if ((context.topStaff != -1) && (context.botStaff != -1)) {
return 1;
}
}
return 0;
} else {
if (context.isThick) {
if ((context.topStaff != -1) && (context.botStaff != -1)) {
return 1;
}
} else {
if ((context.topStaff != -1) || (context.botStaff != -1)) {
return 1;
}
}
return 0;
}
}
}
//---------------//
// BarCheckBoard //
//---------------//
/**
* A specific board dedicated to physical checks of barline sticks
*/
private class BarCheckBoard
extends CheckBoard<GlyphContext>
{
//~ Constructors -------------------------------------------------------
public BarCheckBoard (CheckSuite<GlyphContext> suite,
SelectionService eventService,
Class[] eventList)
{
super("BarlineCheck", suite, eventService, eventList);
}
//~ Methods ------------------------------------------------------------
@Override
public void onEvent (UserEvent event)
{
try {
// Ignore RELEASING
if (event.movement == MouseMovement.RELEASING) {
return;
}
if (event instanceof GlyphEvent) {
BarsChecker.GlyphContext context = null;
GlyphEvent glyphEvent = (GlyphEvent) event;
Glyph glyph = glyphEvent.getData();
if (glyph != null) {
// Make sure this is a rather vertical stick
if (Math.abs(glyph.getInvertedSlope()) <= constants.maxCoTangentForCheck.
getValue()) {
// Apply a fresh suite
context = new BarsChecker.GlyphContext(glyph);
applySuite(new BarCheckSuite(), context);
return;
}
}
tellObject(null);
}
} catch (Exception ex) {
logger.warn(getClass().getName() + " onEvent error", ex);
}
}
}
//-------------//
// BottomCheck //
//-------------//
private class BottomCheck
extends LongCheck
{
//~ Constructors -------------------------------------------------------
protected BottomCheck ()
{
super(
"Bottom",
"Check that bottom of stick is close to bottom of staff",
constants.maxStaffShiftDyLow,
constants.maxStaffShiftDyHigh,
false,
BOTTOM_EXCESS);
}
//~ Methods ------------------------------------------------------------
// Retrieve the distance with proper staff border
@Override
protected double getValue (GlyphContext context)
{
Glyph stick = context.stick;
Point2D stop = stick.getStopPoint(VERTICAL);
// Which staff area contains the bottom of the stick?
StaffInfo staff = staffManager.getStaffAt(stop);
// How far are we from the stop of the staff?
double staffBottom = staff.getLastLine().yAt(stop.getX());
double dy = sheet.getScale().pixelsToFrac(Math.
abs(staffBottom - stop.
getY()));
// Change limits according to rough & partDefining
if (rough && context.isPartDefining) {
setLowHigh(
constants.maxStaffShiftDyLowRough,
constants.maxStaffShiftDyHighRough);
} else {
setLowHigh(
constants.maxStaffShiftDyLow,
constants.maxStaffShiftDyHigh);
}
// Side-effect
if (dy <= getLow()) {
context.botStaff = context.bottomArea;
}
return dy;
}
}
//------------------//
// BottomInterCheck //
//------------------//
private class BottomInterCheck
extends Check<GlyphContext>
{
//~ Constructors -------------------------------------------------------
protected BottomInterCheck ()
{
super(
"Bottom",
"Check that bottom of stick reaches last staff",
constants.maxStaffYGapLow,
constants.maxStaffYGapHigh,
false,
HIGH_BOTTOM);
}
//~ Methods ------------------------------------------------------------
// Retrieve the distance with proper staff border
@Override
protected double getValue (GlyphContext context)
{
Glyph stick = context.stick;
Point2D stop = stick.getStopPoint(VERTICAL);
// Which staff area contains the bottom of the stick?
StaffInfo staff = staffManager.getStaffAt(stop);
// How far are we from the stop of the staff?
double staffBottom = staff.getLastLine().yAt(stop.getX());
double dy = sheet.getScale().pixelsToFrac(Math.
abs(staffBottom - stop.
getY()));
return dy;
}
}
//-----------//
// Constants //
//-----------//
private static final class Constants
extends ConstantSet
{
//~ Instance fields ----------------------------------------------------
Scale.Fraction chunkWidth = new Scale.Fraction(
0.3,
"Width of box to look for chunks");
Scale.Fraction chunkHalfHeight = new Scale.Fraction(
0.5,
"Half height of box to look for chunks");
Constant.Ratio chunkRatioHigh = new Constant.Ratio(
4.0,
"High Minimum ratio of alien pixels to detect chunks");
Constant.Ratio chunkRatioLow = new Constant.Ratio(
2.0,
"Low Minimum ratio of alien pixels to detect chunks");
Scale.Fraction maxStaffShiftDyHigh = new Scale.Fraction(
0.4,
"High Maximum vertical distance between a bar edge and the staff line");
Scale.Fraction maxStaffShiftDyHighRough = new Scale.Fraction(
4.0,
"Rough high Maximum vertical distance between a bar edge and the staff line");
Scale.Fraction maxStaffShiftDyLow = new Scale.Fraction(
0.3,
"Low Maximum vertical distance between a bar edge and the staff line");
Scale.Fraction maxStaffShiftDyLowRough = new Scale.Fraction(
2.0,
"Rough low Maximum vertical distance between a bar edge and the staff line");
Scale.Fraction maxStaffDHeightHigh = new Scale.Fraction(
0.4,
"High Maximum difference between a bar length and min staff height");
Scale.Fraction maxStaffDHeightHighRough = new Scale.Fraction(
1,
"Rough high Maximum difference between a bar length and min staff height");
Scale.Fraction maxStaffDHeightLow = new Scale.Fraction(
0.2,
"Low Maximum difference between a bar length and min staff height");
Scale.Fraction maxStaffDHeightLowRough = new Scale.Fraction(
0.5,
"Rough low Maximum difference between a bar length and min staff height");
Scale.Fraction maxStaffYGapLow = new Scale.Fraction(
2,
"Low Maximum dy between a bar edge and target staff line");
Scale.Fraction maxStaffYGapHigh = new Scale.Fraction(
3,
"High Maximum dy between a bar edge and target staff line");
Scale.Fraction minStaffDxHigh = new Scale.Fraction(
0,
"High Minimum horizontal distance between a bar and a staff edge");
Scale.Fraction minStaffDxHighRough = new Scale.Fraction(
-3,
"Rough high Minimum horizontal distance between a bar and a staff edge");
Scale.Fraction minStaffDxLow = new Scale.Fraction(
-1,
"Low Minimum horizontal distance between a bar and a staff edge");
Scale.Fraction minStaffDxLowRough = new Scale.Fraction(
-5,
"Rough low Minimum horizontal distance between a bar and a staff edge");
Constant.Double maxSlopeLow = new Constant.Double(
"slope",
0.1,
"Low maximum difference with global slope");
Constant.Double maxSlopeHigh = new Constant.Double(
"slope",
0.2,
"High maximum difference with global slope");
Scale.Fraction minRadiusLow = new Scale.Fraction(
25,
"Low minimum radius");
Scale.Fraction minRadiusHigh = new Scale.Fraction(
60,
"High minimum radius");
Scale.Fraction maxThinWidth = new Scale.Fraction(
0.3,
"Maximum width of a normal bar, versus a thick bar");
Check.Grade minCheckResult = new Check.Grade(
0.50,
"Minimum result for suite of check");
Constant.Ratio minStaffCountForLongBar = new Constant.Ratio(
2,
"Minimum length for long bars, stated in number of staff heights");
Constant.Ratio booleanThreshold = new Constant.Ratio(
0.5,
"* DO NOT EDIT * - switch between true & false for a boolean");
Constant.Double maxCoTangentForCheck = new Constant.Double(
"cotangent",
0.1,
"Maximum cotangent for checking a barline candidate");
}
//------------//
// ChunkCheck //
//------------//
private abstract class ChunkCheck
extends Check<GlyphContext>
{
//~ Instance fields ----------------------------------------------------
// Width for chunk window
protected final int nWidth;
// Height for chunk window
protected final int nHeight;
//~ Constructors -------------------------------------------------------
protected ChunkCheck (String name,
String description,
FailureResult redResult)
{
super(
name,
description,
constants.chunkRatioLow,
constants.chunkRatioHigh,
false,
redResult);
// Adjust chunk window according to system scale
nWidth = scale.toPixels(constants.chunkWidth);
nHeight = scale.toPixels(constants.chunkHalfHeight);
}
//~ Methods ------------------------------------------------------------
protected abstract Rectangle getBox (Glyph stick);
@Override
protected double getValue (GlyphContext context)
{
Glyph stick = context.stick;
Rectangle box = getBox(stick);
int aliens = getAlienPixelsIn(stick, box);
int area = box.width * box.height;
// Normalize the ratio with stick length
double ratio = (1000 * aliens) / ((double) area * stick.getLength(
Orientation.VERTICAL));
logger.debug("{} {} aliens:{} area:{} ratio:{}",
stick.idString(), getName(), aliens, area,
(float) ratio);
return ratio;
}
}
//----------------------//
// BottomLeftChunkCheck //
//----------------------//
/**
* Check for lack of chunk on lower left side of the bar stick
*/
private class BottomLeftChunkCheck
extends ChunkCheck
{
//~ Constructors -------------------------------------------------------
protected BottomLeftChunkCheck ()
{
super(
"BLChunk",
"Check for no big chunk stuck on lower left side of stick",
BOTTOM_LEFT_CHUNK);
}
//~ Methods ------------------------------------------------------------
@Override
protected Rectangle getBox (Glyph stick)
{
Point2D bottom = stick.getStopPoint(VERTICAL);
Rectangle box = new Rectangle(
(int) Math.rint(bottom.getX() - nWidth),
(int) Math.rint(bottom.getY() - (1.5 * nHeight)),
nWidth,
2 * nHeight);
stick.addAttachment("bl", box);
return box;
}
}
//-----------------------//
// BottomRightChunkCheck //
//-----------------------//
/**
* Check for lack of chunk on lower right side of the bar stick
*/
private class BottomRightChunkCheck
extends ChunkCheck
{
//~ Constructors -------------------------------------------------------
protected BottomRightChunkCheck ()
{
super(
"BRChunk",
"Check for no big chunk stuck on lower right side of stick",
BOTTOM_RIGHT_CHUNK);
}
//~ Methods ------------------------------------------------------------
@Override
protected Rectangle getBox (Glyph stick)
{
Point2D bottom = stick.getStopPoint(VERTICAL);
Rectangle box = new Rectangle(
(int) Math.rint(bottom.getX()),
(int) Math.rint(bottom.getY() - (1.5 * nHeight)),
nWidth,
2 * nHeight);
stick.addAttachment("br", box);
return box;
}
}
//-----------------//
// HeightDiffCheck //
//-----------------//
private class HeightDiffCheck
extends Check<GlyphContext>
{
//~ Constructors -------------------------------------------------------
protected HeightDiffCheck ()
{
super(
"HeightDiff",
"Check that stick is as long as minimum staff height",
rough ? constants.maxStaffDHeightLowRough
: constants.maxStaffDHeightLow,
rough ? constants.maxStaffDHeightHighRough
: constants.maxStaffDHeightHigh,
false,
TOO_SHORT_BAR);
}
//~ Methods ------------------------------------------------------------
// Retrieve the length data
@Override
protected double getValue (GlyphContext context)
{
Glyph stick = context.stick;
int height = Integer.MAX_VALUE;
// Check wrt every staff in the stick getRange
for (int i = context.topArea; i <= context.bottomArea; i++) {
StaffInfo area = staffManager.getStaff(i);
height = Math.min(height, area.getHeight());
}
return sheet.getScale().pixelsToFrac(
height - stick.getLength(Orientation.VERTICAL));
}
}
//-----------//
// LeftCheck //
//-----------//
private class LeftCheck
extends Check<GlyphContext>
{
//~ Constructors -------------------------------------------------------
protected LeftCheck ()
{
super(
"Left",
"Check that stick is on the right of staff beginning bar",
rough ? constants.minStaffDxLowRough : constants.minStaffDxLow,
rough ? constants.minStaffDxHighRough : constants.minStaffDxHigh,
true,
OUTSIDE_STAFF_WIDTH);
}
//~ Methods ------------------------------------------------------------
// Retrieve the stick abscissa
@Override
protected double getValue (GlyphContext context)
{
Glyph stick = context.stick;
double dist = Double.MAX_VALUE;
// Check wrt every staff in the stick range
for (int i = context.topArea; i <= context.bottomArea; i++) {
StaffInfo staff = staffManager.getStaff(i);
Point2D top = staff.getFirstLine().getEndPoint(LEFT);
Point2D bot = staff.getLastLine().getEndPoint(LEFT);
double y = (top.getY() + bot.getY()) / 2;
double x = stick.getPositionAt(y, Orientation.VERTICAL);
double dx = x - staff.getAbscissa(LEFT);
dist = Math.min(dist, dx);
}
return sheet.getScale().pixelsToFrac(dist);
}
}
//-----------//
// LongCheck //
//-----------//
/**
* This kind of check allows to force the result in certain cases.
*/
private abstract class LongCheck
extends Check<GlyphContext>
{
//~ Constructors -------------------------------------------------------
public LongCheck (String name,
String description,
Constant.Double low,
Constant.Double high,
boolean covariant,
FailureResult redResult)
{
super(name, description, low, high, covariant, redResult);
}
//~ Methods ------------------------------------------------------------
@Override
public CheckResult pass (GlyphContext context,
CheckResult result,
boolean update)
{
if (rough && context.isPartDefining) {
// Since this stick is a long one, embracing several staves,
// this check is not relevant
result.value = getValue(context); // For possible side-effect
result.flag = GREEN;
return result;
} else {
return super.pass(context, result, update);
}
}
}
//-------------//
// RadiusCheck //
//-------------//
private class RadiusCheck
extends Check<GlyphContext>
{
//~ Constructors -------------------------------------------------------
protected RadiusCheck ()
{
super(
"Radius",
"Check mean stick radius of curvature",
constants.minRadiusLow,
constants.minRadiusHigh,
true,
NON_STRAIGHT);
}
//~ Methods ------------------------------------------------------------
// Retrieve a measure of x variance
@Override
protected double getValue (GlyphContext context)
{
Glyph stick = context.stick;
if (stick instanceof Filament) {
Filament fil = (Filament) stick;
return sheet.getScale().pixelsToFrac(fil.getMeanCurvature());
} else {
return getHigh(); // A pass-through check
}
}
}
//------------//
// RightCheck //
//------------//
private class RightCheck
extends Check<GlyphContext>
{
//~ Constructors -------------------------------------------------------
protected RightCheck ()
{
super(
"Right",
"Check that stick is on the left of staff ending bar",
rough ? constants.minStaffDxLowRough : constants.minStaffDxLow,
rough ? constants.minStaffDxHighRough : constants.minStaffDxHigh,
true,
OUTSIDE_STAFF_WIDTH);
}
//~ Methods ------------------------------------------------------------
// Retrieve the stick abscissa
@Override
protected double getValue (GlyphContext context)
{
Glyph stick = context.stick;
double dist = Double.MAX_VALUE;
// Check wrt every staff in the stick getRange
for (int i = context.topArea; i <= context.bottomArea; i++) {
StaffInfo staff = staffManager.getStaff(i);
Point2D top = staff.getFirstLine().getEndPoint(RIGHT);
Point2D bot = staff.getLastLine().getEndPoint(RIGHT);
double y = (top.getY() + bot.getY()) / 2;
double x = (stick instanceof Filament)
? ((Filament) stick).getPositionAt(
y,
Orientation.VERTICAL) : stick.getLine().xAtY(y);
dist = Math.min(dist, staff.getAbscissa(RIGHT) - x);
}
return sheet.getScale().pixelsToFrac(dist);
}
}
//----------//
// TopCheck //
//----------//
private class TopCheck
extends LongCheck
{
//~ Constructors -------------------------------------------------------
protected TopCheck ()
{
super(
"Top",
"Check that top of stick is close to top of staff",
constants.maxStaffShiftDyLow,
constants.maxStaffShiftDyHigh,
false,
TOP_EXCESS);
}
//~ Methods ------------------------------------------------------------
// Retrieve the distance with proper staff border
@Override
protected double getValue (GlyphContext context)
{
Glyph stick = context.stick;
Point2D start = stick.getStartPoint(VERTICAL);
// Which staff area contains the top of the stick?
StaffInfo staff = staffManager.getStaffAt(start);
// How far are we from the start of the staff?
double staffTop = staff.getFirstLine().yAt(start.getX());
double dy = sheet.getScale().pixelsToFrac(Math.abs(staffTop - start.
getY()));
// Change limits according to rough & partDefining
if (rough && context.isPartDefining) {
setLowHigh(
constants.maxStaffShiftDyLowRough,
constants.maxStaffShiftDyHighRough);
} else {
setLowHigh(
constants.maxStaffShiftDyLow,
constants.maxStaffShiftDyHigh);
}
// Side-effect
if (dy <= getLow()) {
context.topStaff = context.topArea;
}
return dy;
}
}
//---------------//
// TopInterCheck //
//---------------//
private class TopInterCheck
extends Check<GlyphContext>
{
//~ Constructors -------------------------------------------------------
protected TopInterCheck ()
{
super(
"TopInter",
"Check that top of stick intersects a staff",
constants.maxStaffYGapLow,
constants.maxStaffYGapHigh,
false,
LOW_TOP);
}
//~ Methods ------------------------------------------------------------
// Retrieve the distance with proper staff line
@Override
protected double getValue (GlyphContext context)
{
Glyph stick = context.stick;
Point2D start = stick.getStartPoint(VERTICAL);
// Which staff area contains the top of the stick?
StaffInfo staff = staffManager.getStaffAt(start);
// How far are we from the start of the staff?
double staffTop = staff.getFirstLine().yAt(start.getX());
double dy = sheet.getScale().pixelsToFrac(Math.abs(staffTop - start.
getY()));
return dy;
}
}
//-------------------//
// TopLeftChunkCheck //
//-------------------//
/**
* Check for lack of chunk on upper left side of the bar stick
*/
private class TopLeftChunkCheck
extends ChunkCheck
{
//~ Constructors -------------------------------------------------------
protected TopLeftChunkCheck ()
{
super(
"TLChunk",
"Check for no big chunk stuck on upper left side of stick",
TOP_LEFT_CHUNK);
}
//~ Methods ------------------------------------------------------------
@Override
protected Rectangle getBox (Glyph stick)
{
Point2D top = stick.getStartPoint(VERTICAL);
Rectangle box = new Rectangle(
(int) Math.rint(top.getX() - nWidth),
(int) Math.rint(top.getY() - (nHeight / 2)),
nWidth,
2 * nHeight);
stick.addAttachment("tl", box);
return box;
}
}
//--------------------//
// TopRightChunkCheck //
//--------------------//
/**
* Check for lack of chunk on upper right side of the bar stick
*/
private class TopRightChunkCheck
extends ChunkCheck
{
//~ Constructors -------------------------------------------------------
protected TopRightChunkCheck ()
{
super("TRChunk",
"Check for no big chunk stuck on upper right side of stick",
TOP_RIGHT_CHUNK);
}
//~ Methods ------------------------------------------------------------
@Override
protected Rectangle getBox (Glyph stick)
{
Point2D top = stick.getStartPoint(VERTICAL);
Rectangle box = new Rectangle(
(int) Math.rint(top.getX()),
(int) Math.rint(top.getY() - (nHeight / 2)),
nWidth,
2 * nHeight);
stick.addAttachment("tr", box);
return box;
}
}
//---------------//
// VerticalCheck //
//---------------//
private class VerticalCheck
extends Check<GlyphContext>
{
//~ Constructors -------------------------------------------------------
protected VerticalCheck ()
{
super("Vertical",
"Check that stick is vertical, according to global slope",
constants.maxSlopeLow,
constants.maxSlopeHigh,
false,
NON_VERTICAL);
}
//~ Methods ------------------------------------------------------------
// Retrieve the difference between stick slope and global slope
@Override
protected double getValue (GlyphContext context)
{
Glyph stick = context.stick;
Point2D start = stick.getStartPoint(VERTICAL);
Point2D stop = stick.getStopPoint(VERTICAL);
// Beware of sign of stickSlope (it is opposite of globalSlope)
double stickSlope = -(stop.getX() - start.getX()) / (stop.getY()
- start.getY());
return Math.abs(stickSlope - sheet.getSkew().getSlope());
}
}
}