//----------------------------------------------------------------------------//
// //
// B a r P a i n t 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.score.ui;
import omr.glyph.Shape;
import static omr.glyph.Shape.*;
import omr.grid.LineInfo;
import omr.grid.StaffInfo;
import omr.math.BasicLine;
import omr.math.Line;
import omr.score.entity.SystemPart;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.geom.Point2D;
import java.util.List;
/**
* Class {code BarPainter} handles the painting of a barline, according
* to its shape.
*
* @author Hervé Bitteur
*/
public class BarPainter
{
//~ Static fields/initializers ---------------------------------------------
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(
BarPainter.class);
/** Thin barline item. */
private static final BarItem thin = new BarItem(0.2);
/** Thick barline item. */
private static final BarItem thick = new BarItem(0.5);
/** Repeat dots item. */
private static final BarItem dots = new DotItem(0.4);
/** Typical abscissa gap between two vertical lines. */
private static final double delta = 0.2;
/** Typical abscissa gap when a repeat dot is involved. */
private static final double dotDelta = 0.3;
// Predefined painters, created once for all
private static final BarPainter THIN_BP = new BarPainter(thin);
private static final BarPainter THICK_BP = new BarPainter(thick);
private static final BarPainter DOUBLE_BP = new BarPainter(thin, thin);
private static final BarPainter FINAL_BP = new BarPainter(thin, thick);
private static final BarPainter REVERSE_FINAL_BP = new BarPainter(
thick,
thin);
private static final BarPainter LEFT_REPEAT_BP = new BarPainter(
thick,
thin,
dots);
private static final BarPainter RIGHT_REPEAT_BP = new BarPainter(
dots,
thin,
thick);
private static final BarPainter B2B_REPEAT_BP = new BarPainter(
dots,
thin,
thick,
thin,
dots);
//~ Instance fields --------------------------------------------------------
//
/** Sequence of items to paint. */
private final BarItem[] items;
//~ Constructors -----------------------------------------------------------
//
//------------//
// BarPainter //
//------------//
private BarPainter (BarItem... items)
{
this.items = items;
}
//~ Methods ----------------------------------------------------------------
//------//
// draw //
//------//
/**
* Perform the drawing of the barline structure.
*
* @param g graphics context
* @param topCenter center on top line
* @param botCenter center on bottom line
* @param part containing part
*/
public void draw (Graphics2D g,
Point2D topCenter,
Point2D botCenter,
SystemPart part)
{
double offset = -getGlobalWidth() / 2;
BarItem prev = null;
for (BarItem item : items) {
// Translate to beginning of current item
offset += gap(prev, item);
// Draw current item
item.draw(g, topCenter, botCenter, part, offset);
// Move to end of current item
offset += item.width;
prev = item;
}
}
//
//---------------//
// getBarPainter //
//---------------//
/**
* Factory method to get a BarPainter instance suitable for the
* provided barline shape.
*
* @param shape the provided barline shape
* @return the proper BarPainter instance
*/
public static BarPainter getBarPainter (Shape shape)
{
switch (shape) {
case PART_DEFINING_BARLINE:
case THIN_BARLINE:
return THIN_BP;
case THICK_BARLINE:
return THICK_BP;
case DOUBLE_BARLINE:
return DOUBLE_BP;
case FINAL_BARLINE:
return FINAL_BP;
case REVERSE_FINAL_BARLINE:
return REVERSE_FINAL_BP;
case LEFT_REPEAT_SIGN:
return LEFT_REPEAT_BP;
case RIGHT_REPEAT_SIGN:
return RIGHT_REPEAT_BP;
case BACK_TO_BACK_REPEAT_SIGN:
return B2B_REPEAT_BP;
default:
logger.error("Illegal barline shape " + shape);
return null;
}
}
//-----//
// gap //
//-----//
/**
* Compute the abscissa gap between {@code prev} item and
* {@code current} item.
*
* @param prev previous item
* @param current current item
* @return the x gap between these two items
*/
private double gap (BarItem prev,
BarItem current)
{
if (prev != null) {
if ((prev == dots) || (current == dots)) {
return dotDelta;
} else {
return delta;
}
} else {
return 0;
}
}
//----------------//
// getGlobalWidth //
//----------------//
/**
* Compute the global width of this barline structure.
*
* @return the global width (expressed in interline fraction)
*/
private double getGlobalWidth ()
{
double w = 0;
BarItem prev = null;
for (BarItem item : items) {
w += gap(prev, item);
w += item.width;
prev = item;
}
return w;
}
//~ Inner Classes ----------------------------------------------------------
//---------//
// BarItem //
//---------//
/**
* Handles the drawing of a bar, with provided width.
*/
private static class BarItem
{
//~ Instance fields ----------------------------------------------------
/** Typical item width, expressed in interline fraction. */
final double width;
//~ Constructors -------------------------------------------------------
public BarItem (double width)
{
this.width = width;
}
//~ Methods ------------------------------------------------------------
public void draw (Graphics2D g,
Point2D topCenter,
Point2D botCenter,
SystemPart part,
double offset)
{
int il = part.getScale()
.getInterline();
// Use a line stroke (=> problem with clipping)
// g.setStroke(new BasicStroke((float) (il * width)));
// Line2D line = new Line2D.Double(
// new Point2D.Double(
// topCenter.getX() + il * (offset + width / 2),
// topCenter.getY()),
// new Point2D.Double(
// botCenter.getX() + il * (offset + width / 2),
// botCenter.getY()));
// g.draw(line);
// Use a polygon (no need to play with clipping)
Polygon poly = new Polygon();
poly.addPoint(
(int) Math.rint(topCenter.getX() + (il * offset)),
(int) Math.rint(topCenter.getY()));
poly.addPoint(
(int) Math.rint(topCenter.getX() + (il * (offset + width))),
(int) Math.rint(topCenter.getY()));
poly.addPoint(
(int) Math.rint(botCenter.getX() + (il * (offset + width))),
(int) Math.rint(botCenter.getY()));
poly.addPoint(
(int) Math.rint(botCenter.getX() + (il * offset)),
(int) Math.rint(botCenter.getY()));
g.fill(poly);
}
}
//---------//
// DotItem //
//---------//
/**
* Handles the drawing of a pair of repeat dots.
*/
private static class DotItem
extends BarItem
{
//~ Constructors -------------------------------------------------------
public DotItem (double width)
{
super(width);
}
//~ Methods ------------------------------------------------------------
@Override
public void draw (Graphics2D g,
Point2D topCenter,
Point2D botCenter,
SystemPart part,
double offset)
{
Line bar = new BasicLine(
new double[]{topCenter.getX(), botCenter.getX()},
new double[]{topCenter.getY(), botCenter.getY()});
int il = part.getScale()
.getInterline();
for (StaffInfo staff : part.getInfo()
.getStaves()) {
// Compute staff-based center
List<LineInfo> lines = staff.getLines();
LineInfo staffMidLine = lines.get(lines.size() / 2);
Point2D inter = staffMidLine.verticalIntersection(bar);
// Draw each point
int scaledWidth = (int) Math.rint(il * width);
for (int i = -1; i <= 1; i += 2) {
g.fillOval(
(int) Math.rint(inter.getX() + (il * offset)),
(int) Math.rint(
inter.getY() + ((il * (i - width)) / 2)),
scaledWidth,
scaledWidth);
}
}
}
}
}