use of com.xenoage.utils.math.VSide in project Zong by Xenoage.
the class ArticulationsNotator method compute.
/**
* Computes the notations of the articulations of the given chord.
* If there are no articulations, {@link ArticulationsNotation#empty} is returned.
*/
public ArticulationsNotation compute(Chord chord, StemDirection stemDirection, NotesNotation notesAlignment, int linesCount) {
// depending on the stem direction, place the articulation on the other side.
// if there is no stem direction, always place at the top
VSide side = (stemDirection == StemDirection.Up ? VSide.Bottom : VSide.Top);
// dependent on the side of the articulation, take the top or bottom note
NoteDisplacement outerNote = (side == VSide.Top ? notesAlignment.getTopNote() : notesAlignment.getBottomNote());
// compute alignment of articulations
return compute(chord.getArticulations(), outerNote, side, linesCount);
}
use of com.xenoage.utils.math.VSide in project Zong by Xenoage.
the class SlurReader method readToContext.
public static void readToContext(Chord chord, int noteIndex, int staffIndexInPart, Context context, MxlSlurOrTied mxlSlur) {
Pitch pitch = chord.getNotes().get(noteIndex).getPitch();
float noteLP = context.getMusicContext(staffIndexInPart).getLp(pitch);
// type
SlurType type = (mxlSlur.getElementType() == MxlElementType.Slur ? SlurType.Slur : SlurType.Tie);
// number (tied does usually not use a number, but is distinguished by pitch)
Integer number = mxlSlur.getNumber();
BezierPoint bezierPoint = readBezierPoint(mxlSlur.getPosition(), mxlSlur.getBezier(), context.getTenthMm(), context.getStaffLinesCount(staffIndexInPart), noteLP, chord.getDuration());
VSide side = readVSide(mxlSlur.getPlacement());
// waypoint
SlurWaypoint wp = new SlurWaypoint(chord, noteIndex, bezierPoint);
if (type == SlurType.Tie && number == null) {
// unnumbered tied
OpenUnnumberedTieds openTieds = context.getOpenElements().getOpenUnnumberedTies();
if (mxlSlur.getType() == MxlStartStopContinue.Start) {
openTieds.startTied(wp, side);
} else if (mxlSlur.getType() == MxlStartStopContinue.Stop) {
OpenSlur openTied = openTieds.stopTied(wp, side, context);
if (openTied != null)
context.createSlur(openTied);
}
} else {
// numbered
WaypointPosition wpPos;
if (mxlSlur.getType() == MxlStartStopContinue.Start)
wpPos = WaypointPosition.Start;
else if (mxlSlur.getType() == MxlStartStopContinue.Stop)
wpPos = WaypointPosition.Stop;
else
wpPos = WaypointPosition.Continue;
context.registerSlur(type, wpPos, number, wp, side);
}
}
use of com.xenoage.utils.math.VSide in project Zong by Xenoage.
the class StemReader method read.
/**
* Reads and returns the stem of the given chord.
* If not available, {@link Stem#defaultStem} is returned.
* @param context the global context
* @param chord the chord, whose notes are already collected
* @param staff the staff index of the current chord
*/
public Stem read(Context context, Chord chord, int staff) {
if (mxlStem == null)
return Companion.getDefaultStem();
// direction
StemDirection direction = readStemDirection();
// length
Float length = null;
MxlPosition yPos = mxlStem.getPosition();
if (yPos.getDefaultY() != null) {
// convert position in tenths relative to topmost staff line into
// a length in interline spaces measured from the outermost chord note on stem side
float stemEndLinePosition = convertDefaultYToLP(context, yPos.getDefaultY(), staff);
VSide side = (direction == StemDirection.Up ? VSide.Top : VSide.Bottom);
length = Math.abs(stemEndLinePosition - getOuterNoteLp(context, chord, side, staff)) / 2;
}
// create stem
return new Stem(direction, length);
}
use of com.xenoage.utils.math.VSide in project Zong by Xenoage.
the class QuadraticCurvesTools method computeOverConvexHull.
/**
* Computes a list of possible quadratic curves over the given {@link ConvexHull},
* that start at the first point of the hull and end at the last point of the hull,
* but may also start and end further away according to the given parameters. The curve
* will not cross the area of the convex hull.
* @param convexHull the convex hull
* @param leftArea the tolerance of the distance of the start point
* (between 0 and this value). always positive or 0.
* @param rightArea the tolerance of the distance of the end point
* (between 0 and this value). always positive or 0.
* @return a list of possible quadratic curves
*/
public static List<QuadraticCurve> computeOverConvexHull(ConvexHull convexHull, float leftArea, float rightArea) {
LinkedList<QuadraticCurve> ret = new LinkedList<>();
ArrayList<Point2f> points = convexHull.getPoints();
VSide side = convexHull.getSide();
int sideDir = side.getDir();
int n = points.size();
// compute the possible start and endpoints
Point2f[] startPoints = new Point2f[] { points.get(0), points.get(0).add(0, sideDir * leftArea) };
Point2f[] endPoints = new Point2f[] { points.get(n - 1), points.get(n - 1).add(0, sideDir * rightArea) };
// the quadratic expression {a, b, c} for ax² + bx + c = 0
// equations:
// - (2): must start at startPoints[0] or startPoints[1] (between is never optimal!)
// - (2): must end at endPoints[0] or endPoints[1] (between is never optimal!)
// inequations:
// - (0): a must be <=/>= 0 (parabola is open on the bottom/top side, dependent
// on the side of the convex hull) - not used in SLE, checked later
// - (m): curve must be above/below each of the m = n-2 middle points (dependent on the side)
int m = n - 2;
Point2f[] eq = new Point2f[2 + 2 + m];
eq[0] = startPoints[0];
eq[1] = startPoints[1];
eq[2] = endPoints[0];
eq[3] = endPoints[1];
for (int i = 0; i < m; i++) {
eq[4 + i] = points.get(1 + i);
}
// strategy, based on the simplex algorithm for linear optimization:
// for each possible combination of 3 equations (optimum is always at the corner of the simplex,
// so we can use the inequations like equations), solve the SLE, test, if the curve is
// valid for all inequations, and if so, compute the area between the curve and the convex hull.
// take the curve which has the smallest area.
// there are ((m+4) choose 3) possible SLEs, but we have to ignore those where eq[0] AND eq[1]
// are used and those where eq[2] AND eq[3] are used.
int[][] subsets = getAllCombinationsOf3(m + 4);
for (int[] eqIndices : subsets) {
// not useable: {0,1,?}, {2,3,?} and {?,2,3}
if (eqIndices[0] == 0 && eqIndices[1] == 1 || eqIndices[0] == 2 && eqIndices[1] == 3 || eqIndices[1] == 2 && eqIndices[2] == 3) {
// ignore
} else {
// usable. solve SLE
double[][] A = new double[3][3];
double[] b = new double[3];
for (int iy = 0; iy < 3; iy++) {
Point2f p = eq[eqIndices[iy]];
A[iy][0] = p.x * p.x;
A[iy][1] = p.x;
A[iy][2] = 1;
b[iy] = p.y;
}
double[] params = Gauss.solve(A, b);
// parameters ok for all equations?
boolean ok = true;
ok &= sideDir * getY(startPoints[0].x, params) >= sideDir * startPoints[0].y;
ok &= sideDir * getY(startPoints[1].x, params) <= sideDir * startPoints[1].y;
ok &= sideDir * getY(endPoints[0].x, params) >= sideDir * endPoints[0].y;
ok &= sideDir * getY(endPoints[1].x, params) <= sideDir * endPoints[1].y;
// parabole is open on the bottom/top side
ok &= sideDir * params[0] <= 0;
for (int im = 0; ok && im < m; im++) {
ok &= sideDir * getY(points.get(1 + im).x, params) >= sideDir * points.get(1 + im).y;
}
if (ok) {
// remember this equation
ret.add(new QuadraticCurve((float) params[0], (float) params[1], (float) params[2]));
}
}
}
if (ret.size() == 0) {
// no curve found. use direct line between first and last point.
double[][] A = new double[][] { { points.get(0).x, 1 }, { points.get(n - 1).x, 1 } };
double[] b = new double[] { points.get(0).y, points.get(n - 1).y };
double[] params = Gauss.solve(A, b);
ret.add(new QuadraticCurve(0f, (float) params[0], (float) params[1]));
}
// return result
return ret;
}
use of com.xenoage.utils.math.VSide in project Zong by Xenoage.
the class ChordStamper method stampAll.
/**
* Returns all the stampings for the given {@link Chord}, including beams,
* tuplets, slurs and other attachments.
*
* The given {@link OpenSlursCache},
* {@link OpenLyricsCache}, {@link LastLyrics} and {@link OpenTupletsCache} may be modified.
*/
public List<Stamping> stampAll(ChordNotation chord, float xMm, BeamSpacing beam, StaffStampings staffStampings, StamperContext context, FormattedTextStyle defaultLyricStyle, OpenSlursCache openSlursCache, OpenLyricsCache openLyricsCache, LastLyrics lastLyrics, OpenTupletsCache openTupletsCache) {
List<Stamping> ret = alist();
Chord element = chord.getElement();
int staffIndex = context.staffIndex;
int systemIndex = context.systemIndex;
// noteheads, leger lines, dots, accidentals, stem, flags, articulations
ChordStampings chordSt = stampCore(chord, xMm, context);
chordSt.addAllTo(ret);
// beam
if (beam != null) {
// stamp the whole beam (when we find the beginning of the beam)
// TIDY: create/store beam stampings elsewhere?
Beam beamElement = beam.notation.element;
int chordIndex = beamElement.getWaypointIndex(element);
if (chordIndex == 0) {
ret.addAll(beamStamper.stamp(beam, context.getCurrentStaffStamping()));
}
}
// ties and slurs
for (Slur slur : element.getSlurs()) {
SlurWaypoint wp = slur.getWaypoint(element);
WaypointPosition pos = slur.getWaypointPosition(element);
// TODO: choose top/bottom
int noteIndex = notNull(wp.getNoteIndex(), 0);
NoteheadStamping notehead = chordSt.noteheads[noteIndex];
// define the placement: above or below (TODO: better strategy)
VSide side = slurStamper.getSide(slur);
// compute position
val staff = staffStampings.get(systemIndex, notehead.parentStaff.staffIndex);
val slurCache = openSlursCache.getOrCreate(slur);
float distanceIs = slurStamper.getAdditionalDistanceIs(chord, slur.getSide());
SP defaultSp = sp(notehead.position.xMm, notehead.position.lp + side.getDir() * distanceIs * 2);
if (pos == WaypointPosition.Start)
slurCache.setStart(defaultSp, staff, systemIndex);
else
slurCache.setStop(defaultSp, staff, systemIndex);
}
// lyric
List<Lyric> lyrics = element.getLyrics();
if (lyrics.size() > 0) {
float baseLine = -10;
for (Lyric lyric : lyrics) {
if (lyric != null) {
SyllableType lyricType = lyric.getSyllableType();
StaffTextStamping lastLyric = lastLyrics.get(staffIndex, lyric.getVerse());
if (lyricType == SyllableType.Extend) {
// extend
if (// TODO: frame breaks...
lastLyric != null) {
// remember it
openLyricsCache.setUnderscore((Lyric) lastLyric.getElement(), lastLyric, chordSt.noteheads[0], /* TODO*/
staffIndex);
}
} else {
// normal lyric
// create text stamping
StaffTextStamping sts = lyricStamper.createSyllableStamping(lyric, defaultLyricStyle, context.getCurrentStaffStamping(), chordSt.noteheads[0].position.xMm, baseLine);
ret.add(sts);
// when middle or end syllable, add a hypen between the preceding syllable and this syllable
if (// TODO: frame breaks...
lastLyric != null) {
if (lyricType == SyllableType.Middle || lyricType == SyllableType.End) {
StaffTextStamping hyphenStamping = lyricStamper.createHyphenStamping(lastLyric, sts, defaultLyricStyle);
ret.add(hyphenStamping);
}
}
// remember this lyric as the currently last one in the current staff and verse
lastLyrics.set(staffIndex, lyric.getVerse(), sts);
}
}
baseLine += -5;
}
}
// directions
ret.addAll(directionStamper.stampForChord(chordSt, context.layouter.symbols));
// tuplet
Tuplet tuplet = element.getTuplet();
if (tuplet != null) {
openTupletsCache.addChord(element, tuplet, chordSt);
}
return ret;
}
Aggregations