package com.xenoage.zong.musiclayout.stampings;
import static com.xenoage.utils.annotations.Optimized.Reason.Performance;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import com.xenoage.utils.annotations.Const;
import com.xenoage.utils.annotations.Optimized;
import com.xenoage.utils.math.geom.Rectangle2f;
import com.xenoage.utils.math.geom.Shape;
import com.xenoage.zong.core.music.format.SP;
import com.xenoage.zong.musiclayout.notation.ChordNotation;
import com.xenoage.zong.symbols.Symbol;
/**
* Class for a flags stamping.
*
* It consists of one ore more flags needed for unbeamed
* eighth, 16th, 32th notes and so on.
*
* @author Andreas Wenger
*/
@Const @RequiredArgsConstructor @Getter
public class FlagsStamping
extends Stamping {
/** The direction of a flag. This is usually the opposite stem direction. */
public enum FlagsDirection {
Up,
Down;
}
/** The parent chord. */
public final ChordNotation chord;
/** The parent staff. */
public final StaffStamping parentStaff;
/** The direction of this flag. This is usually the opposite stem direction. */
public final FlagsDirection flagsDirection;
/** The number of flags. */
public final int flagsCount;
/** The flag symbol. */
public final Symbol symbol;
/** The scaling of the flags. This is 1 for full chords and < 1 for grace/cue chords */
public final float scaling;
/** The position of the flag. The vertical coordinate is the LP where the flag starts.
* This should always be the end position of the stem. */
public final SP position;
@Optimized(Performance)
private Rectangle2f cachedBoundingShape = null;
@Override public Shape getBoundingShape() {
if (cachedBoundingShape != null)
return cachedBoundingShape;
//compute bounding shape
float flagsDistance = getFlagsDistance(flagsDirection, scaling);
float interlineSpace = parentStaff.is;
for (int i = 0; i < flagsCount; i++) {
Rectangle2f bounds = symbol.getBoundingRect().scale(scaling * interlineSpace);
if (flagsDirection == FlagsDirection.Up) {
bounds = bounds.move(0, -bounds.size.height);
}
bounds = bounds.move(0, -i * flagsDistance * interlineSpace);
if (cachedBoundingShape == null)
cachedBoundingShape = bounds;
else
cachedBoundingShape = cachedBoundingShape.extend(bounds);
}
cachedBoundingShape.move(position.xMm, parentStaff.computeYMm(position.lp));
return cachedBoundingShape;
}
/**
* Gets the distance between the flags in interline spaces.
*/
public static float getFlagsDistance(FlagsDirection flagsDirection, float scaling) {
return (flagsDirection == FlagsDirection.Down ? -1 : 1) * scaling;
}
@Override public StampingType getType() {
return StampingType.FlagsStamping;
}
@Override public Level getLevel() {
return Level.Music;
}
}