Skip to content
Open
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
157 changes: 127 additions & 30 deletions src/engraving/dom/shadownote.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,51 +132,146 @@ bool ShadowNote::computeUp() const
}
}

// draw articulations, laying them out in a fashion similar to
// ChordLayout::layoutArticulations
void ShadowNote::drawArticulations(Painter* painter) const
{
// Approximate bounding box of note, including stem, relative to notehead
double noteheadWidth = symWidth(m_noteheadSymbol);
double noteheadHeight = symHeight(m_noteheadSymbol);
double ms = spatium();
double x1 = noteheadWidth * .5 - (ms * mag());
double x2 = x1 + 2 * ms * mag();
ms *= .5;
double y1 = -ms * (m_lineIndex) + m_segmentSkylineTopY;
double y2 = -ms * (m_lineIndex) + m_segmentSkylineBottomY;

RectF boundRect = RectF(PointF(x1, y1), PointF(x2, y2));
bool up = !computeUp();
double y01 = -noteheadHeight * .5;
double y02 = -y01;
if (hasStem()) {
// Stem set at 3.5 spatium in SingleDraw::draw(const ShadowNote* item ...
if (up) {
y02 = ms * 3.5;
} else {
y01 = -ms * 3.5;
}
}
RectF boundRect = RectF(PointF(x1, y01), PointF(x2, y02));

// Place articulations kept close to the note first, near the notehead
// Articulations close to the note are defined in Articulation::layoutCloseToNote
bool wasStaccato = false; // keep track for special accent-staccato kerning
SymId prevArticulation = mu::engraving::SymId::noSym;
for (const SymId& artic: m_articulationIds) {
bool isMarcato = Articulation::symId2ArticulationName(artic).contains(u"marcato");
String articName = Articulation::symId2ArticulationName(artic);
if (articName == u"staccato") {
wasStaccato = true;
drawCloseArticulation(painter, artic, boundRect, up, prevArticulation);
prevArticulation = artic;
} else {
if (articName == u"tenuto") {
drawCloseArticulation(painter, artic, boundRect, up, prevArticulation);
prevArticulation = artic;
}
wasStaccato = false;
}
}

// Adjust bounding box to include the whole of the staff segment,
// then place articulations not closeToNote
ms *= .5;
// double y1 = -ms * (m_lineIndex) + m_segmentSkylineTopY;
// double y2 = -ms * (m_lineIndex) + m_segmentSkylineBottomY;
double y1 = -m_lineIndex * ms;
double y2 = (this->staffType()->bottomLine() - m_lineIndex) * ms;
if (boundRect.y() > y1) {
boundRect.setTop(y1);
if (up) {
wasStaccato = false;
}
}
if (boundRect.bottom() < y2) {
boundRect.setBottom(y2);
if (!up) {
wasStaccato = false;
}
}

if (isMarcato) {
drawMarcato(painter, artic, boundRect);
for (const SymId& artic: m_articulationIds) {
String articName = Articulation::symId2ArticulationName(artic);
if (articName == u"staccato" || articName == u"tenuto") {
continue;
} else {
drawArticulation(painter, artic, boundRect);
if (articName.contains(u"marcato")) {
// Marcato always above staff
drawFarArticulation(painter, artic, boundRect, true, false);
} else if (articName == u"accent") {
// Accents get special kerning if adjacent to staccato
drawFarArticulation(painter, artic, boundRect, up, wasStaccato);
} else {
// Other articulations (e.g., laissez-vibrer)
drawFarArticulation(painter, artic, boundRect, up, false);
}
wasStaccato = false;
}
}
}

void ShadowNote::drawMarcato(Painter* painter, const SymId& artic, RectF& boundRect) const
void ShadowNote::drawCloseArticulation(muse::draw::Painter* painter, const SymId& artic, RectF& boundRect, bool up,
const SymId& prevArticulation) const
{
PointF coord;
double spacing = spatium();

double topY = boundRect.y();
if (topY > 0) {
topY = 0;
SymId articSym = artic;
double articHeight = symHeight(articSym);

// Center the articulation on the note-head
coord.rx() = boundRect.x() + (boundRect.width() - symWidth(artic)) / 2;

// Place articulation above or below boundRect and adjust boundRect accordingly
double minDist = 0.4 * spacing;
if (up) {
// If operating inside the staff, center articulation between lines
if (m_lineIndex > 1) {
coord.ry() = (articHeight - spacing * (3 - (m_lineIndex & 1))) / 2;
if (prevArticulation != mu::engraving::SymId::noSym) {
if (m_lineIndex > 2) {
coord.ry() -= spacing;
} else {
coord.ry() -= minDist + symHeight(prevArticulation);
}
}
} else {
double topY = boundRect.y();
coord.ry() = topY - minDist;
}
boundRect.setTop(coord.ry() - articHeight);
} else {
if (m_lineIndex < this->staffType()->bottomLine() - 1) {
coord.ry() = (articHeight + spacing * (3 - (m_lineIndex & 1))) / 2;
if (prevArticulation != mu::engraving::SymId::noSym) {
if (m_lineIndex < this->staffType()->bottomLine() - 2) {
coord.ry() += spacing;
} else {
coord.ry() += minDist + symHeight(prevArticulation);
}
}
} else {
double bottomY = boundRect.bottom();
coord.ry() = bottomY + minDist + articHeight;
}
boundRect.setBottom(coord.ry());
}
coord.ry() = topY - symHeight(artic);

boundRect.setTop(boundRect.y() - symHeight(artic) - spacing);
drawSymbol(artic, painter, coord);
drawSymbol(articSym, painter, coord);
}

void ShadowNote::drawArticulation(Painter* painter, const SymId& artic, RectF& boundRect) const
void ShadowNote::drawFarArticulation(muse::draw::Painter* painter, const SymId& artic, RectF& boundRect, bool up,
bool accentStaccatoKern) const
{
PointF coord;
double spacing = spatium();
SymId articSym = artic;
double articHeight = symHeight(articSym);

bool up = !computeUp();
// If a LaissezVibrer, place beside the note and widen the bounding box
if (articSym == SymId::articLaissezVibrerAbove || articSym == SymId::articLaissezVibrerBelow) {
articSym = up ? SymId::articLaissezVibrerAbove : SymId::articLaissezVibrerBelow;
const int upDir = up ? -1 : 1;
Expand All @@ -189,20 +284,22 @@ void ShadowNote::drawArticulation(Painter* painter, const SymId& artic, RectF& b
coord.ry() = (noteHeight / 2 + 0.2 * spacing) * upDir;
boundRect.setWidth(boundRect.width() + symWidth(articSym));
} else {
// If on the stem side of a note (e.g., marcato on a low note), move artic over for stem,
// else, center it on the notehead;
coord.rx() = boundRect.x() + (boundRect.width() - symWidth(artic)) / 2;
if (up == computeUp() && hasStem()) {
coord.rx() += 0.25 * symWidth(m_noteheadSymbol);
}

// Place articulation above or below boundRect and adjust boundRect accordingly
if (up) {
double topY = boundRect.y();
if (topY > 0) {
topY = 0;
}
coord.ry() = topY - symHeight(articSym);
boundRect.setTop(topY - symHeight(articSym) - spacing);
coord.ry() = topY - (accentStaccatoKern ? 0.2 : 0.4) * spacing;
boundRect.setTop(coord.ry() - articHeight);
} else {
double bottomY = boundRect.bottomLeft().y();
if (bottomY < 0) {
bottomY = symHeight(m_noteheadSymbol);
}
coord.ry() = bottomY + symHeight(articSym);
boundRect.setHeight(bottomY + symHeight(articSym) + spacing);
double bottomY = boundRect.bottom();
coord.ry() = bottomY + (accentStaccatoKern ? 0.2 : 0.4) * spacing + articHeight;
boundRect.setBottom(coord.ry());
}
}

Expand Down
6 changes: 4 additions & 2 deletions src/engraving/dom/shadownote.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,10 @@ class ShadowNote final : public EngravingItem
AccidentalType accidentalType = AccidentalType::NONE, const std::set<SymId>& articulationIds = {});

void drawArticulations(muse::draw::Painter* painter) const;
void drawMarcato(muse::draw::Painter* painter, const SymId& articulation, RectF& boundRect) const;
void drawArticulation(muse::draw::Painter* painter, const SymId& articulation, RectF& boundRect) const;
void drawCloseArticulation(muse::draw::Painter* painter, const SymId& articulation, RectF& boundRect, bool up,
const SymId& prevArticulation) const;
void drawFarArticulation(muse::draw::Painter* painter, const SymId& articulation, RectF& boundRect, bool up,
bool accentStaccatoKern) const;

bool computeUp() const;
SymId noteheadSymbol() const { return m_noteheadSymbol; }
Expand Down
Loading