/** * Class <code>Dynamics</code> represents a dynamics event * * @author Hervé Bitteur */ public class Dynamics extends MeasureElement implements Direction, Notation { // ~ Static fields/initializers --------------------------------------------- /** Usual logger utility */ private static final Logger logger = Logger.getLogger(Dynamics.class); /** Specific application parameters */ private static final Constants constants = new Constants(); /** Map Shape -> Signature */ private static final Map<Shape, String> sigs = new HashMap<Shape, String>(); static { // Additional characters : m, r, s & z sigs.put(Shape.DYNAMICS_CHAR_M, "m"); sigs.put(Shape.DYNAMICS_CHAR_R, "r"); sigs.put(Shape.DYNAMICS_CHAR_S, "s"); sigs.put(Shape.DYNAMICS_CHAR_Z, "z"); // // True dynamics symbols sigs.put(Shape.DYNAMICS_F, "f"); sigs.put(Shape.DYNAMICS_FF, "ff"); sigs.put(Shape.DYNAMICS_FFF, "fff"); sigs.put(Shape.DYNAMICS_FFFF, "ffff"); sigs.put(Shape.DYNAMICS_FFFFF, "fffff"); sigs.put(Shape.DYNAMICS_FFFFFF, "ffffff"); sigs.put(Shape.DYNAMICS_FP, "fp"); sigs.put(Shape.DYNAMICS_FZ, "fz"); sigs.put(Shape.DYNAMICS_MF, "mf"); sigs.put(Shape.DYNAMICS_MP, "mp"); sigs.put(Shape.DYNAMICS_P, "p"); sigs.put(Shape.DYNAMICS_PP, "pp"); sigs.put(Shape.DYNAMICS_PPP, "ppp"); sigs.put(Shape.DYNAMICS_PPPP, "pppp"); sigs.put(Shape.DYNAMICS_PPPPP, "ppppp"); sigs.put(Shape.DYNAMICS_PPPPPP, "pppppp"); sigs.put(Shape.DYNAMICS_RF, "rf"); sigs.put(Shape.DYNAMICS_RFZ, "rfz"); sigs.put(Shape.DYNAMICS_SF, "sf"); sigs.put(Shape.DYNAMICS_SFFZ, "sffz"); sigs.put(Shape.DYNAMICS_SFP, "sfp"); sigs.put(Shape.DYNAMICS_SFPP, "sfpp"); sigs.put(Shape.DYNAMICS_SFZ, "sfz"); } /* Map Signature -> Shape */ private static final Map<String, Shape> shapes = new HashMap<String, Shape>(); static { shapes.put("f", Shape.DYNAMICS_F); shapes.put("ff", Shape.DYNAMICS_FF); shapes.put("fff", Shape.DYNAMICS_FFF); shapes.put("ffff", Shape.DYNAMICS_FFFF); shapes.put("fffff", Shape.DYNAMICS_FFFFF); shapes.put("ffffff", Shape.DYNAMICS_FFFFFF); shapes.put("fp", Shape.DYNAMICS_FP); shapes.put("fz", Shape.DYNAMICS_FZ); shapes.put("mf", Shape.DYNAMICS_MF); shapes.put("mp", Shape.DYNAMICS_MP); shapes.put("p", Shape.DYNAMICS_P); shapes.put("pp", Shape.DYNAMICS_PP); shapes.put("ppp", Shape.DYNAMICS_PPP); shapes.put("pppp", Shape.DYNAMICS_PPPP); shapes.put("ppppp", Shape.DYNAMICS_PPPPP); shapes.put("pppppp", Shape.DYNAMICS_PPPPPP); shapes.put("rf", Shape.DYNAMICS_RF); shapes.put("rfz", Shape.DYNAMICS_RFZ); shapes.put("sf", Shape.DYNAMICS_SF); shapes.put("sffz", Shape.DYNAMICS_SFFZ); shapes.put("sfp", Shape.DYNAMICS_SFP); shapes.put("sfpp", Shape.DYNAMICS_SFPP); shapes.put("sfz", Shape.DYNAMICS_SFZ); } // ~ Constructors ----------------------------------------------------------- // ----------// // Dynamics // // ----------// /** * Creates a new instance of Dynamics event * * @param measure measure that contains this mark * @param point location of mark * @param chord the chord related to the mark * @param glyph the underlying glyph */ public Dynamics(Measure measure, SystemPoint point, Chord chord, Glyph glyph) { super(measure, true, point, chord, glyph); if (chord != null) { chord.addDirection(this); // //// TODO: Not always !!!!!!!!!!!!!!!!!!! } } // ~ Methods ---------------------------------------------------------------- // --------// // accept // // --------// @Override public boolean accept(ScoreVisitor visitor) { return visitor.visit(this); } // ----------// // populate // // ----------// /** * Used by SystemTranslator to allocate the dynamics marks * * @param glyph underlying glyph * @param measure measure where the mark is located * @param point location for the mark */ public static void populate(Glyph glyph, Measure measure, SystemPoint point) { // Can we gather with another dynamics letter? (e.g. m + p -> mp) for (TreeNode node : measure.getChildren()) { if (node instanceof Dynamics) { Dynamics d = (Dynamics) node; if (d.isCompatibleWith(point)) { d.addGlyph(glyph); glyph.setTranslation(d); return; } } } // Otherwise, create a brand new instance glyph.setTranslation(new Dynamics(measure, point, findChord(measure, point), glyph)); } // ---------------// // computeCenter // // ---------------// @Override protected void computeCenter() { setCenter(computeGlyphsCenter(getGlyphs())); } // --------------// // computeShape // // --------------// @Override protected Shape computeShape() { StringBuilder sig = new StringBuilder(); for (Glyph glyph : getGlyphs()) { sig.append(sigs.get(glyph.getShape())); } Shape shape = shapes.get(sig.toString()); if (shape == null) { addError("Invalid dynamics signature:" + sig); } return shape; } // ------------------// // isCompatibleWith // // ------------------// private boolean isCompatibleWith(SystemPoint point) { // Check x-proximity and y-alignment Scale scale = getSystem().getScale(); int dx = scale.toUnits(constants.maxDx); int dy = scale.toUnits(constants.maxDy); // Horizontal distance int xDist = Math.min(Math.abs(getBox().x - point.x), Math.abs((getBox().x + getBox().width) - point.x)); // Vertical distance int yDist = Math.abs(getReferencePoint().y - point.y); return (xDist <= dx) && (yDist <= dy); } // ~ Inner Classes ---------------------------------------------------------- // -----------// // Constants // // -----------// private static final class Constants extends ConstantSet { // ~ Instance fields ---------------------------------------------------- /** Maximum abscissa difference */ Scale.Fraction maxDx = new Scale.Fraction(1.5, "Maximum abscissa difference"); /** Maximum ordinate difference */ Scale.Fraction maxDy = new Scale.Fraction(0.5, "Maximum ordinate difference"); } }
/** * Class {@code BasicAlignment} implements a basic handling of Alignment facet * * @author Hervé Bitteur */ class BasicAlignment extends BasicFacet implements GlyphAlignment { // ~ Static fields/initializers --------------------------------------------- /** Usual logger utility */ private static final Logger logger = Logger.getLogger(BasicAlignment.class); // ~ Instance fields -------------------------------------------------------- /** Best line equation */ private Line line; // ~ Constructors ----------------------------------------------------------- /** * Create a new BasicAlignment object * * @param glyph our glyph */ public BasicAlignment(Glyph glyph) { super(glyph); } // ~ Methods ---------------------------------------------------------------- // ------------------// // getAlienPixelsIn // // ------------------// public int getAlienPixelsIn(Rectangle area) { int count = 0; final int posMin = area.y; final int posMax = (area.y + area.height) - 1; final List<GlyphSection> neighbors = glyph.getLag().getSectionsIn(area); for (GlyphSection section : neighbors) { // Keep only non-patch sections that are not part of the stick if (!section.isPatch() && (section.getGlyph() != glyph)) { int pos = section.getFirstPos() - 1; // Ordinate for horizontal, // Abscissa for vertical for (Run run : section.getRuns()) { pos++; if (pos > posMax) { break; } if (pos < posMin) { continue; } int coordMin = Math.max(area.x, run.getStart()); int coordMax = Math.min((area.x + area.width) - 1, run.getStop()); if (coordMax >= coordMin) { count += (coordMax - coordMin + 1); } } } } if (logger.isFineEnabled()) { logger.fine("Stick" + glyph.getId() + " " + area + " getAlienPixelsIn=" + count); } return count; } // ------------------// // getAliensAtStart // // ------------------// public int getAliensAtStart(int dCoord, int dPos) { return getAlienPixelsIn(new Rectangle(getStart(), getStartingPos() - dPos, dCoord, 2 * dPos)); } // -----------------------// // getAliensAtStartFirst // // -----------------------// public int getAliensAtStartFirst(int dCoord, int dPos) { return getAlienPixelsIn(new Rectangle(getStart(), getStartingPos() - dPos, dCoord, dPos)); } // ----------------------// // getAliensAtStartLast // // ----------------------// public int getAliensAtStartLast(int dCoord, int dPos) { return getAlienPixelsIn(new Rectangle(getStart(), getStartingPos(), dCoord, dPos)); } // -----------------// // getAliensAtStop // // -----------------// public int getAliensAtStop(int dCoord, int dPos) { return getAlienPixelsIn( new Rectangle(getStop() - dCoord, getStoppingPos() - dPos, dCoord, 2 * dPos)); } // ----------------------// // getAliensAtStopFirst // // ----------------------// public int getAliensAtStopFirst(int dCoord, int dPos) { return getAlienPixelsIn( new Rectangle(getStop() - dCoord, getStoppingPos() - dPos, dCoord, dPos)); } // ---------------------// // getAliensAtStopLast // // ---------------------// public int getAliensAtStopLast(int dCoord, int dPos) { return getAlienPixelsIn(new Rectangle(getStop() - dCoord, getStoppingPos(), dCoord, dPos)); } // -----------// // getAspect // // -----------// public double getAspect() { return (double) getLength() / (double) getThickness(); } // ---------------// // isExtensionOf // // ---------------// public boolean isExtensionOf( Stick other, int maxDeltaCoord, int maxDeltaPos, double maxDeltaSlope) { // Check that a pair of start/stop is compatible if ((Math.abs(other.getStart() - getStop()) <= maxDeltaCoord) || (Math.abs(other.getStop() - getStart()) <= maxDeltaCoord)) { // Check that a pair of positions is compatible if ((Math.abs(other.getLine().yAt(other.getStart()) - getLine().yAt(other.getStop())) <= maxDeltaPos) || (Math.abs(other.getLine().yAt(other.getStop()) - getLine().yAt(other.getStart())) <= maxDeltaPos)) { // Check that slopes are compatible (a useless test ?) if (Math.abs(other.getLine().getSlope() - getLine().getSlope()) <= maxDeltaSlope) { return true; } else if (logger.isFineEnabled()) { logger.fine("isExtensionOf: Incompatible slopes"); } } else if (logger.isFineEnabled()) { logger.fine("isExtensionOf: Incompatible positions"); } } else if (logger.isFineEnabled()) { logger.fine("isExtensionOf: Incompatible coordinates"); } return false; } // -------------// // getFirstPos // // -------------// public int getFirstPos() { return glyph.getBounds().y; } // ---------------// // getFirstStuck // // ---------------// public int getFirstStuck() { int stuck = 0; for (GlyphSection section : glyph.getMembers()) { Run sectionRun = section.getFirstRun(); for (GlyphSection sct : section.getSources()) { if (!sct.isGlyphMember() || (sct.getGlyph() != glyph)) { stuck += sectionRun.getCommonLength(sct.getLastRun()); } } } return stuck; } // ------------// // getLastPos // // ------------// public int getLastPos() { return (getFirstPos() + getThickness()) - 1; } // --------------// // getLastStuck // // --------------// public int getLastStuck() { int stuck = 0; for (GlyphSection section : glyph.getMembers()) { Run sectionRun = section.getLastRun(); for (GlyphSection sct : section.getTargets()) { if (!sct.isGlyphMember() || (sct.getGlyph() != glyph)) { stuck += sectionRun.getCommonLength(sct.getFirstRun()); } } } return stuck; } // -----------// // getLength // // -----------// public int getLength() { return glyph.getBounds().width; } // ---------// // getLine // // ---------// public Line getLine() { if (line == null) { computeLine(); } return line; } // -----------// // getMidPos // // -----------// public int getMidPos() { if (getLine().isVertical()) { // Fall back value return (int) Math.rint((getFirstPos() + getLastPos()) / 2.0); } else { return (int) Math.rint(getLine().yAt((getStart() + getStop()) / 2.0)); } } // ----------// // getStart // // ----------// public int getStart() { return glyph.getBounds().x; } // ---------------// // getStartPoint // // ---------------// public PixelPoint getStartPoint() { Point start = glyph.getLag().switchRef(new Point(getStart(), line.yAt(getStart())), null); return new PixelPoint(start.x, start.y); } // ----------------// // getStartingPos // // ----------------// public int getStartingPos() { if ((getThickness() >= 2) && !getLine().isVertical()) { return getLine().yAt(getStart()); } else { return getFirstPos() + (getThickness() / 2); } } // ---------// // getStop // // ---------// public int getStop() { return (getStart() + getLength()) - 1; } // --------------// // getStopPoint // // --------------// public PixelPoint getStopPoint() { Point stop = glyph.getLag().switchRef(new Point(getStop(), line.yAt(getStop())), null); return new PixelPoint(stop.x, stop.y); } // ----------------// // getStoppingPos // // ----------------// public int getStoppingPos() { if ((getThickness() >= 2) && !getLine().isVertical()) { return getLine().yAt(getStop()); } else { return getFirstPos() + (getThickness() / 2); } } // --------------// // getThickness // // --------------// public int getThickness() { return glyph.getBounds().height; } // -------------// // computeLine // // -------------// public void computeLine() { line = new BasicLine(); for (GlyphSection section : glyph.getMembers()) { StickSection ss = (StickSection) section; line.includeLine(ss.getLine()); } if (logger.isFineEnabled()) { logger.fine( line + " pointNb=" + line.getNumberOfPoints() + " meanDistance=" + (float) line.getMeanDistance()); } } // ------// // dump // // ------// /** Print out glyph internal data */ @Override public void dump() { super.dump(); System.out.println(" line=" + getLine()); } // --------------// // overlapsWith // // --------------// public boolean overlapsWith(Stick other) { return Math.max(getStart(), other.getStart()) < Math.min(getStop(), other.getStop()); } // ------------// // renderLine // // ------------// public void renderLine(Graphics g) { if (glyph.getContourBox().intersects(g.getClipBounds())) { getLine(); // To make sure the line has been computed Point start = glyph .getLag() .switchRef( new Point(getStart(), (int) Math.rint(line.yAt((double) getStart()))), null); Point stop = glyph .getLag() .switchRef( new Point(getStop() + 1, (int) Math.rint(line.yAt((double) getStop() + 1))), null); g.drawLine(start.x, start.y, stop.x, stop.y); } } }
/** * Class <code>StemInspector</code> is a GlyphInspector dedicated to the inspection of Stems at * System level * * @author Hervé Bitteur */ public class StemInspector { // ~ Static fields/initializers --------------------------------------------- /** Usual logger utility */ private static final Logger logger = Logger.getLogger(StemInspector.class); /** Predicate to filter only reliable symbols attached to a stem */ private static final Predicate<Glyph> reliableStemSymbols = new Predicate<Glyph>() { public boolean check(Glyph glyph) { Shape shape = glyph.getShape(); boolean res = glyph.isWellKnown() && ShapeRange.StemSymbols.contains(shape) && (shape != Shape.BEAM_HOOK); return res; } }; // ~ Instance fields -------------------------------------------------------- /** Dedicated system */ private final SystemInfo system; // ~ Constructors ----------------------------------------------------------- /** * Creates a new StemInspector object. * * @param system the dedicated system */ public StemInspector(SystemInfo system) { this.system = system; } // ~ Methods ---------------------------------------------------------------- // ----------------// // runStemPattern // // ----------------// /** * In a specified system, look for all stems that should not be kept, rebuild surrounding glyphs * and try to recognize them. If this action does not lead to some recognized symbol, then we * restore the stems. * * @return the number of symbols recognized */ public int runStemPattern() { int nb = 0; // Collect all undue stems List<Glyph> SuspectedStems = new ArrayList<Glyph>(); for (Glyph glyph : system.getGlyphs()) { if (glyph.isStem() && glyph.isActive()) { Set<Glyph> goods = new HashSet<Glyph>(); Set<Glyph> bads = new HashSet<Glyph>(); glyph.getSymbolsBefore(reliableStemSymbols, goods, bads); glyph.getSymbolsAfter(reliableStemSymbols, goods, bads); if (goods.isEmpty()) { if (logger.isFineEnabled()) { logger.finest("Suspected Stem " + glyph); } SuspectedStems.add(glyph); // Discard "bad" ones for (Glyph g : bads) { if (logger.isFineEnabled()) { logger.finest("Deassigning bad glyph " + g); } g.setShape((Shape) null); } } } } // Remove these stem glyphs since nearby stems are used for recognition for (Glyph glyph : SuspectedStems) { system.removeGlyph(glyph); } // Extract brand new glyphs (removeInactiveGlyphs + retrieveGlyphs) system.extractNewGlyphs(); // Try to recognize each glyph in turn List<Glyph> symbols = new ArrayList<Glyph>(); final GlyphEvaluator evaluator = GlyphNetwork.getInstance(); final double maxDoubt = GlyphInspector.getPatternsMaxDoubt(); for (Glyph glyph : system.getGlyphs()) { if (glyph.getShape() == null) { Evaluation vote = evaluator.vote(glyph, maxDoubt); if (vote != null) { glyph.setShape(vote.shape, vote.doubt); if (glyph.isWellKnown()) { if (logger.isFineEnabled()) { logger.finest("New symbol " + glyph); } symbols.add(glyph); nb++; } } } } // Keep stems that have not been replaced by symbols, definitively // remove the others for (Glyph stem : SuspectedStems) { // Check if one of its section is now part of a symbol boolean known = false; Glyph glyph = null; for (GlyphSection section : stem.getMembers()) { glyph = section.getGlyph(); if ((glyph != null) && glyph.isWellKnown()) { known = true; break; } } if (!known) { // Remove the newly created glyph if (glyph != null) { system.removeGlyph(glyph); } // Restore the stem system.addGlyph(stem); } } return nb; } }