Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved distances and centering of cross-staff beams #23575

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/engraving/dom/beam.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,32 @@ const Chord* Beam::findChordWithCustomStemDirection() const
return nullptr;
}

const BeamSegment* Beam::topLevelSegmentForElement(const ChordRest* element) const
{
size_t segmentsSize = m_beamSegments.size();

IF_ASSERT_FAILED(segmentsSize > 0) {
return nullptr;
}

const BeamSegment* curSegment = m_beamSegments[0];
if (segmentsSize == 1) {
return curSegment;
}

for (const BeamSegment* segment : m_beamSegments) {
if (segment->level <= curSegment->level) {
continue;
}
Fraction elementTick = element->tick();
if (segment->startTick <= elementTick && segment->endTick >= elementTick) {
curSegment = segment;
}
}

return curSegment;
}

//---------------------------------------------------------
// move
//---------------------------------------------------------
Expand Down
2 changes: 2 additions & 0 deletions src/engraving/dom/beam.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,8 @@ class Beam final : public BeamBase

const Chord* findChordWithCustomStemDirection() const;

const BeamSegment* topLevelSegmentForElement(const ChordRest* element) const;

inline int directionIdx() const { return (m_direction == DirectionV::AUTO || m_direction == DirectionV::DOWN) ? 0 : 1; }

private:
Expand Down
2 changes: 2 additions & 0 deletions src/engraving/dom/beambase.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ class BeamBase : public EngravingItem
const StaffType* tab = nullptr;
bool isBesideTabStaff = false;
CrossStaffBeamPosition crossStaffBeamPos = CrossStaffBeamPosition::INVALID;
const Chord* limitingChordAbove = nullptr; // <-
const Chord* limitingChordBelow = nullptr; // <- For cross-staff spacing and centering

void setAnchors(PointF startA, PointF endA) { startAnchor = startA; endAnchor = endA; }
bool isValid() const override { return !(beamType == BeamType::INVALID); }
Expand Down
29 changes: 26 additions & 3 deletions src/engraving/rendering/dev/beamtremololayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -846,9 +846,32 @@ bool BeamTremoloLayout::calculateAnchorsCross(const BeamBase* item, BeamBase::La
}
}

// Sets position for beams inbetween staves
ldata->startAnchor.setY((maxY + minY) / 2);
ldata->endAnchor.setY((maxY + minY) / 2);
double yMidPoint = (maxY + minY) / 2;
if (item->isBeam()) {
// Cross-staff beams may have segments both above and below the midline.
// Compute the y-difference between the top and bottom segment and aim for centering in between.
const Beam* beam = toBeam(item);
const std::vector<BeamSegment*>& beamSegments = beam->beamSegments();
const Chord* limitingChordAbove = beam->ldata()->limitingChordAbove;
const Chord* limitingChordBelow = beam->ldata()->limitingChordBelow;
if (!beamSegments.empty() && limitingChordAbove && limitingChordBelow) {
double yCenterSegment = 0.5 * (beamSegments.front()->line.y1() + beamSegments.front()->line.y2());

const BeamSegment* topBeamSegmentForChordAbove = beam->topLevelSegmentForElement(limitingChordAbove);
double yTopSegAbove = 0.5 * (topBeamSegmentForChordAbove->line.y1() + topBeamSegmentForChordAbove->line.y2());
double distAbove = yTopSegAbove - yCenterSegment;

const BeamSegment* topBeamSegmentForChordBelow = beam->topLevelSegmentForElement(limitingChordBelow);
double yTopSegBelow = 0.5 * (topBeamSegmentForChordBelow->line.y1() + topBeamSegmentForChordBelow->line.y2());
double distBelow = yTopSegBelow - yCenterSegment;

double averageDist = 0.5 * (distAbove + distBelow);
yMidPoint -= 0.5 * averageDist;
}
}

ldata->startAnchor.setY(yMidPoint);
ldata->endAnchor.setY(yMidPoint);
ldata->startAnchor.setX(chordBeamAnchorX(ldata, startCr, ChordBeamAnchorType::Start));
ldata->endAnchor.setX(chordBeamAnchorX(ldata, endCr, ChordBeamAnchorType::End));

Expand Down
5 changes: 3 additions & 2 deletions src/engraving/rendering/dev/beamtremololayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ class BeamTremoloLayout
static PointF chordBeamAnchor(const BeamBase::LayoutData* ldata, const ChordRest* chord, ChordBeamAnchorType anchorType);
static int getMaxSlope(const BeamBase::LayoutData* ldata);
static void extendStem(const BeamBase::LayoutData* ldata, Chord* chord, double addition);
static int minStemLength(const ChordRest* cr, const BeamBase::LayoutData* ldata);
static int strokeCount(const BeamBase::LayoutData* ldata, const ChordRest* cr);

