Skip to content

Commit

Permalink
Merge pull request #3455 from DavidBauer1984/develop-accidSpace
Browse files Browse the repository at this point in the history
Restructure accidental adjustment
  • Loading branch information
lpugin authored Jun 19, 2023
2 parents cb6e57a + c1aefee commit 1eca927
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 131 deletions.
20 changes: 19 additions & 1 deletion include/vrv/adjustaccidxfunctor.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,30 @@ class AdjustAccidXFunctor : public DocFunctor {
protected:
//
private:
//
/*
* Get the accidentals for adjustment
*/
std::vector<Accid *> GetAccidentalsForAdjustment(AlignmentReference *alignmentReference) const;

/**
* Sets whether the accidental should be aligned with all elements of the alignmentReference
* or elements from the same layer only.
*/
void SetAccidLayerAlignment(Accid *accid, const AlignmentReference *alignmentReference) const;

/**
* Adjust an accidental horizontally
*/
void AdjustAccidWithSpace(Accid *accid, AlignmentReference *alignmentReference, int staffSize);

public:
//
private:
// The current measure
Measure *m_currentMeasure;

// The accidentals that were already adjusted
std::set<Accid *> m_adjustedAccids;
};

} // namespace vrv
Expand Down
28 changes: 1 addition & 27 deletions include/vrv/horizontalaligner.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,6 @@ class Alignment : public Object {
*/
std::pair<int, int> GetAlignmentTopBottom() const;

/**
* Add an accidental to the accidSpace of the AlignmentReference holding it.
* The Alignment has to have a AlignmentReference holding it.
*/
void AddToAccidSpace(Accid *accid);

/**
* Return true if there is vertical overlap with accidentals from another alignment for specific staffN
*/
Expand Down Expand Up @@ -295,16 +289,6 @@ class AlignmentReference : public Object, public AttNInteger {
*/
void AddChild(Object *object) override;

/**
* Add an accidental to the accidSpace of the AlignmentReference.
*/
void AddToAccidSpace(Accid *accid);

/**
* See AdjustAccidXFunctor
*/
void AdjustAccidWithAccidSpace(Accid *accid, const Doc *doc, int staffSize, std::set<Accid *> &adjustedAccids);

/**
* Return true if one of objects overlaps with accidentals from current reference (i.e. if there are accidentals)
*/
Expand All @@ -320,12 +304,6 @@ class AlignmentReference : public Object, public AttNInteger {
*/
bool HasCrossStaffElements() const;

/**
* Set whether accidentals should be aligned with all elements of alignmentReference or elements from same layer
* only. Set for each accidental in accidSpace separately
*/
void SetAccidLayerAlignment();

//----------//
// Functors //
//----------//
Expand All @@ -343,11 +321,7 @@ class AlignmentReference : public Object, public AttNInteger {
private:
//
public:
/**
* The accid space of the AlignmentReference.
*/
std::vector<Accid *> m_accidSpace;

//
private:
/**
*
Expand Down
91 changes: 76 additions & 15 deletions src/adjustaccidxfunctor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,26 +35,29 @@ FunctorCode AdjustAccidXFunctor::VisitAlignment(Alignment *alignment)

FunctorCode AdjustAccidXFunctor::VisitAlignmentReference(AlignmentReference *alignmentReference)
{
std::vector<Accid *> &accidSpace = alignmentReference->m_accidSpace;
if (accidSpace.empty()) return FUNCTOR_SIBLINGS;
m_adjustedAccids.clear();

std::vector<Accid *> accids = this->GetAccidentalsForAdjustment(alignmentReference);
if (accids.empty()) return FUNCTOR_SIBLINGS;

assert(m_doc);
StaffDef *staffDef = m_doc->GetCurrentScoreDef()->GetStaffDef(alignmentReference->GetN());
int staffSize = (staffDef && staffDef->HasScale()) ? staffDef->GetScale() : 100;

std::sort(accidSpace.begin(), accidSpace.end(), AccidSpaceSort());
std::sort(accids.begin(), accids.end(), AccidSpaceSort());
// process accid layer alignment
alignmentReference->SetAccidLayerAlignment();
for (Accid *accid : accids) {
this->SetAccidLayerAlignment(accid, alignmentReference);
}

// Detect accids which are an octave apart => they will be grouped together in the multiset
std::multiset<Accid *, AccidOctaveSort> octaveEquivalence;
std::copy(accidSpace.begin(), accidSpace.end(), std::inserter(octaveEquivalence, octaveEquivalence.begin()));
std::copy(accids.begin(), accids.end(), std::inserter(octaveEquivalence, octaveEquivalence.begin()));

std::set<Accid *> adjustedAccids;
// Align the octaves
for (Accid *accid : accidSpace) {
for (Accid *accid : accids) {
// Skip any accid that was already adjusted
if (adjustedAccids.count(accid) > 0) continue;
if (m_adjustedAccids.count(accid) > 0) continue;
auto range = octaveEquivalence.equal_range(accid);
// Handle at least two octave accids without unisons
int octaveAccidCount = 0;
Expand All @@ -68,7 +71,7 @@ FunctorCode AdjustAccidXFunctor::VisitAlignmentReference(AlignmentReference *ali
// Now adjust the octave accids and store the left most position
int minDrawingX = -VRV_UNSET;
for (auto octaveIter = range.first; octaveIter != range.second; ++octaveIter) {
alignmentReference->AdjustAccidWithAccidSpace(*octaveIter, m_doc, staffSize, adjustedAccids);
this->AdjustAccidWithSpace(*octaveIter, alignmentReference, staffSize);
minDrawingX = std::min(minDrawingX, (*octaveIter)->GetDrawingX());
}
// Finally, align the accidentals whenever the adjustment is not too large
Expand All @@ -84,26 +87,26 @@ FunctorCode AdjustAccidXFunctor::VisitAlignmentReference(AlignmentReference *ali
}

// Align accidentals for unison notes if any of them are present
for (Accid *accid : accidSpace) {
for (Accid *accid : accids) {
if (accid->GetDrawingUnisonAccid() == NULL) continue;
accid->SetDrawingXRel(accid->GetDrawingUnisonAccid()->GetDrawingXRel());
}

const int count = (int)accidSpace.size();
const int count = (int)accids.size();
const int middle = (count / 2) + (count % 2);
// Zig-zag processing
for (int i = 0, j = count - 1; i < middle; ++i, --j) {
// top one - but skip if already adjusted (i.e. octaves)
if (adjustedAccids.count(accidSpace.at(i)) == 0) {
alignmentReference->AdjustAccidWithAccidSpace(accidSpace.at(i), m_doc, staffSize, adjustedAccids);
if (m_adjustedAccids.count(accids.at(i)) == 0) {
this->AdjustAccidWithSpace(accids.at(i), alignmentReference, staffSize);
}

// Break with odd number of elements once the middle is reached
if (i == j) break;

// bottom one - but skip if already adjusted
if (adjustedAccids.count(accidSpace.at(j)) == 0) {
alignmentReference->AdjustAccidWithAccidSpace(accidSpace.at(j), m_doc, staffSize, adjustedAccids);
if (m_adjustedAccids.count(accids.at(j)) == 0) {
this->AdjustAccidWithSpace(accids.at(j), alignmentReference, staffSize);
}
}

Expand All @@ -119,4 +122,62 @@ FunctorCode AdjustAccidXFunctor::VisitMeasure(Measure *measure)
return FUNCTOR_CONTINUE;
}

std::vector<Accid *> AdjustAccidXFunctor::GetAccidentalsForAdjustment(AlignmentReference *alignmentReference) const
{
std::vector<Accid *> accidentals;
for (Object *child : alignmentReference->GetChildren()) {
if (child->Is(ACCID)) {
Accid *accid = vrv_cast<Accid *>(child);
if (accid->HasAccid()) accidentals.push_back(accid);
}
}
return accidentals;
}

void AdjustAccidXFunctor::SetAccidLayerAlignment(Accid *accid, const AlignmentReference *alignmentReference) const
{
if (accid->IsAlignedWithSameLayer()) return;

const ArrayOfConstObjects &children = alignmentReference->GetChildren();
Note *parentNote = vrv_cast<Note *>(accid->GetFirstAncestor(NOTE));
const bool hasUnisonOverlap = std::any_of(children.begin(), children.end(), [parentNote](const Object *object) {
if (!object->Is(NOTE)) return false;
const Note *otherNote = vrv_cast<const Note *>(object);
// in case notes are in unison but have different accidentals
return parentNote && parentNote->IsUnisonWith(otherNote, true) && !parentNote->IsUnisonWith(otherNote, false);
});

if (!hasUnisonOverlap) return;

Chord *chord = parentNote->IsChordTone();
// no chord, so align only parent note
if (!chord) {
accid->IsAlignedWithSameLayer(true);
return;
}
// we have chord ancestor, so need to align all of its accidentals
ListOfObjects accidentals = chord->FindAllDescendantsByType(ACCID);
std::for_each(accidentals.begin(), accidentals.end(), [](Object *object) {
Accid *accid = vrv_cast<Accid *>(object);
accid->IsAlignedWithSameLayer(true);
});
}

void AdjustAccidXFunctor::AdjustAccidWithSpace(Accid *accid, AlignmentReference *alignmentReference, int staffSize)
{
std::vector<Accid *> leftAccids;
const ArrayOfObjects &children = alignmentReference->GetChildren();

// bottom one
for (Object *child : children) {
// if accidental has unison overlap, ignore elements on other layers for overlap
if (accid->IsAlignedWithSameLayer() && (accid->GetFirstAncestor(LAYER) != child->GetFirstAncestor(LAYER)))
continue;
accid->AdjustX(dynamic_cast<LayerElement *>(child), m_doc, staffSize, leftAccids, m_adjustedAccids);
}

// Mark as adjusted (even if position was not altered)
m_adjustedAccids.insert(accid);
}

} // namespace vrv
17 changes: 1 addition & 16 deletions src/calcalignmentpitchposfunctor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,22 +46,7 @@ FunctorCode CalcAlignmentPitchPosFunctor::VisitLayerElement(LayerElement *layerE
if (layerElement->Is(ACCID)) {
Accid *accid = vrv_cast<Accid *>(layerElement);
assert(accid);
Note *note = vrv_cast<Note *>(accid->GetFirstAncestor(NOTE));
// We should probably also avoid to add editorial accidentals to the accid space
// However, since they are placed above by View::DrawNote it works without avoiding it
if (note) {
if (note->HasGraceAlignment()) {
note->GetGraceAlignment()->AddToAccidSpace(accid);
}
else {
accid->GetAlignment()->AddToAccidSpace(accid);
}
}
else if (accid->GetFirstAncestor(CUSTOS)) {
accid->GetAlignment()->AddToAccidSpace(
accid); // If this is not added, the accidental is drawn an octave below the custos
}
else {
if (!accid->GetFirstAncestor(NOTE) && !accid->GetFirstAncestor(CUSTOS)) {
// do something for accid that are not children of a note - e.g., mensural?
accid->SetDrawingYRel(staffY->CalcPitchPosYRel(m_doc, accid->CalcDrawingLoc(layerY, layerElementY)));
}
Expand Down
72 changes: 0 additions & 72 deletions src/horizontalaligner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -735,18 +735,6 @@ std::pair<int, int> Alignment::GetAlignmentTopBottom() const
return { min, max };
}

void Alignment::AddToAccidSpace(Accid *accid)
{
assert(accid);

// Do not added them if no @accid (e.g., @accid.ges only)
if (!accid->HasAccid()) return;

AlignmentReference *reference = this->GetReferenceWithElement(accid);
assert(reference);
reference->AddToAccidSpace(accid);
}

int Alignment::HorizontalSpaceForDuration(
double intervalTime, int maxActualDur, double spacingLinear, double spacingNonLinear)
{
Expand Down Expand Up @@ -807,7 +795,6 @@ void AlignmentReference::Reset()
Object::Reset();
this->ResetNInteger();

m_accidSpace.clear();
m_layerCount = 0;
}

Expand Down Expand Up @@ -844,33 +831,6 @@ void AlignmentReference::AddChild(Object *child)
Modify();
}

void AlignmentReference::AddToAccidSpace(Accid *accid)
{
assert(accid);

if (std::find(m_accidSpace.begin(), m_accidSpace.end(), accid) != m_accidSpace.end()) return;

m_accidSpace.push_back(accid);
}

void AlignmentReference::AdjustAccidWithAccidSpace(
Accid *accid, const Doc *doc, int staffSize, std::set<Accid *> &adjustedAccids)
{
std::vector<Accid *> leftAccids;
const ArrayOfObjects &children = this->GetChildren();

// bottom one
for (Object *child : children) {
// if accidental has unison overlap, ignore elements on other layers for overlap
if (accid->IsAlignedWithSameLayer() && (accid->GetFirstAncestor(LAYER) != child->GetFirstAncestor(LAYER)))
continue;
accid->AdjustX(dynamic_cast<LayerElement *>(child), doc, staffSize, leftAccids, adjustedAccids);
}

// Mark as adjusted (even if position was not altered)
adjustedAccids.insert(accid);
}

bool AlignmentReference::HasAccidVerticalOverlap(const ArrayOfConstObjects &objects) const
{
for (const auto child : this->GetChildren()) {
Expand Down Expand Up @@ -898,38 +858,6 @@ bool AlignmentReference::HasCrossStaffElements() const
return false;
}

void AlignmentReference::SetAccidLayerAlignment()
{
const ArrayOfObjects &children = this->GetChildren();
for (Accid *accid : m_accidSpace) {
if (accid->IsAlignedWithSameLayer()) continue;

Note *parentNote = vrv_cast<Note *>(accid->GetFirstAncestor(NOTE));
const bool hasUnisonOverlap = std::any_of(children.begin(), children.end(), [parentNote](Object *object) {
if (!object->Is(NOTE)) return false;
Note *otherNote = vrv_cast<Note *>(object);
// in case notes are in unison but have different accidentals
return parentNote && parentNote->IsUnisonWith(otherNote, true)
&& !parentNote->IsUnisonWith(otherNote, false);
});

if (!hasUnisonOverlap) continue;

Chord *chord = vrv_cast<Chord *>(accid->GetFirstAncestor(CHORD));
// no chord, so align only parent note
if (!chord) {
accid->IsAlignedWithSameLayer(true);
continue;
}
// we have chord ancestor, so need to align all of its accidentals
ListOfObjects accidentals = chord->FindAllDescendantsByType(ACCID);
std::for_each(accidentals.begin(), accidentals.end(), [](Object *object) {
Accid *accid = vrv_cast<Accid *>(object);
accid->IsAlignedWithSameLayer(true);
});
}
}

FunctorCode AlignmentReference::Accept(Functor &functor)
{
return functor.VisitAlignmentReference(this);
Expand Down

0 comments on commit 1eca927

Please sign in to comment.