package com.xenoage.zong.musiclayout.spacer.beam;
import com.xenoage.utils.annotations.Const;
import lombok.ToString;
import static java.lang.Math.*;
/**
* Possible values for the slant of a beam.
*
* The slant is defined as the directed vertical distance between the outer LP
* of the left stem and the outer LP of the right stem.
*
* The slant may be fixed to one value or may be within an accepted range.
* This allows corrections of the slant to apply to artistic ideals, like
* avoiding white wedges within the staff (see Ross, p. 98, and Chlapik, p. 41).
*
* Since the slant is always measured in quarter spaces, to avoid rounding errors we
* use an integer value for the number of quater spaces (QS).
*
* @author Andreas Wenger
*/
@Const @ToString
public final class Slant {
public static final Slant horizontalSlant = new Slant(0, 0, Direction.Horizontal);
/** The smallest ("flattest") possible absolute slant in QS, e.g. 2 for one half-space up or down. */
public final int minAbsQs;
/** The biggest ("steepest") possible absolute slant in QS, e.g. 0 for a horizontal beam. */
public final int maxAbsQs;
/** The direction of the slant. */
public final Direction direction;
public static final Slant slant(float slantIs) {
int slantAbsQs = abs(round(slantIs * 4));
if (slantAbsQs == 0)
return horizontalSlant;
else
return new Slant(slantAbsQs, slantAbsQs, slantIs > 0 ? Direction.Ascending : Direction.Descending);
}
public static final Slant slantIs(float absIs, Direction direction) {
return slantIs(absIs, absIs, direction);
}
public static final Slant slantIs(float minAbsIs, float maxAbsIs, Direction direction) {
int minAbsQs = round(minAbsIs * 4);
int maxAbsQs = round(maxAbsIs * 4);
return slantQs(minAbsQs, maxAbsQs, direction);
}
public static final Slant slantQs(int absQs, Direction direction) {
return new Slant(absQs, absQs, direction);
}
public static final Slant slantQs(int minAbsQs, int maxAbsQs, Direction direction) {
return new Slant(minAbsQs, maxAbsQs, direction);
}
private Slant(int minAbsQs, int maxAbsQs, Direction direction) {
if (minAbsQs < 0 || maxAbsQs < minAbsQs)
throw new IllegalArgumentException("Minimum slant must be > 0 (was " + minAbsQs + " qs) " +
"and maximum slant (was " + minAbsQs + " qs) must be > minimum slant");
this.minAbsQs = minAbsQs;
this.maxAbsQs = maxAbsQs;
this.direction = direction;
}
/**
* Returns true, iff the given slant in QS is within the range of this slant.
*/
public boolean contains(int slantQs) {
if (direction == Direction.Descending)
return (-maxAbsQs <= slantQs && slantQs <= -minAbsQs);
else
return (minAbsQs <= slantQs && slantQs <= maxAbsQs);
}
/**
* Limits the values to the given absolute value in quarter spaces.
*/
public Slant limitQs(int absQs) {
return new Slant(min(absQs, minAbsQs), min(absQs, maxAbsQs), direction);
}
/**
* Gets the directed slant with the minimum absolute value in IS.
*/
public float getFlattestIs() {
return minAbsQs * direction.getSign() / 4f;
}
/**
* Gets the minimum directed slant value.
*/
public float getMinIs() {
return (direction == Direction.Ascending ? minAbsQs : maxAbsQs) * direction.getSign() / 4f;
}
/**
* Gets the maximum directed slant value.
*/
public float getMaxIs() {
return (direction == Direction.Ascending ? maxAbsQs : minAbsQs) * direction.getSign() / 4f;
}
}