private:

Expand Down Expand Up @@ -93,10 +95,9 @@ class BeamTremoloLayout
static void add8thSpaceSlant(BeamBase::LayoutData* ldata, PointF& dictatorAnchor, int dictator, int pointer, int beamCount,
int interval, int middleLine, bool Flat);
static bool noSlope(const Beam* beam);
static int strokeCount(const BeamBase::LayoutData* ldata, const ChordRest* cr);

static bool calculateAnchorsCross(const BeamBase* item, BeamBase::LayoutData* ldata, const LayoutConfiguration& conf);
static bool computeTremoloUp(const BeamBase::LayoutData* ldata);
static int minStemLength(const ChordRest* cr, const BeamBase::LayoutData* ldata);
};
} // namespace mu::engraving
#endif
56 changes: 40 additions & 16 deletions src/engraving/rendering/dev/systemlayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
#include "alignmentlayout.h"
#include "autoplace.h"
#include "beamlayout.h"
#include "beamtremololayout.h"
#include "chordlayout.h"
#include "harmonylayout.h"
#include "lyricslayout.h"
Expand Down Expand Up @@ -2356,8 +2357,8 @@ void SystemLayout::layout2(System* system, LayoutContext& ctx)
}
}
dist = std::max(dist, d + minVerticalDistance);
dist = std::max(dist, minVertSpaceForCrossStaffBeams(system, si1, si2, ctx));
}
dist = std::max(dist, minVertSpaceForCrossStaffBeams(system, si1, si2));
ss->setYOff(yOffset);
ss->setbbox(system->leftMargin(), y - yOffset, system->width() - system->leftMargin(), h);
ss->saveLayout();
Expand All @@ -2382,7 +2383,7 @@ void SystemLayout::layout2(System* system, LayoutContext& ctx)
SystemLayout::layoutInstrumentNames(system, ctx);
}

