diff --git a/include/vrv/adjustbeamsfunctor.h b/include/vrv/adjustbeamsfunctor.h index bf8462a161..83cc3efb93 100644 --- a/include/vrv/adjustbeamsfunctor.h +++ b/include/vrv/adjustbeamsfunctor.h @@ -53,6 +53,15 @@ class AdjustBeamsFunctor : public DocFunctor { // Get the drawing interface of the outer beam or the outer ftrem BeamDrawingInterface *GetOuterBeamInterface() const; + /** + * Calculate the overlap with other layer elements that + * are placed within the duration of the element + */ + int CalcLayerOverlap(const LayerElement *beamElement) const; + + // Rounds the overlap to the closest multiple of a half unit + int AdjustOverlapToHalfUnit(int overlap, int unit) const; + public: // private: diff --git a/include/vrv/beam.h b/include/vrv/beam.h index 2ab3afd24c..464499f98d 100644 --- a/include/vrv/beam.h +++ b/include/vrv/beam.h @@ -375,11 +375,6 @@ class Beam : public LayerElement, */ void FilterList(ListOfConstObjects &childList) const override; - /** - * See LayerElement::SetElementShortening - */ - void SetElementShortening(int shortening) override; - private: /** * A pointer to the beam with which stems are shared. @@ -410,7 +405,6 @@ class BeamElementCoord { m_tabDurSym = NULL; m_stem = NULL; m_overlapMargin = 0; - m_maxShortening = -1; m_beamRelativePlace = BEAMPLACE_NONE; m_partialFlagPlace = BEAMPLACE_NONE; } @@ -458,7 +452,6 @@ class BeamElementCoord { data_DURATION m_dur; // drawing duration int m_breaksec; int m_overlapMargin; - int m_maxShortening; // maximum allowed shortening in half units bool m_centered; // beam is centered on the line data_BEAMPLACE m_beamRelativePlace; char m_partialFlags[MAX_DURATION_PARTIALS]; diff --git a/include/vrv/ftrem.h b/include/vrv/ftrem.h index 79e1e80577..4e38e755e5 100644 --- a/include/vrv/ftrem.h +++ b/include/vrv/ftrem.h @@ -90,11 +90,6 @@ class FTrem : public LayerElement, public BeamDrawingInterface, public AttFTremV */ void FilterList(ListOfConstObjects &childList) const override; - /** - * See LayerElement::SetElementShortening - */ - void SetElementShortening(int shortening) override; - public: /** */ BeamSegment m_beamSegment; diff --git a/include/vrv/layerelement.h b/include/vrv/layerelement.h index b4557c22f6..6a9e1cc55d 100644 --- a/include/vrv/layerelement.h +++ b/include/vrv/layerelement.h @@ -304,11 +304,6 @@ class LayerElement : public Object, std::pair CalcElementHorizontalOverlap(const Doc *doc, const std::vector &otherElements, bool areDotsAdjusted, bool isChordElement, bool isLowerElement = false, bool unison = true); - /** - * Helper function to set shortening for elements with beam interface - */ - virtual void SetElementShortening(int shortening) {} - /** * Get the stem mod for the element (if any) */ @@ -330,12 +325,6 @@ class LayerElement : public Object, */ MapOfDotLocs CalcOptimalDotLocations(); - /** - * Calculate the overlap with other layer elements that - * are placed within the duration of the element - */ - int CalcLayerOverlap(const Doc *doc, int direction, int y1, int y2); - //----------// // Functors // //----------// diff --git a/src/adjustbeamsfunctor.cpp b/src/adjustbeamsfunctor.cpp index 86c2f41b52..ebb4629c4b 100644 --- a/src/adjustbeamsfunctor.cpp +++ b/src/adjustbeamsfunctor.cpp @@ -63,7 +63,7 @@ FunctorCode AdjustBeamsFunctor::VisitBeam(Beam *beam) m_x2 = beamSegment.m_beamElementCoordRefs.back()->m_x; m_beamSlope = beamSegment.m_beamSlope; m_directionBias = (beam->m_drawingPlace == BEAMPLACE_above) ? 1 : -1; - m_overlapMargin = beam->CalcLayerOverlap(m_doc, m_directionBias, m_y1, m_y2); + m_overlapMargin = this->CalcLayerOverlap(beam); } return FUNCTOR_CONTINUE; } @@ -148,15 +148,13 @@ FunctorCode AdjustBeamsFunctor::VisitClef(Clef *clef) // calculate margins for the clef const int leftMargin = m_directionBias * (currentBeamYLeft - clefBounds) - beams * beamWidth; const int rightMargin = m_directionBias * (currentBeamYRight - clefBounds) - beams * beamWidth; - const int overlapMargin = std::min(leftMargin, rightMargin); + int overlapMargin = std::min(leftMargin, rightMargin); if (overlapMargin >= 0) return FUNCTOR_CONTINUE; // calculate offset required for the beam + overlapMargin *= -m_directionBias; const int unit = m_doc->GetDrawingUnit(staff->m_drawingStaffSize); - const int unitChangeNumber = ((std::abs(overlapMargin) + unit / 6) / unit); - if (unitChangeNumber > 0) { - const int adjust = unitChangeNumber * unit * m_directionBias; - if (std::abs(adjust) > std::abs(m_overlapMargin)) m_overlapMargin = adjust; - } + const int adjust = this->AdjustOverlapToHalfUnit(overlapMargin, unit); + if (std::abs(adjust) > std::abs(m_overlapMargin)) m_overlapMargin = adjust; return FUNCTOR_CONTINUE; } @@ -181,7 +179,7 @@ FunctorCode AdjustBeamsFunctor::VisitFTrem(FTrem *fTrem) m_x2 = beamSegment.m_beamElementCoordRefs.back()->m_x; m_beamSlope = beamSegment.m_beamSlope; m_directionBias = (fTrem->m_drawingPlace == BEAMPLACE_above) ? 1 : -1; - m_overlapMargin = fTrem->CalcLayerOverlap(m_doc, m_directionBias, m_y1, m_y2); + m_overlapMargin = this->CalcLayerOverlap(fTrem); } return FUNCTOR_CONTINUE; } @@ -301,7 +299,7 @@ FunctorCode AdjustBeamsFunctor::VisitRest(Rest *rest) // Calculate possible overlap for the rest with beams const int beams = m_outerBeam->GetBeamPartDuration(rest, false) - DURATION_4; const int beamWidth = m_outerBeam->m_beamWidth; - const int overlapMargin = rest->Intersects(m_outerBeam, SELF, beams * beamWidth, true) * m_directionBias; + int overlapMargin = rest->Intersects(m_outerBeam, SELF, beams * beamWidth, true) * m_directionBias; // Adjust drawing location for the rest based on the overlap with beams. // Adjustment should be an even number, so that the rest is positioned properly @@ -335,9 +333,9 @@ FunctorCode AdjustBeamsFunctor::VisitRest(Rest *rest) } } + overlapMargin *= -m_directionBias; const int unit = m_doc->GetDrawingUnit(staff->m_drawingStaffSize); - const int unitChangeNumber = std::abs(overlapMargin) / unit + 1; - const int adjust = unitChangeNumber * unit * m_directionBias; + const int adjust = this->AdjustOverlapToHalfUnit(overlapMargin, unit); if (std::abs(adjust) > std::abs(m_overlapMargin)) m_overlapMargin = adjust; return FUNCTOR_CONTINUE; @@ -350,4 +348,87 @@ BeamDrawingInterface *AdjustBeamsFunctor::GetOuterBeamInterface() const return NULL; } +int AdjustBeamsFunctor::CalcLayerOverlap(const LayerElement *beamElement) const +{ + assert(beamElement); + + const Layer *parentLayer = vrv_cast(beamElement->GetFirstAncestor(LAYER)); + if (!parentLayer) return 0; + // Check whether there are elements on the other layer in the duration of the current beam + ListOfConstObjects collidingElementsList = parentLayer->GetLayerElementsForTimeSpanOf(beamElement, true); + // Ignore any elements part of a stem-sameas beam + if (beamElement->Is(BEAM)) { + const Beam *beam = vrv_cast(beamElement); + const Beam *stemSameAsBeam = beam->GetStemSameasBeam(); + if (stemSameAsBeam) { + collidingElementsList.remove_if([stemSameAsBeam](const Object *object) { + const LayerElement *layerElement = vrv_cast(object); + return (layerElement->GetAncestorBeam() == stemSameAsBeam); + }); + } + } + // If there are none - stop here, there's nothing to be done + if (collidingElementsList.empty()) return 0; + + const Staff *staff = beamElement->GetAncestorStaff(); + const int drawingY = beamElement->GetDrawingY(); + const int yMin = std::min(m_y1, m_y2); + const int yMax = std::max(m_y1, m_y2); + const int unit = m_doc->GetDrawingUnit(staff->m_drawingStaffSize); + + int elementOverlap = 0; + std::vector elementOverlaps; + for (const Object *object : collidingElementsList) { + const LayerElement *layerElement = vrv_cast(object); + if (!beamElement->HorizontalContentOverlap(object)) continue; + const int elementBottom = layerElement->GetContentBottom(); + const int elementTop = layerElement->GetContentTop(); + if (m_directionBias > 0) { + // Ensure that there's actual overlap first + if (elementBottom > yMax) continue; + if (drawingY >= elementTop) continue; + // If there is a mild overlap, then decrease the beam stem length via negative overlap + if (elementBottom > yMax - 3 * unit) { + elementOverlap = std::min(elementBottom - yMax, 0); + } + else { + elementOverlap = std::max(elementTop - yMin, 0); + } + } + else { + // Ensure that there's actual overlap first + if (elementTop < yMin) continue; + if (drawingY <= elementBottom) continue; + // If there is a mild overlap, then decrease the beam stem length via negative overlap + if (elementTop < yMin + 3 * unit) { + elementOverlap = std::min(yMin - elementTop, 0); + } + else { + elementOverlap = std::max(yMax - elementBottom, 0); + } + } + elementOverlaps.emplace_back(elementOverlap); + } + if (elementOverlaps.empty()) return 0; + + const auto [minOverlap, maxOverlap] = std::minmax_element(elementOverlaps.begin(), elementOverlaps.end()); + int overlap = 0; + if (*maxOverlap > 0) { + overlap = *maxOverlap * m_directionBias; + } + else if (*minOverlap < 0) { + overlap = (*minOverlap - unit) * m_directionBias; + } + const int adjust = this->AdjustOverlapToHalfUnit(overlap, unit); + return adjust; +} + +int AdjustBeamsFunctor::AdjustOverlapToHalfUnit(int overlap, int unit) const +{ + const int overlapSign = (overlap >= 0) ? 1 : -1; + const int halfUnit = unit / 2; + const int halfUnitChangeNumber = (std::abs(overlap) + halfUnit / 2) / halfUnit; + return halfUnitChangeNumber * halfUnit * overlapSign; +} + } // namespace vrv diff --git a/src/beam.cpp b/src/beam.cpp index 5cffc7fb90..f779239884 100644 --- a/src/beam.cpp +++ b/src/beam.cpp @@ -1951,13 +1951,9 @@ int BeamElementCoord::CalculateStemLength( const int standardStemLen = STANDARD_STEMLENGTH * 2; // Check if the stem has to be shortened because outside the staff // In this case, Note::CalcStemLenInThirdUnits will return a value shorter than 2 * STANDARD_STEMLENGTH - int stemLenInHalfUnits - = !m_maxShortening ? standardStemLen : m_closestNote->CalcStemLenInThirdUnits(staff, stemDir) * 2 / 3; + const int stemLenInHalfUnits = m_closestNote->CalcStemLenInThirdUnits(staff, stemDir) * 2 / 3; // Do not extend when not on the staff line if (stemLenInHalfUnits != standardStemLen) { - if ((m_maxShortening > 0) && ((stemLenInHalfUnits - standardStemLen) > m_maxShortening)) { - stemLenInHalfUnits = standardStemLen - m_maxShortening; - } extend = false; } @@ -2098,12 +2094,6 @@ std::pair Beam::GetAdditionalBeamCount() const return { topShortestDur - DURATION_8, bottomShortestDur - DURATION_8 }; } -void Beam::SetElementShortening(int shortening) -{ - std::for_each(m_beamSegment.m_beamElementCoordRefs.begin(), m_beamSegment.m_beamElementCoordRefs.end(), - [shortening](BeamElementCoord *coord) { coord->m_maxShortening = shortening; }); -} - int Beam::GetBeamPartDuration(int x, bool includeRests) const { // find element with position closest to the specified coordinate diff --git a/src/ftrem.cpp b/src/ftrem.cpp index f89d56e183..869d9b489f 100644 --- a/src/ftrem.cpp +++ b/src/ftrem.cpp @@ -112,12 +112,6 @@ std::pair FTrem::GetFloatingBeamCount() const return { this->GetBeams(), this->GetBeamsFloat() }; } -void FTrem::SetElementShortening(int shortening) -{ - std::for_each(m_beamSegment.m_beamElementCoordRefs.begin(), m_beamSegment.m_beamElementCoordRefs.end(), - [shortening](BeamElementCoord *coord) { coord->m_maxShortening = shortening; }); -} - //---------------------------------------------------------------------------- // Functors methods //---------------------------------------------------------------------------- diff --git a/src/layerelement.cpp b/src/layerelement.cpp index 9351443a93..8585bd8be3 100644 --- a/src/layerelement.cpp +++ b/src/layerelement.cpp @@ -996,90 +996,6 @@ MapOfDotLocs LayerElement::CalcOptimalDotLocations() return usePrimary ? dotLocs1 : dotLocs2; } -int LayerElement::CalcLayerOverlap(const Doc *doc, int direction, int y1, int y2) -{ - Layer *parentLayer = vrv_cast(this->GetFirstAncestor(LAYER)); - if (!parentLayer) return 0; - // Check whether there are elements on the other layer in the duration of the current beam - ListOfObjects collidingElementsList = parentLayer->GetLayerElementsForTimeSpanOf(this, true); - // Ignore any elements part of a stem-sameas beam - if (this->Is(BEAM)) { - const Beam *beam = vrv_cast(this); - const Beam *stemSameAsBeam = beam->GetStemSameasBeam(); - if (stemSameAsBeam) { - collidingElementsList.remove_if([stemSameAsBeam](Object *object) { - const LayerElement *layerElement = vrv_cast(object); - return (layerElement->GetAncestorBeam() == stemSameAsBeam); - }); - } - } - // If there are none - stop here, there's nothing to be done - if (collidingElementsList.empty()) return 0; - - Staff *staff = this->GetAncestorStaff(); - - const int unit = doc->GetDrawingUnit(staff->m_drawingStaffSize); - int leftMargin = 0; - int rightMargin = 0; - bool sameDirElement = false; - std::vector elementOverlaps; - for (Object *object : collidingElementsList) { - LayerElement *layerElement = vrv_cast(object); - if (!this->HorizontalContentOverlap(object)) continue; - const int elementBottom = layerElement->GetDrawingBottom(doc, staff->m_drawingStaffSize); - const int elementTop = layerElement->GetDrawingTop(doc, staff->m_drawingStaffSize); - if (direction > 0) { - // make sure that there's actual overlap first - if ((elementBottom > y1) && (elementBottom > y2)) continue; - const int currentBottom = this->GetDrawingBottom(doc, staff->m_drawingStaffSize); - if (currentBottom >= elementTop) continue; - const StemmedDrawingInterface *stemInterface = layerElement->GetStemmedDrawingInterface(); - if (stemInterface && (sameDirElement || (stemInterface->GetDrawingStemDir() == STEMDIRECTION_up))) { - if (elementBottom - stemInterface->GetDrawingStemLen() < currentBottom) continue; - leftMargin = unit + y1 - elementBottom; - rightMargin = unit + y2 - elementBottom; - sameDirElement = true; - } - else { - leftMargin = elementTop - y1; - rightMargin = elementTop - y2; - } - } - else { - // make sure that there's actual overlap first - if ((elementTop < y1) && (elementTop < y2)) continue; - const int currentTop = this->GetDrawingTop(doc, staff->m_drawingStaffSize); - if (currentTop <= elementBottom) continue; - const StemmedDrawingInterface *stemInterface = layerElement->GetStemmedDrawingInterface(); - if (stemInterface && (sameDirElement || (stemInterface->GetDrawingStemDir() == STEMDIRECTION_down))) { - if (currentTop - stemInterface->GetDrawingStemLen() > currentTop) continue; - leftMargin = unit + y1 - elementTop; - rightMargin = unit + y2 - elementTop; - sameDirElement = true; - } - else { - leftMargin = elementBottom - y1; - rightMargin = elementBottom - y2; - } - } - elementOverlaps.emplace_back(std::max(leftMargin * direction, rightMargin * direction)); - } - if (elementOverlaps.empty()) return 0; - - const auto maxOverlap = std::max_element(elementOverlaps.begin(), elementOverlaps.end()); - int overlap = 0; - if (*maxOverlap >= 0) { - const int multiplier = sameDirElement ? -1 : 1; - overlap = ((*maxOverlap == 0) ? unit : *maxOverlap) * direction * multiplier; - } - else { - int maxShorteningInHalfUnits = (std::abs(*maxOverlap) / unit) * 2; - if (maxShorteningInHalfUnits > 0) --maxShorteningInHalfUnits; - this->SetElementShortening(maxShorteningInHalfUnits); - } - return overlap; -} - data_STEMMODIFIER LayerElement::GetDrawingStemMod() const { const AttStems *stem = dynamic_cast(this);