Kaynağa Gözat

fix(Layout): Pickup measures aren't unnecessarily wide anymore (#938)

fix #938

The length of pickup measures is now in proportion to the length of the pickup measure
compared to the active time signature.
E.g. a 1/4 pickup in a 3/4 time signature is now 1/4 / 3/4 = 1/3rd as wide.

This involves a temporary change regarding maxStaffEntries in VexFlowMusicSheetCalculator,
which were previously only considered for the top vertical measure (e.g. voice in voice+piano).
This should be considered for all measures (remove the if),
but that would change spacing overall,
and this commit represents the pure diff for pickup measures only.

Added two new test samples for pickup (implicit) measures
sschmid 4 yıl önce
ebeveyn
işleme
f02bce1b65

+ 30 - 4
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts

@@ -133,7 +133,12 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
   protected calculateMeasureXLayout(measures: GraphicalMeasure[]): number {
     const visibleMeasures: GraphicalMeasure[] = [];
     for (const measure of measures) {
-      visibleMeasures.push(measure);
+      if (measure) {
+        visibleMeasures.push(measure);
+      }
+    }
+    if (visibleMeasures.length === 0) { // e.g. after Multiple Rest measures (VexflowMultiRestMeasure)
+      return 0;
     }
     measures = visibleMeasures;
 
@@ -144,10 +149,15 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
       softmaxFactor: this.rules.SoftmaxFactorVexFlow // this setting is only applied in Vexflow 3.x. also this needs @types/vexflow ^3.0.0
     });
 