double SystemLayout::minVertSpaceForCrossStaffBeams(System* system, staff_idx_t staffIdx1, staff_idx_t staffIdx2)
double SystemLayout::minVertSpaceForCrossStaffBeams(System* system, staff_idx_t staffIdx1, staff_idx_t staffIdx2, LayoutContext& ctx)
{
double minSpace = -DBL_MAX;
track_idx_t startTrack = staffIdx1 * VOICES;
Expand All @@ -2401,33 +2402,56 @@ double SystemLayout::minVertSpaceForCrossStaffBeams(System* system, staff_idx_t
continue;
}
Beam* beam = toChord(item)->beam();
if (!beam || !beam->cross() || !beam->autoplace() || beam->elements().front() != item) {
if (!beam || !beam->autoplace() || beam->elements().front() != item) {
continue;
}
Note* highestOfBottomStaff = nullptr;
Note* lowestOfTopStaff = nullptr;
for (ChordRest* cr : beam->elements()) {
if (beam->ldata()->crossStaffBeamPos != BeamBase::CrossStaffBeamPosition::BETWEEN) {
continue;
}
const Chord* limitingChordAbove = nullptr;
const Chord* limitingChordBelow = nullptr;
double limitFromAbove = -DBL_MAX;
double limitFromBelow = DBL_MAX;
for (const ChordRest* cr : beam->elements()) {
if (!cr->isChord()) {
continue;
}
Chord* chord = toChord(cr);
const Chord* chord = toChord(cr);
double minStemLength = BeamTremoloLayout::minStemLength(chord, beam->ldata()) * (chord->spatium() / 4);
bool isUnderCrossBeam = cr->isBelowCrossBeam(beam);
if (isUnderCrossBeam && chord->vStaffIdx() == staffIdx2) {
if (!highestOfBottomStaff || chord->upNote()->line() < highestOfBottomStaff->line()) {
highestOfBottomStaff = chord->upNote();
const Note* topNote = chord->upNote();
double noteLimit = topNote->y() - minStemLength;
if (noteLimit < limitFromBelow) {
limitFromBelow = noteLimit;
limitingChordBelow = chord;
}
limitFromBelow = std::min(limitFromBelow, noteLimit);
} else if (!isUnderCrossBeam && chord->vStaffIdx() == staffIdx1) {
if (!lowestOfTopStaff || chord->downNote()->line() > lowestOfTopStaff->line()) {
lowestOfTopStaff = chord->downNote();
const Note* bottomNote = chord->downNote();
double noteLimit = bottomNote->y() + minStemLength;
if (noteLimit > limitFromAbove) {
limitFromAbove = noteLimit;
limitingChordAbove = chord;
}
}
}
if (!highestOfBottomStaff || !lowestOfTopStaff) {
continue;
if (limitingChordAbove && limitingChordBelow) {
double minSpaceRequired = limitFromAbove - limitFromBelow;
beam->mutldata()->limitingChordAbove = limitingChordAbove;
beam->mutldata()->limitingChordBelow = limitingChordBelow;

const BeamSegment* topBeamSegmentForChordAbove = beam->topLevelSegmentForElement(limitingChordAbove);
const BeamSegment* topBeamSegmentForChordBelow = beam->topLevelSegmentForElement(limitingChordBelow);
if (topBeamSegmentForChordAbove->above == topBeamSegmentForChordBelow->above) {
// In this case the two opposing stems overlap the beam height, so we must subtract it
int strokeCount = std::min(BeamTremoloLayout::strokeCount(beam->ldata(), limitingChordAbove),
BeamTremoloLayout::strokeCount(beam->ldata(), limitingChordBelow));
double beamHeight = ctx.conf().styleMM(Sid::beamWidth).val() + beam->beamDist() * (strokeCount - 1);
minSpaceRequired -= beamHeight;
}
minSpace = std::max(minSpace, minSpaceRequired);
}
double bottomYOfTopStaff = lowestOfTopStaff->y() + lowestOfTopStaff->chord()->minStemLength();
double topYofBottomStaff = highestOfBottomStaff->y() - highestOfBottomStaff->chord()->minStemLength();
minSpace = std::max(minSpace, bottomYOfTopStaff - topYofBottomStaff);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/engraving/rendering/dev/systemlayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class SystemLayout
static void addBrackets(System* system, Measure* measure, LayoutContext& ctx);
static Bracket* createBracket(System* system, LayoutContext& ctx, BracketItem* bi, size_t column, staff_idx_t staffIdx,
std::vector<Bracket*>& bl, Measure* measure);
static double minVertSpaceForCrossStaffBeams(System* system, staff_idx_t staffIdx1, staff_idx_t staffIdx2);
static double minVertSpaceForCrossStaffBeams(System* system, staff_idx_t staffIdx1, staff_idx_t staffIdx2, LayoutContext& ctx);

static bool elementShouldBeCenteredBetweenStaves(const EngravingItem* item, const System* system);
static void centerElementBetweenStaves(EngravingItem* element, const System* system);
Expand Down
32 changes: 16 additions & 16 deletions src/importexport/mei/tests/data/cross-staff-01.mscx
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@
<Measure>
<voice>
<Beam>
<l1>48</l1>
<l2>48</l2>
<l1>51</l1>
<l2>51</l2>
</Beam>
<Chord>
<durationType>16th</durationType>
Expand Down Expand Up @@ -113,8 +113,8 @@
</Note>
</Chord>
<Beam>
<l1>48</l1>
<l2>48</l2>
<l1>51</l1>
<l2>51</l2>
</Beam>
<Chord>
<staffMove>1</staffMove>
Expand Down Expand Up @@ -147,8 +147,8 @@
</Note>
</Chord>
<Beam>
<l1>48</l1>
<l2>48</l2>
<l1>51</l1>
<l2>51</l2>
</Beam>
<Chord>
<durationType>16th</durationType>
Expand Down Expand Up @@ -181,8 +181,8 @@
</Note>
</Chord>
<Beam>
<l1>48</l1>
<l2>48</l2>
<l1>45</l1>
<l2>45</l2>
</Beam>
<Chord>
<staffMove>1</staffMove>
Expand Down Expand Up @@ -233,8 +233,8 @@
<sigD>4</sigD>
</TimeSig>
<Beam>
<l1>-16</l1>
<l2>-8</l2>
<l1>-13</l1>
<l2>-5</l2>
</Beam>
<Chord>
<staffMove>-1</staffMove>
Expand Down Expand Up @@ -267,8 +267,8 @@
</Note>
</Chord>
<Beam>
<l1>-16</l1>
<l2>-8</l2>
<l1>-13</l1>
<l2>-5</l2>
</Beam>
<Chord>
<staffMove>-1</staffMove>
Expand Down Expand Up @@ -301,8 +301,8 @@
</Note>
</Chord>
<Beam>
<l1>-15</l1>
<l2>-9</l2>
<l1>-12</l1>
<l2>-6</l2>
</Beam>
<Chord>
<staffMove>-1</staffMove>
Expand Down Expand Up @@ -332,8 +332,8 @@
<durationType>16th</durationType>
</Rest>
<Beam>
<l1>-23</l1>
<l2>-21</l2>
<l1>-26</l1>
<l2>-24</l2>
</Beam>
<Chord>
<staffMove>-1</staffMove>
Expand Down
Loading