finishLayout method

void finishLayout(
  1. ChantContext ctxt
)

Implementation

void finishLayout(ChantContext ctxt) {
  final theme = ctxt.theme;
  ledgerLines = [];

  final notations = score.notations;
  final lastIndex = notationsStartIndex + numNotationsOnLine;

  void processElementForLedgerLine(
    ChantLayoutElement element, {
    Note? endElem,
    int? staffPosition,
    double? offsetX,
  }) {
    final sp = staffPosition ?? (element as dynamic).staffPosition;
    final ox = offsetX ?? (element is Note ? element.neume!.bounds.x : 0);
    final ee = endElem ?? element;

    final ledgerLinePositionAbove = ctxt.staffLineCount * 2 + 1;
    if (sp >= ledgerLinePositionAbove || sp <= -1) {
      var x1 = ox + element.bounds.x - ctxt.intraNeumeSpacing;
      final x2 = ox + ee.bounds.x + ee.bounds.width + ctxt.intraNeumeSpacing;

      int roundedStaffPosition = sp;
      if (sp > 0) {
        roundedStaffPosition = sp - ((sp - 1) % 2);
      } else {
        roundedStaffPosition = sp - ((sp + 1) % 2);
      }

      final minLedgerSeparation =
          ctxt.staffInterval * theme.minLedgerSeparation;

      if (ledgerLines.isNotEmpty &&
          ledgerLines.last.x2 + minLedgerSeparation >= x1) {
        final half = (x1 - ledgerLines.last.x2) / 2;
        ledgerLines.last.x2 += half;
        x1 -= half;
      }

      double finalX2 = x2;
      if (finalX2 > staffRight) finalX2 = staffRight;

      ledgerLines.add(LedgerLinePos(x1, finalX2, roundedStaffPosition));
    }
  }

  List<HorizontalEpisema> episemata = [];
  BracePoint? startBrace;
  double minY = double.infinity;
  double maxY = double.negativeInfinity;

  void positionNonLyricText(
    TextElement text,
    ChantNotationElement neume, {
    double? rightX,
  }) {
    final initialX = text.bounds.x;
    text.setMaxWidth(ctxt, staffRight);
    text.bounds = text.bounds.copyWith(x: initialX);
    if (rightX != null) {
      text.bounds = text.bounds.copyWith(
        x: (text.bounds.x + rightX - text.bounds.width) / 2,
      );
    }
    final beyondStaffRight = neume.bounds.x + text.bounds.right - staffRight;
    if (beyondStaffRight > 0) {
      text.bounds = text.bounds.copyWith(x: text.bounds.x - beyondStaffRight);
    }
    if (neume.bounds.x + text.bounds.x < 0) {
      text.bounds = text.bounds.copyWith(x: -neume.bounds.x);
    }
  }

  int i; // TODO: clean up if(startBrace...) section
  for (i = notationsStartIndex; i < lastIndex; i++) {
    final neume = notations[i];

    minY = (minY < neume.bounds.y) ? minY : neume.bounds.y;
    maxY = (maxY > neume.bounds.bottom) ? maxY : neume.bounds.bottom;

    if (neume is Custos) {
      processElementForLedgerLine(neume);
      continue;
    }

    if (neume.alText.isNotEmpty) {
      for (int j = 0; j < neume.alText.length; j++) {
        positionNonLyricText(neume.alText[j], neume);
      }
    }

    if (neume.translationText.isNotEmpty) {
      for (int j = 0; j < neume.translationText.length; j++) {
        final text = neume.translationText[j];
        if (text.endNeume != null) {
          double rightX = (text.endNeume!.hasLyrics)
              ? text.endNeume!.bounds.x +
                    text.endNeume!.lyrics
                        .map((l) => l.bounds.right)
                        .reduce(math.max)
              : text.endNeume!.bounds.right;
          rightX -= neume.bounds.x;
          positionNonLyricText(text, neume, rightX: rightX);
        } else {
          positionNonLyricText(text, neume);
        }
      }
    }

    if (neume is! Neume) continue;

    for (int j = 0; j < neume.ledgerLines.length; j++) {
      final ll = neume.ledgerLines[j];
      processElementForLedgerLine(
        ll.element,
        endElem: ll.endElem,
        staffPosition: ll.staffPosition,
      );
    }

    for (int j = 0; j < neume.notes.length; j++) {
      final note = neume.notes[j];

      if (note.episemata.isEmpty) episemata = [];
      for (int k = 0; k < note.episemata.length; k++) {
        final episema = note.episemata[k];

        double spaceBetweenEpisemata = 0;

        if (episemata.isNotEmpty) {
          spaceBetweenEpisemata =
              neume.bounds.x +
              episema.bounds.x -
              (episemata.last.note.neume!.bounds.x +
                  episemata.last.bounds.right);
        }

        if (episemata.isEmpty ||
            episemata.last.positionHint != episema.positionHint ||
            episemata.last.terminating == true ||
            episemata.last.alignment == HorizontalEpisemaAlignment.left ||
            episemata.last.alignment == HorizontalEpisemaAlignment.center ||
            episema.alignment == HorizontalEpisemaAlignment.right ||
            episema.alignment == HorizontalEpisemaAlignment.center ||
            (spaceBetweenEpisemata > ctxt.intraNeumeSpacing * 2 &&
                note.glyphVisualizer!.glyphCode != GlyphCode.none)) {
          episemata = [episema];
        } else {
          double newY;

          if (episema.positionHint == MarkingPositionHint.below) {
            newY = (episema.bounds.y > episemata.last.bounds.y)
                ? episema.bounds.y
                : episemata.last.bounds.y;
          } else {
            newY = (episema.bounds.y < episemata.last.bounds.y)
                ? episema.bounds.y
                : episemata.last.bounds.y;
          }

          if (episema.bounds.y != newY) {
            episema.bounds = episema.bounds.copyWith(y: newY);
          } else {
            for (int l = 0; l < episemata.length; l++) {
              episemata[l].bounds = episemata[l].bounds.copyWith(y: newY);
            }
          }

          double newWidth =
              neume.bounds.x +
              episema.bounds.x -
              (episemata.last.note.neume!.bounds.x + episemata.last.bounds.x);
          if (newWidth < 0) {
            newWidth *= -1;
            episemata.last.bounds = episemata.last.bounds.copyWith(
              x: episemata.last.bounds.x - newWidth,
            );
          }
          episemata.last.bounds = episemata.last.bounds.copyWith(
            width: newWidth,
          );

          episemata.add(episema);
        }
      }

      if (note.braceEnd != null) handleEndBrace(ctxt, note, i);

      if (note.braceStart != null) {
        ctxt.lastStartBrace = startBrace = note.braceStart;
        startBrace!.notationIndex = i;
      }
    }
  }

  if (startBrace != null) {
    if (custos != null) {
      final nextNotation = notations[lastIndex];
      final nextNote = (nextNotation is Neume) ? nextNotation.notes[0] : null;
      final nextNotationButOne = (lastIndex + 1 < notations.length)
          ? notations[lastIndex + 1]
          : null;
      final nextNoteButOne = (nextNotationButOne is Neume)
          ? nextNotationButOne.notes[0]
          : null;
      final braceEnd =
          (nextNote?.braceEnd) ??
          (nextNotation is Accidental ? nextNoteButOne?.braceEnd : null);
      if (braceEnd != null) {
        custos!.braceEnd = braceEnd;
        handleEndBrace(ctxt, custos!, i);
      } else {
        custos!.braceEnd = BracePoint(
          custos!,
          startBrace.isAbove,
          startBrace.shape,
          BraceAttachment.right,
        );
        handleEndBrace(ctxt, custos!, i - 1);
        ctxt.lastStartBrace = BracePoint(
          null,
          startBrace.isAbove,
          startBrace.shape,
          BraceAttachment.left,
        );
        ctxt.lastStartBrace!.notationIndex = i;
      }
    }
  }

  if (custos != null) processElementForLedgerLine(custos!);
}