//----------------------------------------------------------------------------//
// //
// S h a p e //
// //
//----------------------------------------------------------------------------//
// <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.glyph;
import omr.constant.Constant;
import omr.ui.symbol.ShapeSymbol;
import omr.ui.symbol.Symbols;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Class {@code Shape} defines the comprehensive list of glyph shapes.
* It is organized according to the Unicode Standard 4.0, with a few addition
* for convenience only.
*
* <p>The enumeration begins with physical shapes (which are the
* only ones usable for training) and ends with the logical shapes.
* The method {@link #isTrainable()} can be used to disambiguate between
* physical and logical shapes.</p>
*
* <p><b>Nota</b>: All the physical shapes <b>MUST</b> have different
* characteristics for the training to work correctly.
* The ART evaluator uses moments that are invariant to translation, scaling
* and rotation (and to symmetry as well).
* Shapes that exhibit some symmetry (like FERMATA vs FERMATA_BELOW) would
* be considered as the same shape by the ART evaluator.
* Therefore, the strategy is to define a single shape (FERMATA_SET) for the
* evaluator, leaving the final disambiguation between FERMATA_BELOW and
* FERMATA to tests performed beyond the ART evaluator.
* FERMATA_SET belongs to the physical shapes, while FERMATA_BELOW and
* FERMATA belong to the logical shapes.
* All shapes whose name ends with "_set" are in this case.</p>
*
* <p>As far as possible, a symbol should be generated for every shape.</p>
*
* <p>A shape may have a related "decorated" symbol. For example the BREVE_REST
* is similar to a black rectangle which is used for training / recognition and
* the related symbol is used for drawing in score view. However, in menu items,
* it is displayed as a black rectangle surrounded by a staff line above and a
* staff line below.
* The method {@link #getDecoratedSymbol()} returns the symbol to use in menu
* items.</p>
*
* @author Hervé Bitteur
*/
public enum Shape
{
/**
* ================================================================
* Nota: Avoid changing the order of these physical shapes,
* otherwise the evaluators won't detect this and you'll have to
* retrain them on your own.
* =========================================================================
*/
//
// Sets --------------------------------------------------------------------
//
DOT_set("Dot set", new Color(0x0cccc)),
FERMATA_set("Set of Fermata's"),
HW_REST_set("Half & Whole Rest set"),
TIME_69_set("Time 6 & 9 set"),
FLAG_1_set("Single flag set"),
FLAG_2_set("Double flag set"),
FLAG_3_set("Triple flag set"),
FLAG_4_set("Quadruple flag set"),
FLAG_5_set("Quintuple flag set"),
WEDGE_set("Crescendo & Decrescendo set"),
TURN_set("Turn set"),
//
// Bars --------------------------------------------------------------------
//
DAL_SEGNO("Repeat from the sign"),
DA_CAPO("Repeat from the beginning"),
SEGNO("Sign"),
CODA("Closing section"),
BREATH_MARK("Breath Mark"),
CAESURA("Caesura"),
BRACE("Brace"),
BRACKET("Bracket"),
//
// Clefs -------------------------------------------------------------------
//
G_CLEF("Treble Clef", new Color(0xff99ff)),
G_CLEF_SMALL("Small Treble Clef", new Color(0xff99ff)),
G_CLEF_8VA("Treble Clef Ottava Alta", new Color(0xff99ff)),
G_CLEF_8VB("Treble Clef Ottava Bassa", new Color(0xff99ff)),
C_CLEF("Ut Clef", new Color(0xff99ff)),
F_CLEF("Bass Clef"),
F_CLEF_SMALL("Small Bass Clef"),
F_CLEF_8VA("Bass Clef Ottava Alta"),
F_CLEF_8VB("Bass Clef Ottava Bassa"),
PERCUSSION_CLEF("Percussion Clef"),
FLAT("Minus one half step", new Color(0x00aaaa)),
NATURAL("Natural value", new Color(0x0066ff)),
SHARP("Plus one half step", new Color(0x3399ff)),
DOUBLE_SHARP("Double Sharp"),
DOUBLE_FLAT("Double Flat"),
TIME_ZERO("Digit 0"),
TIME_ONE("Digit 1"),
TIME_TWO("Digit 2"),
TIME_THREE("Digit 3"),
TIME_FOUR("Digit 4"),
TIME_FIVE("Digit 5"),
TIME_SEVEN("Digit 7"),
TIME_EIGHT("Digit 8"),
TIME_TWELVE("Number 12"),
TIME_SIXTEEN("Number 16"),
TIME_FOUR_FOUR("Rational 4/4"),
TIME_TWO_TWO("Rational 2/2"),
TIME_TWO_FOUR("Rational 2/4"),
TIME_THREE_FOUR("Rational 3/4"),
TIME_SIX_EIGHT("Rational 6/8"),
COMMON_TIME("Alpha = 4/4", new Color(0xcc6600)),
CUT_TIME("Semi-Alpha = 2/2"),
OTTAVA_ALTA("8 va", new Color(0xcc66ff)),
OTTAVA_BASSA("8 vb", new Color(0xcc66ff)),
//
// Key signatures ----------------------------------------------------------
//
KEY_FLAT_7("Seven Flats"),
KEY_FLAT_6("Six Flats"),
KEY_FLAT_5("Five Flats"),
KEY_FLAT_4("Four Flats"),
KEY_FLAT_3("Three Flats"),
KEY_FLAT_2("Two Flats"),
KEY_SHARP_2("Two Sharps"),
KEY_SHARP_3("Three Sharps"),
KEY_SHARP_4("Four Sharps"),
KEY_SHARP_5("Five Sharps"),
KEY_SHARP_6("Six Sharps"),
KEY_SHARP_7("Seven Sharps"),
//
// Rests -------------------------------------------------------------------
//
LONG_REST("Rest for 4 measures"),
BREVE_REST("Rest for 2 measures"),
QUARTER_REST("Rest for a 1/4"),
EIGHTH_REST("Rest for a 1/8"),
ONE_16TH_REST("Rest for a 1/16"),
ONE_32ND_REST("Rest for a 1/32"),
ONE_64TH_REST("Rest for a 1/64"),
ONE_128TH_REST("Rest for a 1/128"),
//
// Noteheads ---------------------------------------------------------------
//
NOTEHEAD_VOID("Hollow node head for halves", new Color(0xffcc00)),
NOTEHEAD_VOID_2("Pack of two hollow node heads for halves", new Color(0xffcc00)),
NOTEHEAD_VOID_3("Pack of three hollow node heads for halves", new Color(0xffcc00)),
NOTEHEAD_BLACK("Filled node head for quarters and less"),
NOTEHEAD_BLACK_2("Pack of two filled node heads for quarters and less"),
NOTEHEAD_BLACK_3("Pack of three filled node heads for quarters and less"),
//
// Notes -------------------------------------------------------------------
//
BREVE("Double Whole"),
WHOLE_NOTE("Hollow node head for wholes"),
WHOLE_NOTE_2("Pack of two hollow node heads for wholes"),
WHOLE_NOTE_3("Pack of three hollow node heads for wholes"),
//
// Beams and slurs ---------------------------------------------------------
//
BEAM("Beam between two stems"),
BEAM_2("Pack of 2 beams"),
BEAM_3("Pack of 3 beams"),
BEAM_HOOK("Hook of a beam attached on one stem"),
SLUR("Slur tying notes", new Color(0xbb8888)),
//
// Articulation ------------------------------------------------------------
//
ACCENT,
TENUTO,
STACCATISSIMO,
STRONG_ACCENT("Marcato"),
ARPEGGIATO,
//
// Dynamics ----------------------------------------------------------------
//
DYNAMICS_CHAR_M("m character"),
DYNAMICS_CHAR_R("r character"),
DYNAMICS_CHAR_S("c character"),
DYNAMICS_CHAR_Z("z character"),
DYNAMICS_F("Forte"),
DYNAMICS_FF("Fortissimo"),
DYNAMICS_FFF("Fortississimo"),
DYNAMICS_FP("FortePiano"),
DYNAMICS_FZ("Forzando"),
DYNAMICS_MF("Mezzo forte"),
DYNAMICS_MP("Mezzo piano"),
DYNAMICS_P("Piano"),
DYNAMICS_PP("Pianissimo"),
DYNAMICS_PPP("Pianississimo"),
DYNAMICS_RF,
DYNAMICS_RFZ("Rinforzando"),
DYNAMICS_SF,
DYNAMICS_SFFZ,
DYNAMICS_SFP("Subito fortepiano"),
DYNAMICS_SFPP,
DYNAMICS_SFZ("Sforzando"),
//
// Ornaments ---------------------------------------------------------------
//
GRACE_NOTE_SLASH("Grace Note with a Slash"),
GRACE_NOTE("Grace Note"),
TR("Trill"),
TURN_SLASH("Turn with a Slash"),
MORDENT("Mordent"),
INVERTED_MORDENT("Mordent with a Slash"),
//
// Tuplets -----------------------------------------------------------------
//
TUPLET_THREE("3", new Color(0xcc00cc)),
TUPLET_SIX("6", new Color(0xcc00cc)),
PEDAL_MARK("Pedal down", new Color(0x009999)),
PEDAL_UP_MARK("Pedal downup", new Color(0x009999)),
//
// Miscellaneous -----------------------------------------------------------
//
CLUTTER("Pure clutter", new Color(0x999900)),
CHARACTER("A letter"),
TEXT("Sequence of letters & spaces", new Color(0x9999ff)),
//
// =========================================================================
// This is the end of physical shapes.
// Next shapes are pure logical shapes, that CANNOT be inferred only from
// their physical characteristics.
// =========================================================================
//
//
// Shapes from shape sets --------------------------------------------------
//
REPEAT_DOT("Repeat dot", DOT_set, new Color(0x0000ff)),
AUGMENTATION_DOT("Augmentation Dot", DOT_set),
STACCATO("Staccato dot", DOT_set),
FERMATA("Fermata", FERMATA_set),
FERMATA_BELOW("Fermata Below", FERMATA_set),
WHOLE_REST("Rest for whole measure", HW_REST_set),
HALF_REST("Rest for a 1/2", HW_REST_set),
TIME_SIX("Digit 6", TIME_69_set),
TIME_NINE("Digit 9", TIME_69_set),
FLAG_1("Single flag down", FLAG_1_set),
FLAG_1_UP("Single flag up", FLAG_1_set),
FLAG_2("Double flag down", FLAG_2_set),
FLAG_2_UP("Double flag up", FLAG_2_set),
FLAG_3("Triple flag down", FLAG_3_set),
FLAG_3_UP("Triple flag up", FLAG_3_set),
FLAG_4("Quadruple flag down", FLAG_4_set),
FLAG_4_UP("Quadruple flag up", FLAG_4_set),
FLAG_5("Quintuple flag down", FLAG_5_set),
FLAG_5_UP("Quintuple flag up", FLAG_5_set),
CRESCENDO("Crescendo", WEDGE_set),
DECRESCENDO("Decrescendo", WEDGE_set),
TURN("Turn", TURN_set),
INVERTED_TURN("Inverted Turn", TURN_set),
TURN_UP("Turn Up", TURN_set),
//
// Bars --------------------------------------------------------------------
//
PART_DEFINING_BARLINE("Bar line that defines a part"),
THIN_BARLINE("Thin bar line"),
THICK_BARLINE("Thick bar line"),
DOUBLE_BARLINE("Double thin bar line"),
FINAL_BARLINE("Thin / Thick bar line"),
REVERSE_FINAL_BARLINE("Thick / Thin bar line"),
LEFT_REPEAT_SIGN("Thick / Thin bar line + Repeat dots"),
RIGHT_REPEAT_SIGN("Repeat dots + Thin / Thick bar line"),
BACK_TO_BACK_REPEAT_SIGN("Repeat dots + Thin / Thick / Thin + REPEAT_DOTS"),
ENDING("Alternate ending"),
//
// Miscellaneous
//
REPEAT_DOT_PAIR("Pair of repeat dots"),
NOISE("Too small stuff", new Color(0xcccccc)),
STAFF_LINE("Staff Line", new Color(0xffffcc)),
LEDGER("Ledger", new Color(0xaaaaaa)),
ENDING_HORIZONTAL("Horizontal part of ending"),
ENDING_VERTICAL("Vertical part of ending"),
//
// Stems
//
STEM("Stem", new Color(0xccff66)),
//
// Key signatures ----------------------------------------------------------
//
KEY_FLAT_1("One Flat"),
KEY_SHARP_1("One Sharp"),
//
// Other stuff -------------------------------------------------------------
//
FORWARD("To indicate a forward"),
NON_DRAGGABLE("Non draggable shape"),
GLYPH_PART("Part of a larger glyph"),
CUSTOM_TIME("Time signature defined by user"),
NO_LEGAL_TIME("No Legal Time Shape");
//
// =========================================================================
// This is the end of shape enumeration
// =========================================================================
//
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(Shape.class);
/** Last physical shape */
public static final Shape LAST_PHYSICAL_SHAPE = TEXT;
/** A comparator based on shape name */
public static Comparator<Shape> alphaComparator = new Comparator<Shape>()
{
@Override
public int compare (Shape o1,
Shape o2)
{
return o1.name()
.compareTo(o2.name());
}
};
//~ Instance fields --------------------------------------------------------
//
/** Explanation of the glyph shape */
private final String description;
/** Potential related symbol */
private ShapeSymbol symbol;
/** Potential related decorated symbol for menus */
private ShapeSymbol decoratedSymbol;
/** Remember the fact that this shape has no related symbol */
private boolean hasNoSymbol;
/** Remember the fact that this shape has no related decorated symbol */
private boolean hasNoDecoratedSymbol;
/** Potential related physical shape */
private Shape physicalShape;
/** Related color */
private Color color;
/** Related color constant */
private Constant.Color constantColor;
//--------------------------------------------------------------------------
//
//-------//
// Shape //
//-------//
Shape ()
{
this("", null, null);
}
//-------//
// Shape //
//-------//
Shape (String description)
{
this(description, null, null);
}
//-------//
// Shape //
//-------//
Shape (String description,
Color color)
{
this(description, null, color);
}
//-------//
// Shape //
//-------//
Shape (String description,
Shape physicalShape)
{
this(description, physicalShape, null);
}
//-------//
// Shape //
//-------//
Shape (String description,
Shape physicalShape,
Color color)
{
this.description = description;
this.physicalShape = physicalShape;
this.color = color;
// Create the underlying constant
constantColor = new Constant.Color(
getClass().getName(), // Unit
name() + ".color", // Name
Constant.Color.encodeColor(color != null ? color : Color.BLACK),
"Color for shape " + name());
}
//--------------------------------------------------------------------------
//---------------//
// isMeasureRest //
//---------------//
/**
* Check whether the shape is a whole (or multi) rest, for which
* no duration can be specified.
*
* @return true if whole or multi rest
*/
public boolean isMeasureRest ()
{
return (this == WHOLE_REST) || (this == BREVE_REST)
|| (this == LONG_REST);
}
//--------------//
// isPersistent //
//--------------//
/**
* Report whether the impact of this shape persists across system
* (actually measure) borders (clefs, time signatures, key signatures).
* Based on just the shape, we cannot tell whether an accidental is part of
* a key signature or not, so we take a conservative approach.
*
* @return true if persistent, false otherwise
*/
public boolean isPersistent ()
{
return ShapeSet.Clefs.contains(this) || ShapeSet.Times.contains(this)
|| ShapeSet.Accidentals.contains(this);
}
//--------//
// isText //
//--------//
/**
* Check whether the shape is a text (or a simple character).
*
* @return true if text or character
*/
public boolean isText ()
{
return (this == TEXT) || (this == CHARACTER);
}
//--------------//
// isSharpBased //
//--------------//
/**
* Check whether the shape is a sharp or a key-sig sequence of
* sharps.
*
* @return true if sharp or sharp key sig
*/
public boolean isSharpBased ()
{
return (this == SHARP) || ShapeSet.SharpKeys.contains(this);
}
//-------------//
// isFlatBased //
//-------------//
/**
* Check whether the shape is a flat or a key-sig sequence of
* flats.
*
* @return true if flat or flat key sig
*/
public boolean isFlatBased ()
{
return (this == FLAT) || ShapeSet.FlatKeys.contains(this);
}
//-------------//
// isTrainable //
//-------------//
/**
* Report whether this shape can be used to train an evaluator.
*
* @return true if trainable, false otherwise
*/
public boolean isTrainable ()
{
return ordinal() <= LAST_PHYSICAL_SHAPE.ordinal();
}
//-------------//
// isWellKnown //
//-------------//
/**
* Report whether this shape is well known, that is a non-garbage
* symbol.
*
* @return true if non-garbage, false otherwise
*/
public boolean isWellKnown ()
{
return (this != NO_LEGAL_TIME) && (this != GLYPH_PART)
&& (this != NOISE);
}
//----------------//
// getDescription //
//----------------//
/**
* Report a user-friendly description of this shape.
*
* @return the shape description
*/
public String getDescription ()
{
if (description == null) {
return toString(); // Could be improved
} else {
return description;
}
}
//----------//
// getColor //
//----------//
/**
* Report the color assigned to the shape, if any.
*
* @return the related color, or null
*/
public java.awt.Color getColor ()
{
return color;
}
//----------//
// setColor //
//----------//
/**
* Assign a color for this shape.
*
* @param color the display color
*/
public void setColor (java.awt.Color color)
{
this.color = color;
}
//------------------//
// setConstantColor //
//------------------//
/**
* Define a specific color for the shape.
*
* @param color the specified color
*/
public void setConstantColor (Color color)
{
constantColor.setValue(color);
setColor(color);
}
//------------------//
// createShapeColor //
//------------------//
void createShapeColor (Color color)
{
// Assign the shape display color
if (!constantColor.isSourceValue()) {
setColor(constantColor.getValue()); // Use the shape specific color
} else if (this.color == null) {
setColor(color); // Use the provided (range) default color
}
}
//-----------//
// getSymbol //
//-----------//
/**
* Report the symbol related to the shape, if any.
*
* @return the related symbol, or null
*/
public ShapeSymbol getSymbol ()
{
if (hasNoSymbol) {
return null;
}
if (symbol == null) {
symbol = Symbols.getSymbol(this);
if (symbol == null) {
hasNoSymbol = true;
}
}
return symbol;
}
//-----------//
// setSymbol //
//-----------//
/**
* Assign a symbol to this shape.
*
* @param symbol the assigned symbol, which may be null
*/
public void setSymbol (ShapeSymbol symbol)
{
this.symbol = symbol;
}
//--------------------//
// getDecoratedSymbol //
//--------------------//
/**
* Report the symbol to use for menu items.
*
* @return the shape symbol, with decorations if any
*/
public ShapeSymbol getDecoratedSymbol ()
{
// Avoid a new search, just use the undecorated symbol instead
if (hasNoDecoratedSymbol) {
return getSymbol();
}
// Try to build / load a decorated symbol
if (decoratedSymbol == null) {
setDecoratedSymbol(Symbols.getSymbol(this, true));
if (decoratedSymbol == null) {
hasNoDecoratedSymbol = true;
return getSymbol();
}
}
// Simply return the cached decorated symbol
return decoratedSymbol;
}
//--------------------//
// setDecoratedSymbol //
//--------------------//
/**
* Assign a decorated symbol to this shape.
*
* @param decoratedSymbol the assigned decorated symbol, which may be null
*/
public void setDecoratedSymbol (ShapeSymbol decoratedSymbol)
{
this.decoratedSymbol = decoratedSymbol;
}
//------------------//
// getPhysicalShape //
//------------------//
/**
* Report the shape to use for training or precise drawingw.
*
* @return the related physical shape, if different
*/
public Shape getPhysicalShape ()
{
if (physicalShape != null) {
return physicalShape;
} else {
return this;
}
}
//-------------//
// isDraggable //
//-------------//
/**
* Report whether this shape can be dragged (in a DnD gesture).
*
* @return true if draggable
*/
public boolean isDraggable ()
{
return getPhysicalShape()
.getSymbol() != null;
}
//-----------------//
// dumpShapeColors //
//-----------------//
/**
* Dump the color of every shape.
*/
public static void dumpShapeColors ()
{
List<String> names = new ArrayList<>();
for (Shape shape : Shape.values()) {
names.add(
shape + " " + Constant.Color.encodeColor(shape.getColor()));
}
Collections.sort(names);
for (String str : names) {
System.out.println(str);
}
}
}