//----------------------------------------------------------------------------//
// //
// L i n e F i l a m e n t A l i g n m e n t //
// //
//----------------------------------------------------------------------------//
// <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.glyph.facets.Glyph;
import omr.math.NaturalSpline;
import omr.run.Orientation;
import omr.sheet.Scale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.geom.Point2D;
import java.util.List;
/**
* Class {@code LineFilamentAlignment} is a GlyphAlignment
* implementation meant for long staff lines filaments.
*
* @author Hervé Bitteur
*/
public class LineFilamentAlignment
extends FilamentAlignment
{
//~ Static fields/initializers ---------------------------------------------
/** Specific application parameters */
private static final Constants constants = new Constants();
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(
LineFilamentAlignment.class);
//~ Constructors -----------------------------------------------------------
//
//-----------------------//
// LineFilamentAlignment //
//-----------------------//
/**
* Creates a new LineFilamentAlignment object.
*
* @param glyph the containing filament
*/
public LineFilamentAlignment (Glyph glyph)
{
super(glyph);
}
//~ Methods ----------------------------------------------------------------
//
//-----------//
// fillHoles //
//-----------//
/**
* Fill large holes (due to missing intermediate points) in
* this filament, by interpolating (or extrapolating) from the
* collection of rather parallel fils, this filament is part of
* (at provided pos index).
*
* @param pos the index of this filament in the provided collection
* @param fils the provided collection of parallel filaments
*/
public void fillHoles (int pos,
List<LineFilament> fils)
{
Scale scale = new Scale(glyph.getInterline());
int maxHoleLength = scale.toPixels(constants.maxHoleLength);
int virtualLength = scale.toPixels(constants.virtualSegmentLength);
// Look for long holes
Double holeStart = null;
boolean modified = false;
for (int ip = 0; ip < points.size(); ip++) {
Point2D point = points.get(ip);
if (holeStart == null) {
holeStart = point.getX();
} else {
double holeStop = point.getX();
double holeLength = holeStop - holeStart;
if (holeLength > maxHoleLength) {
// Try to insert artificial intermediate point(s)
int insert = (int) Math.rint(holeLength / virtualLength)
- 1;
if (insert > 0) {
logger.debug(
"Hole before ip: {} insert:{} for {}",
ip,
insert,
this);
double dx = holeLength / (insert + 1);
for (int i = 1; i <= insert; i++) {
int x = (int) Math.rint(holeStart + (i * dx));
Point2D pt = new Filler(
x,
pos,
fils,
virtualLength / 2).findInsertion();
if (pt == null) {
// Take default line point instead
pt = new VirtualPoint(
x,
getPositionAt(x, Orientation.HORIZONTAL));
}
logger.debug("Inserted {}", pt);
points.add(ip++, pt);
modified = true;
}
}
}
holeStart = holeStop;
}
}
if (modified) {
// Regenerate the underlying curve
line = NaturalSpline.interpolate(
points.toArray(new Point2D[points.size()]));
}
}
//~ Inner Classes ----------------------------------------------------------
//-----------//
// Constants //
//-----------//
private static final class Constants
extends ConstantSet
{
//~ Instance fields ----------------------------------------------------
final Scale.Fraction virtualSegmentLength = new Scale.Fraction(
6,
"Typical length used for virtual intermediate points");
final Scale.Fraction maxHoleLength = new Scale.Fraction(
8,
"Maximum length for holes without intermediate points");
}
//--------//
// Filler //
//--------//
/**
* A utility class to fill the filament holes with virtual points
*/
private static class Filler
{
//~ Instance fields ----------------------------------------------------
final int x; // Preferred abscissa for point insertion
final int pos; // Relative position within fils collection
final List<LineFilament> fils; // Collection of fils this one is part of
final int margin; // Margin on abscissa to lookup refs
//~ Constructors -------------------------------------------------------
public Filler (int x,
int pos,
List<LineFilament> fils,
int margin)
{
this.x = x;
this.pos = pos;
this.fils = fils;
this.margin = margin;
}
//~ Methods ------------------------------------------------------------
//---------------//
// findInsertion //
//---------------//
/**
* Look for a suitable insertion point.
* A point is returned only if it can be computed by interpolation,
* which needs one reference above and one reference below.
* Extrapolation is not reliable enough, so no insertion point is
* returned if we lack reference above or below.
*
* @return the computed insertion point, or null
*/
public Point2D findInsertion ()
{
// Check for a reference above
Neighbor one = findNeighbor(fils.subList(0, pos), -1);
if (one == null) {
return null;
}
// Check for a reference below
Neighbor two = findNeighbor(fils.subList(pos + 1, fils.size()), 1);
if (two == null) {
return null;
}
// Interpolate
double ratio = (double) (pos - one.pos) / (two.pos - one.pos);
return new VirtualPoint(
((1 - ratio) * one.point.getX()) + (ratio * two.point.getX()),
((1 - ratio) * one.point.getY()) + (ratio * two.point.getY()));
}
/**
* Browse the provided list in the desired direction to find a
* suitable point as a reference in a neighboring filament.
*/
private Neighbor findNeighbor (List<LineFilament> subfils,
int dir)
{
final int firstIdx = (dir > 0) ? 0 : (subfils.size() - 1);
final int breakIdx = (dir > 0) ? subfils.size() : (-1);
for (int i = firstIdx; i != breakIdx; i += dir) {
LineFilament fil = subfils.get(i);
Point2D pt = fil.getAlignment()
.findPoint(
x,
Orientation.HORIZONTAL,
margin);
if (pt != null) {
return new Neighbor(fil.getClusterPos(), pt);
}
}
return null;
}
//~ Inner Classes ------------------------------------------------------
/** Convey a point together with its relative cluster position */
private class Neighbor
{
//~ Instance fields ------------------------------------------------
final int pos;
final Point2D point;
//~ Constructors ---------------------------------------------------
public Neighbor (int pos,
Point2D point)
{
this.pos = pos;
this.point = point;
}
}
}
//--------------//
// VirtualPoint //
//--------------//
/**
* Used for artificial intermediate points
*/
private static class VirtualPoint
extends Point2D.Double
{
//~ Constructors -------------------------------------------------------
public VirtualPoint (double x,
double y)
{
super(x, y);
}
}
}