浏览代码

fix(lyricsSpacing/dashes): reduce lyrics spacing and number of dashes (#316)

change dashes calculation so dashes are also equidistant to lyrics (and each other)
(fix error where distanceBetweenDashes was inversely correlated to minimumDistanceBetweenDashes)

add engraving rules for distance between lyrics (and syllables)
give lyrics more spacing when dashes follow
add lyrics extend drawing method to MusicSheetDrawer (not implemented yet)
LyricsEntry: new attribute: syllableIndex (i-1th syllable in LyricWord)

fix #314, fix #304

includes some staggered changes from PR #313.
sschmidTU 6 年之前
父节点
当前提交
7565bb0151

+ 17 - 1
src/MusicalScore/Graphical/EngravingRules.ts

@@ -90,7 +90,9 @@ export class EngravingRules {
     private lyricsHeight: number;
     private lyricsHeight: number;
     private lyricsYOffsetToStaffHeight: number;
     private lyricsYOffsetToStaffHeight: number;
     private verticalBetweenLyricsDistance: number;
     private verticalBetweenLyricsDistance: number;
+    private horizontalBetweenLyricsDistance: number;
     private betweenSyllabelMaximumDistance: number;
     private betweenSyllabelMaximumDistance: number;
+    private betweenSyllabelMinimumDistance: number;
     private minimumDistanceBetweenDashes: number;
     private minimumDistanceBetweenDashes: number;
     private bezierCurveStepSize: number;
     private bezierCurveStepSize: number;
     private tPower3: number[];
     private tPower3: number[];
@@ -272,8 +274,10 @@ export class EngravingRules {
         this.lyricsHeight = 2.0; // actually size of lyrics
         this.lyricsHeight = 2.0; // actually size of lyrics
         this.lyricsYOffsetToStaffHeight = 3.0; // distance between lyrics and staff. could partly be even lower/dynamic
         this.lyricsYOffsetToStaffHeight = 3.0; // distance between lyrics and staff. could partly be even lower/dynamic
         this.verticalBetweenLyricsDistance = 0.5;
         this.verticalBetweenLyricsDistance = 0.5;
+        this.horizontalBetweenLyricsDistance = 0.3;
         this.betweenSyllabelMaximumDistance = 10.0;
         this.betweenSyllabelMaximumDistance = 10.0;
-        this.minimumDistanceBetweenDashes = 5.0;
+        this.betweenSyllabelMinimumDistance = 0.4;
+        this.minimumDistanceBetweenDashes = 10;
 
 
         // expressions variables
         // expressions variables
         this.instantaniousTempoTextHeight = 2.3;
         this.instantaniousTempoTextHeight = 2.3;
@@ -825,12 +829,24 @@ export class EngravingRules {
     public set VerticalBetweenLyricsDistance(value: number) {
     public set VerticalBetweenLyricsDistance(value: number) {
         this.verticalBetweenLyricsDistance = value;
         this.verticalBetweenLyricsDistance = value;
     }
     }
+    public get HorizontalBetweenLyricsDistance(): number {
+        return this.horizontalBetweenLyricsDistance;
+    }
+    public set HorizontalBetweenLyricsDistance(value: number) {
+        this.horizontalBetweenLyricsDistance = value;
+    }
     public get BetweenSyllabelMaximumDistance(): number {
     public get BetweenSyllabelMaximumDistance(): number {
         return this.betweenSyllabelMaximumDistance;
         return this.betweenSyllabelMaximumDistance;
     }
     }
     public set BetweenSyllabelMaximumDistance(value: number) {
     public set BetweenSyllabelMaximumDistance(value: number) {
         this.betweenSyllabelMaximumDistance = value;
         this.betweenSyllabelMaximumDistance = value;
     }
     }
+    public get BetweenSyllabelMinimumDistance(): number {
+        return this.betweenSyllabelMinimumDistance;
+    }
+    public set BetweenSyllabelMinimumDistance(value: number) {
+        this.betweenSyllabelMinimumDistance = value;
+    }
     public get MinimumDistanceBetweenDashes(): number {
     public get MinimumDistanceBetweenDashes(): number {
         return this.minimumDistanceBetweenDashes;
         return this.minimumDistanceBetweenDashes;
     }
     }

+ 11 - 9
src/MusicalScore/Graphical/MusicSheetCalculator.ts

@@ -1744,8 +1744,9 @@ export abstract class MusicSheetCalculator {
                 nextLyricEntry.GraphicalLabel.PositionAndShape.BorderMarginLeft;
                 nextLyricEntry.GraphicalLabel.PositionAndShape.BorderMarginLeft;
             const y: number = lyricEntry.GraphicalLabel.PositionAndShape.RelativePosition.y;
             const y: number = lyricEntry.GraphicalLabel.PositionAndShape.RelativePosition.y;
             let numberOfDashes: number = 1;
             let numberOfDashes: number = 1;
-            if ((endX - startX) > this.rules.BetweenSyllabelMaximumDistance) {
-                numberOfDashes = Math.ceil((endX - startX) / this.rules.BetweenSyllabelMaximumDistance);
+            if ((endX - startX) > this.rules.MinimumDistanceBetweenDashes * 3) {
+                // *3: need distance between word to first dash, dash to dash, dash to next word
+                numberOfDashes = Math.floor((endX - startX) / this.rules.MinimumDistanceBetweenDashes) - 1;
             }
             }
             // check distance and create the adequate number of Dashes
             // check distance and create the adequate number of Dashes
             if (numberOfDashes === 1) {
             if (numberOfDashes === 1) {
@@ -1792,16 +1793,16 @@ export abstract class MusicSheetCalculator {
      */
      */
     private calculateDashes(staffLine: StaffLine, startX: number, endX: number, y: number): void {
     private calculateDashes(staffLine: StaffLine, startX: number, endX: number, y: number): void {
         let distance: number = endX - startX;
         let distance: number = endX - startX;
-        if (distance < this.rules.MinimumDistanceBetweenDashes) {
+        if (distance < this.rules.MinimumDistanceBetweenDashes * 3) {
             this.calculateSingleDashForLyricWord(staffLine, startX, endX, y);
             this.calculateSingleDashForLyricWord(staffLine, startX, endX, y);
         } else {
         } else {
             // enough distance for more Dashes
             // enough distance for more Dashes
-            const numberOfDashes: number = Math.floor(distance / this.rules.MinimumDistanceBetweenDashes);
-            const distanceBetweenDashes: number = distance / this.rules.MinimumDistanceBetweenDashes;
+            const numberOfDashes: number = Math.floor(distance / this.rules.MinimumDistanceBetweenDashes) - 1;
+            const distanceBetweenDashes: number = distance / (numberOfDashes + 1);
             let counter: number = 0;
             let counter: number = 0;
 
 
-            startX += distanceBetweenDashes / 2;
-            endX -= distanceBetweenDashes / 2;
+            startX += distanceBetweenDashes;
+            endX -= distanceBetweenDashes;
             while (counter <= Math.floor(numberOfDashes / 2.0) && endX > startX) {
             while (counter <= Math.floor(numberOfDashes / 2.0) && endX > startX) {
                 distance = this.calculateRightAndLeftDashesForLyricWord(staffLine, startX, endX, y);
                 distance = this.calculateRightAndLeftDashesForLyricWord(staffLine, startX, endX, y);
                 startX += distanceBetweenDashes;
                 startX += distanceBetweenDashes;
@@ -1809,9 +1810,10 @@ export abstract class MusicSheetCalculator {
                 counter++;
                 counter++;
             }
             }
 
 
-            // if the remaining distance isn't big enough for two Dashes (another check would be if numberOfDashes is uneven),
+            // if the remaining distance isn't big enough for two Dashes,
+            // but long enough for a middle dash inbetween,
             // then put the last Dash in the middle of the remaining distance
             // then put the last Dash in the middle of the remaining distance
-            if (distance > distanceBetweenDashes) {
+            if (distance > distanceBetweenDashes * 2) {
                 this.calculateSingleDashForLyricWord(staffLine, startX, endX, y);
                 this.calculateSingleDashForLyricWord(staffLine, startX, endX, y);
             }
             }
         }
         }

+ 9 - 0
src/MusicalScore/Graphical/MusicSheetDrawer.ts

@@ -343,6 +343,11 @@ export abstract class MusicSheetDrawer {
             this.drawDashes(staffLine.LyricsDashes);
             this.drawDashes(staffLine.LyricsDashes);
         }
         }
 
 
+        // draw lyric lines (e.g. LyricExtends: "dich,___")
+        if (staffLine.LyricLines.length > 0) {
+            this.drawLyricLines(staffLine.LyricLines);
+        }
+
         if (this.skyLineVisible) {
         if (this.skyLineVisible) {
             this.drawSkyLine(staffLine);
             this.drawSkyLine(staffLine);
         }
         }
@@ -352,6 +357,10 @@ export abstract class MusicSheetDrawer {
         }
         }
     }
     }
 
 
+    protected drawLyricLines(lyricLines: GraphicalLine[]): void {
+        // throw new Error("not implemented");
+    }
+
     /**
     /**
      * Draw all dashes to the canvas
      * Draw all dashes to the canvas
      * @param lyricsDashes Array of lyric dashes to be drawn
      * @param lyricsDashes Array of lyric dashes to be drawn

+ 24 - 10
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts

@@ -31,6 +31,7 @@ import {LyricsEntry} from "../../VoiceData/Lyrics/LyricsEntry";
 import {GraphicalLyricWord} from "../GraphicalLyricWord";
 import {GraphicalLyricWord} from "../GraphicalLyricWord";
 import {VexFlowStaffEntry} from "./VexFlowStaffEntry";
 import {VexFlowStaffEntry} from "./VexFlowStaffEntry";
 import {BoundingBox} from "../BoundingBox";
 import {BoundingBox} from "../BoundingBox";
+import { EngravingRules } from "../EngravingRules";
 
 
 export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
 export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
 
 
@@ -141,35 +142,48 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
   }
   }
 
 
   public calculateMeasureWidthFromLyrics(measuresVertical: GraphicalMeasure[], oldMinimumStaffEntriesWidth: number): number {
   public calculateMeasureWidthFromLyrics(measuresVertical: GraphicalMeasure[], oldMinimumStaffEntriesWidth: number): number {
-    const minLyricsSpacing: number = 1;
     let lastLyricsLabelHalfWidth: number = 0; // TODO lyrics entries may not be ordered correctly, create a dictionary
     let lastLyricsLabelHalfWidth: number = 0; // TODO lyrics entries may not be ordered correctly, create a dictionary
     let lastStaffEntryXPosition: number = 0;
     let lastStaffEntryXPosition: number = 0;
     let elongationFactorMeasureWidth: number = 1;
     let elongationFactorMeasureWidth: number = 1;
     for (const measure of measuresVertical) {
     for (const measure of measuresVertical) {
-      for (let i: number = 1; i < measure.staffEntries.length; i++) {
+      for (let i: number = 0; i < measure.staffEntries.length; i++) {
         const staffEntry: GraphicalStaffEntry = measure.staffEntries[i];
         const staffEntry: GraphicalStaffEntry = measure.staffEntries[i];
         if (staffEntry.LyricsEntries.length === 0) {
         if (staffEntry.LyricsEntries.length === 0) {
           continue;
           continue;
         }
         }
-        const lyricsEntry: GraphicalLyricEntry = staffEntry.LyricsEntries[0];
         // TODO choose biggest lyrics entry or handle each separately and take maximum elongation
         // TODO choose biggest lyrics entry or handle each separately and take maximum elongation
+        const lyricsEntry: GraphicalLyricEntry = staffEntry.LyricsEntries[0];
+        let minLyricsSpacing: number = EngravingRules.Rules.HorizontalBetweenLyricsDistance;
+
+        if (lyricsEntry.ParentLyricWord) {
+          if (lyricsEntry.GetLyricsEntry.SyllableIndex > 0) {
+            // give a little more spacing so that the dash between syllables fits
+            minLyricsSpacing = EngravingRules.Rules.BetweenSyllabelMinimumDistance;
+          }
+        }
 
 
         const lyricsBbox: BoundingBox = lyricsEntry.GraphicalLabel.PositionAndShape;
         const lyricsBbox: BoundingBox = lyricsEntry.GraphicalLabel.PositionAndShape;
         const lyricsLabelHalfWidth: number = lyricsBbox.Size.width / 2;
         const lyricsLabelHalfWidth: number = lyricsBbox.Size.width / 2;
         const staffEntryXPosition: number = (staffEntry as VexFlowStaffEntry).PositionAndShape.RelativePosition.x;
         const staffEntryXPosition: number = (staffEntry as VexFlowStaffEntry).PositionAndShape.RelativePosition.x;
 
 
-        const spaceNeededByLyrics: number = lastLyricsLabelHalfWidth + lyricsLabelHalfWidth + minLyricsSpacing;
-        /* make extra space for lyric word dashes. takes too much space currently. doesn't work correctly yet.
-        const nonStartingPartOfLyricWord: boolean = lyricsEntry.ParentLyricWord !== undefined &&
-        lyricsEntry !== lyricsEntry.ParentLyricWord.GraphicalLyricsEntries[0];
-        if (nonStartingPartOfLyricWord) {
-          spaceNeededByLyrics += minLyricsSpacing * 0;
-        }*/
+        if (i === 0) {
+          lastStaffEntryXPosition = staffEntryXPosition;
+          lastLyricsLabelHalfWidth = lyricsLabelHalfWidth;
+          // ignore first lyrics of measure
+          // TODO spacing the first lyrics compared to the last measure's last lyrics entry
+          // will require more sophisticated lastLyrics variable setting because of vertical order
+          continue;
+        }
+
+        const spaceNeededByLyrics: number =
+          lastLyricsLabelHalfWidth + lyricsLabelHalfWidth + minLyricsSpacing;
 
 
         const staffEntrySpacing: number = staffEntryXPosition - lastStaffEntryXPosition;
         const staffEntrySpacing: number = staffEntryXPosition - lastStaffEntryXPosition;
+        // get factor of how much we need to stretch the measure to space the current lyric with the last one
         const elongationFactorMeasureWidthForCurrentLabels: number = spaceNeededByLyrics / staffEntrySpacing;
         const elongationFactorMeasureWidthForCurrentLabels: number = spaceNeededByLyrics / staffEntrySpacing;
         elongationFactorMeasureWidth = Math.max(elongationFactorMeasureWidth, elongationFactorMeasureWidthForCurrentLabels);
         elongationFactorMeasureWidth = Math.max(elongationFactorMeasureWidth, elongationFactorMeasureWidthForCurrentLabels);
 
 
+        // set up last measure information for next measure
         lastStaffEntryXPosition = staffEntryXPosition;
         lastStaffEntryXPosition = staffEntryXPosition;
         lastLyricsLabelHalfWidth = lyricsLabelHalfWidth;
         lastLyricsLabelHalfWidth = lyricsLabelHalfWidth;
       }
       }

+ 2 - 1
src/MusicalScore/ScoreIO/MusicSymbolModules/LyricsReader.ts

@@ -86,7 +86,8 @@ export class LyricsReader {
                             if (syllabic === "single" || syllabic === "end") {
                             if (syllabic === "single" || syllabic === "end") {
                                 if (this.openLyricWords[currentLyricVerseNumber] !== undefined) { // word end given or some word still open
                                 if (this.openLyricWords[currentLyricVerseNumber] !== undefined) { // word end given or some word still open
                                     this.currentLyricWord = this.openLyricWords[currentLyricVerseNumber];
                                     this.currentLyricWord = this.openLyricWords[currentLyricVerseNumber];
-                                    lyricsEntry = new LyricsEntry(text, currentLyricVerseNumber, this.currentLyricWord, currentVoiceEntry);
+                                    const syllabelNumber: number = this.currentLyricWord.Syllables.length;
+                                    lyricsEntry = new LyricsEntry(text, currentLyricVerseNumber, this.currentLyricWord, currentVoiceEntry, syllabelNumber);
                                     this.currentLyricWord.Syllables.push(lyricsEntry);
                                     this.currentLyricWord.Syllables.push(lyricsEntry);
                                     delete this.openLyricWords[currentLyricVerseNumber];
                                     delete this.openLyricWords[currentLyricVerseNumber];
                                     this.currentLyricWord = undefined;
                                     this.currentLyricWord = undefined;

+ 9 - 1
src/MusicalScore/VoiceData/Lyrics/LyricsEntry.ts

@@ -2,16 +2,20 @@ import {LyricWord} from "./LyricsWord";
 import {VoiceEntry} from "../VoiceEntry";
 import {VoiceEntry} from "../VoiceEntry";
 
 
 export class LyricsEntry {
 export class LyricsEntry {
-    constructor(text: string, verseNumber: number, word: LyricWord, parent: VoiceEntry) {
+    constructor(text: string, verseNumber: number, word: LyricWord, parent: VoiceEntry, syllableNumber: number = -1) {
         this.text = text;
         this.text = text;
         this.word = word;
         this.word = word;
         this.parent = parent;
         this.parent = parent;
         this.verseNumber = verseNumber;
         this.verseNumber = verseNumber;
+        if (syllableNumber >= 0) {
+            this.syllableIndex = syllableNumber;
+        }
     }
     }
     private text: string;
     private text: string;
     private word: LyricWord;
     private word: LyricWord;
     private parent: VoiceEntry;
     private parent: VoiceEntry;
     private verseNumber: number;
     private verseNumber: number;
+    private syllableIndex: number;
     public extend: boolean;
     public extend: boolean;
 
 
     public get Text(): string {
     public get Text(): string {
@@ -33,4 +37,8 @@ export class LyricsEntry {
     public get VerseNumber(): number {
     public get VerseNumber(): number {
         return this.verseNumber;
         return this.verseNumber;
     }
     }
+
+    public get SyllableIndex(): number {
+        return this.syllableIndex;
+    }
 }
 }