+    let maxStaffEntries: number = measures[0].staffEntries.length;
     for (const measure of measures) {
       if (!measure) {
         continue;
       }
+      // the if is a TEMP change to show pure diff for pickup measures, should be done for all measures, but increases spacing
+      if (measure.parentSourceMeasure.ImplicitMeasure) {
+        maxStaffEntries = Math.max(measure.staffEntries.length, maxStaffEntries);
+      }
       const mvoices: { [voiceID: number]: Vex.Flow.Voice } = (measure as VexFlowMeasure).vfVoices;
       const voices: Vex.Flow.Voice[] = [];
       for (const voiceID in mvoices) {
@@ -167,14 +177,30 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     }
 
     let minStaffEntriesWidth: number = 12; // a typical measure has roughly a length of 3*StaffHeight (3*4 = 12)
+    const parentSourceMeasure: SourceMeasure = measures[0].parentSourceMeasure;
+
     if (allVoices.length > 0) {
       // the voicing space bonus addition makes the voicing more relaxed. With a bonus of 0 the notes are basically completely squeezed together.
       const staffEntryFactor: number = 0.3;
 
       minStaffEntriesWidth = formatter.preCalculateMinTotalWidth(allVoices) / unitInPixels
-        * this.rules.VoiceSpacingMultiplierVexflow
-        + this.rules.VoiceSpacingAddendVexflow
-        + measures[0].staffEntries.length * staffEntryFactor;
+      * this.rules.VoiceSpacingMultiplierVexflow
+      + this.rules.VoiceSpacingAddendVexflow
+      + maxStaffEntries * staffEntryFactor;
+      if (parentSourceMeasure?.ImplicitMeasure) {
+        // shrink width in the ratio that the pickup measure is shorter compared to a full measure('s time signature):
+        minStaffEntriesWidth = parentSourceMeasure.Duration.RealValue / parentSourceMeasure.ActiveTimeSignature.RealValue * minStaffEntriesWidth;
+        // e.g. a 1/4 pickup measure in a 3/4 time signature should be 1/4 / 3/4 = 1/3 as long (a third)
+        // it seems like this should be respected by staffEntries.length and preCaculateMinTotalWidth, but apparently not,
+        //   without this the pickup measures were always too long.
+
+        // add more than the original staffEntries scaling again: (removing it above makes it too short)
+        if (maxStaffEntries > 1) { // not necessary for only 1 StaffEntry
+          minStaffEntriesWidth += maxStaffEntries * staffEntryFactor; // don't scale this for implicit measures
+          // in fact overscale it, this needs a lot of space the more staffEntries there are
+        }
+      }
+
         // TODO this could use some fine-tuning. currently using *1.5 + 1 by default, results in decent spacing.
       // firstMeasure.formatVoices = (w: number) => {
       //     formatter.format(allVoices, w);

+ 163 - 0
test/data/test_implicit_pickup_measure_width.musicxml

@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
+<score-partwise version="3.1">
+  <work>
+    <work-title>Pickup (Implicit) Measure Length Test</work-title>
+    </work>
+  <identification>
+    <encoding>
+      <software>MuseScore 3.6.0</software>
+      <encoding-date>2021-01-21</encoding-date>
+      <supports element="accidental" type="yes"/>
+      <supports element="beam" type="yes"/>
+      <supports element="print" attribute="new-page" type="yes" value="yes"/>
+      <supports element="print" attribute="new-system" type="yes" value="yes"/>
+      <supports element="stem" type="yes"/>
+      </encoding>
+    </identification>
+  <defaults>
+    <scaling>
+      <millimeters>7</millimeters>
+      <tenths>40</tenths>
+      </scaling>
+    <page-layout>
+      <page-height>1697.14</page-height>
+      <page-width>1200</page-width>
+      <page-margins type="even">
+        <left-margin>85.7143</left-margin>
+        <right-margin>85.7143</right-margin>
+        <top-margin>85.7143</top-margin>
+        <bottom-margin>85.7143</bottom-margin>
+        </page-margins>
+      <page-margins type="odd">
+        <left-margin>85.7143</left-margin>
+        <right-margin>85.7143</right-margin>
+        <top-margin>85.7143</top-margin>
+        <bottom-margin>85.7143</bottom-margin>
+        </page-margins>
+      </page-layout>
+    <word-font font-family="Edwin" font-size="10"/>
+    <lyric-font font-family="Edwin" font-size="10"/>
+    </defaults>
+  <credit page="1">
+    <credit-type>title</credit-type>
+    <credit-words default-x="600" default-y="1611.43" justify="center" valign="top" font-size="22">Pickup (Implicit) Measure Width Test</credit-words>
+    </credit>
+  <part-list>
+    <part-group type="start" number="1">
+      <group-symbol>brace</group-symbol>
+      </part-group>
+    <score-part id="P1">
+      <part-name>Piano</part-name>
+      <part-abbreviation>Pno.</part-abbreviation>
+      <score-instrument id="P1-I1">
+        <instrument-name>Piano</instrument-name>
+        </score-instrument>
+      <midi-device id="P1-I1" port="1"></midi-device>
+      <midi-instrument id="P1-I1">
+        <midi-channel>1</midi-channel>
+        <midi-program>1</midi-program>
+        <volume>78.7402</volume>
+        <pan>0</pan>
+        </midi-instrument>
+      </score-part>
+    </part-list>
+  <part id="P1">
+    <measure number="0" implicit="yes" width="234.37">
+      <print>
+        <system-layout>
+          <system-margins>
+            <left-margin>50.00</left-margin>
+            <right-margin>0.00</right-margin>
+            </system-margins>
+          <top-system-distance>170.00</top-system-distance>
+          </system-layout>
+        </print>
+      <attributes>
+        <divisions>1</divisions>
+        <key>
+          <fifths>4</fifths>
+          </key>
+        <time>
+          <beats>4</beats>
+          <beat-type>4</beat-type>
+          </time>
+        <clef>
+          <sign>G</sign>
+          <line>2</line>
+          </clef>
+        </attributes>
+      <note default-x="130.48" default-y="-30.00">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>4</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>up</stem>
+        </note>
+      </measure>
+    <measure number="1" width="393.13">
+      <note default-x="13.00" default-y="-15.00">
+        <pitch>
+          <step>C</step>
+          <alter>1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="107.58" default-y="-10.00">
+        <pitch>
+          <step>D</step>
+          <alter>1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="202.17" default-y="-5.00">
+        <pitch>
+          <step>E</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      <note default-x="296.75" default-y="0.00">
+        <pitch>
+          <step>F</step>
+          <alter>1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>1</duration>
+        <voice>1</voice>
+        <type>quarter</type>
+        <stem>down</stem>
+        </note>
+      </measure>
+    <measure number="2" width="351.07">
+      <note default-x="13.96" default-y="5.00">
+        <pitch>
+          <step>G</step>
+          <alter>1</alter>
+          <octave>5</octave>
+          </pitch>
+        <duration>4</duration>
+        <voice>1</voice>
+        <type>whole</type>
+        </note>
+      <barline location="right">
+        <bar-style>light-heavy</bar-style>
+        </barline>
+      </measure>
+    </part>
+  </score-partwise>

BIN
test/data/test_pickup_measure_width_chloe_extended_pickup.mxl