positionNotationElement method
bool
positionNotationElement(
- ChantContext ctxt,
- List<
Lyric> prevLyrics, - ChantNotationElement prev,
- ChantNotationElement curr,
- double rightNotationBoundary, [
- List<
CondensableSpace> ? condensableSpaces,
Implementation
bool positionNotationElement(
ChantContext ctxt,
List<Lyric> prevLyrics,
ChantNotationElement prev,
ChantNotationElement curr,
double rightNotationBoundary, [
List<CondensableSpace>? condensableSpaces,
]) {
final spaces = condensableSpaces ?? [];
if (spaces.sum == 0 && spaces.isEmpty) {
spaces.sum = 0;
}
int i;
final space = CondensableSpace(notation: curr);
bool fixedX = false;
if ((curr.hasNoWidth == false || curr.firstWithNoWidth == curr) &&
prev.firstWithNoWidth != null) {
curr.bounds = curr.bounds.copyWith(x: prev.firstWithNoWidth!.bounds.x);
fixedX = true;
} else {
curr.bounds = curr.bounds.copyWith(x: prev.bounds.right);
}
if ((curr is TextOnly && extraTextOnlyIndex == null) ||
(!curr.hasLyrics && prev.calculatedTrailingSpace < 0)) {
curr.calculatedTrailingSpace = prev.calculatedTrailingSpace;
if (curr.hasLyrics) {
curr.calculatedTrailingSpace -= curr.lyrics[0].bounds.width;
}
if (curr is TextOnly && curr.lyrics.length == 1) {
curr.lyrics[0].setMaxWidth(
ctxt,
staffRight,
staffRight -
LyricArray.getRight(prevLyrics) -
ctxt.minLyricWordSpacing,
);
}
} else if (!fixedX) {
curr.bounds = curr.bounds.copyWith(
x: curr.bounds.x + prev.calculatedTrailingSpace,
);
}
if (curr.hasLyrics &&
prev is! Divider &&
prev is! Accidental &&
numNotationsOnLine > 0 &&
(curr.lyrics[0].lyricType == LyricType.singleSyllable ||
curr.lyrics[0].lyricType == LyricType.beginningSyllable)) {
curr.bounds = curr.bounds.copyWith(
x: curr.bounds.x + ctxt.intraNeumeSpacing * ctxt.interVerbalMultiplier,
);
}
if (curr.hasNoWidth || fixedX) {
space.total = 0;
space.condensable = 0;
} else if (extraTextOnlyIndex != null && curr is TextOnly) {
curr.bounds = curr.bounds.copyWith(x: 0);
space.total = 0;
space.condensable = 0;
} else {
space.total = curr.bounds.x - prev.bounds.right;
space.condensable = space.total * ctxt.condensingTolerance;
}
if (prevLyrics.isEmpty) {
double maxRight = curr.bounds.right + curr.calculatedTrailingSpace;
for (i = 0; i < curr.lyrics.length; i++) {
final currLyric = curr.lyrics[i];
bool needsConnector =
currLyric.allowsConnector &&
currLyric.dropCap != null &&
currLyric.originalText.isNotEmpty &&
currLyric.text.isEmpty;
currLyric.setNeedsConnector(needsConnector);
final minLeft = staffLeft - paddingLeft;
if (currLyric.getLeft() < minLeft) {
curr.bounds = curr.bounds.copyWith(
x: curr.bounds.x - currLyric.getLeft() + minLeft,
);
}
space.condensable = math.min(
space.condensable,
currLyric.getLeft() - minLeft,
);
maxRight = math.max(maxRight, currLyric.getRight());
}
if (maxRight > rightNotationBoundary + spaces.sum + space.condensable) {
return false;
}
spaces.add(space);
spaces.sum += space.condensable;
return true;
} else {
if (curr.firstOfSyllable != null &&
prevLyrics.isNotEmpty &&
!curr.hasLyrics) {
curr.bounds = curr.bounds.copyWith(
x: math.max(curr.bounds.x, prevLyrics[0].getRight()),
);
space.total = curr.bounds.x - prev.bounds.right;
space.condensable = space.total * ctxt.condensingTolerance;
}
}
if (!curr.hasLyrics) {
if (curr.bounds.right + curr.calculatedTrailingSpace >
rightNotationBoundary + spaces.sum + space.condensable) {
return false;
}
spaces.add(space);
spaces.sum += space.condensable;
return true;
}
bool hasShifted;
bool atLeastOneWithoutConnector;
do {
hasShifted = false;
atLeastOneWithoutConnector = false;
for (i = 0; i < curr.lyrics.length; i++) {
if (curr.lyrics[i].originalText.isEmpty) continue;
double prevLyricRight = 0;
var condensableSpacesSincePrevLyric = <CondensableSpace>[];
double condensableSpaceSincePrevLyric = 0;
if (i < prevLyrics.length) {
prevLyricRight = prevLyrics[i].getRight();
final notationI = spaces
.map((s) => s.notation)
.toList()
.lastIndexOf(prevLyrics[i].notation!);
if (notationI >= 0) {
condensableSpacesSincePrevLyric = spaces.sublist(notationI + 1);
condensableSpaceSincePrevLyric = condensableSpacesSincePrevLyric
.fold(0.0, (sum, s) => sum + s.condensable);
} else {
condensableSpaceSincePrevLyric = 0;
}
}
curr.lyrics[i].setNeedsConnector(false);
final currLyricLeft = curr.lyrics[i].getLeft();
if (prevLyrics.length <= i || !prevLyrics[i].allowsConnector) {
final extraSpace =
currLyricLeft - prevLyricRight - ctxt.minLyricWordSpacing;
if (extraSpace < 0) {
final shift =
prevLyricRight + ctxt.minLyricWordSpacing - currLyricLeft;
curr.bounds = curr.bounds.copyWith(x: curr.bounds.x + shift);
condensableSpaceSincePrevLyric = 0;
hasShifted = shift > 0.5;
} else {
condensableSpaceSincePrevLyric = extraSpace;
}
} else {
bool overlap =
prevLyricRight + 2.0 >
currLyricLeft -
condensableSpaceSincePrevLyric -
space.condensable;
if (overlap) {
final shift = prevLyricRight + 2.0 - currLyricLeft;
if (shift < -0.1) {
final multiplier =
shift / (condensableSpaceSincePrevLyric + space.condensable);
double offset = 0;
for (final s in condensableSpacesSincePrevLyric) {
offset += multiplier * s.condensable;
s.notation.bounds = s.notation.bounds.copyWith(
x: s.notation.bounds.x + offset,
);
}
}
curr.bounds = curr.bounds.copyWith(x: curr.bounds.x + shift);
condensableSpaceSincePrevLyric = 0;
atLeastOneWithoutConnector = true;
hasShifted = shift > 0.5;
} else {
if (ctxt.minLyricWordSpacing < ctxt.hyphenWidth) {
final spaceBetweenSyls = currLyricLeft - prevLyricRight;
if (spaceBetweenSyls < ctxt.hyphenWidth) {
final minHyphenWidth = prevLyrics.length > 1
? ctxt.intraNeumeSpacing
: ctxt.minLyricWordSpacing;
prevLyrics[i].setConnectorWidth(
math.max(minHyphenWidth, spaceBetweenSyls),
);
}
}
prevLyrics[i].setNeedsConnector(true);
prevLyricRight = prevLyrics[i].getRight();
if (prevLyricRight + 0.1 > currLyricLeft) {
final shift = prevLyricRight - currLyricLeft;
curr.bounds = curr.bounds.copyWith(x: curr.bounds.x + shift);
condensableSpaceSincePrevLyric = 0;
hasShifted = shift > 0.5;
} else {
condensableSpaceSincePrevLyric = currLyricLeft - prevLyricRight;
}
}
}
if (condensableSpaceSincePrevLyric != 0) {
final previousCondensableSpaceSum = condensableSpacesSincePrevLyric
.fold(0.0, (sum, s) => sum + s.condensable);
if (condensableSpaceSincePrevLyric <
previousCondensableSpaceSum + space.condensable) {
final multiplier =
condensableSpaceSincePrevLyric /
(previousCondensableSpaceSum + space.condensable);
space.condensable *= multiplier;
if (condensableSpacesSincePrevLyric.isNotEmpty) {
for (final s in condensableSpacesSincePrevLyric) {
s.condensable *= multiplier;
}
spaces.sum = spaces
.map((s) => s.condensable)
.fold(0.0, (a, b) => a + b);
}
}
}
}
} while (curr.lyrics.length > 1 &&
hasShifted &&
atLeastOneWithoutConnector);
for (i = math.min(curr.lyrics.length, prevLyrics.length) - 1; i >= 0; i--) {
final pLyrics = prevLyrics[i];
if (pLyrics.needsConnector && pLyrics.connectorWidth != null) {
final currLyricLeft = curr.lyrics[i].getLeft();
final prevLyricRight = pLyrics.getRight() - pLyrics.connectorWidth!;
double spaceBetweenSyls = currLyricLeft - prevLyricRight;
if (spaceBetweenSyls >= ctxt.hyphenWidth) {
spaceBetweenSyls = 0;
}
pLyrics.setConnectorWidth(spaceBetweenSyls);
}
}
if (curr.bounds.right + curr.calculatedTrailingSpace <
rightNotationBoundary + spaces.sum + space.condensable &&
LyricArray.getRight(curr.lyrics, true) <=
staffRight + spaces.sum + space.condensable) {
if (prev is Accidental) {
final shift =
curr.bounds.x -
prev.bounds.width -
prev.calculatedTrailingSpace -
prev.bounds.x;
prev.bounds = prev.bounds.copyWith(x: prev.bounds.x + shift);
if (shift.abs() > 0.1) {
final lastCondensable = spaces[spaces.length - 1];
spaces.sum -= lastCondensable.condensable;
lastCondensable.condensable = 0;
}
}
spaces.add(space);
spaces.sum += space.condensable;
return true;
}
return false;
}