Skip to content

Commit

Permalink
Merge pull request rism-digital#3962 from rism-digital/develop-mensural
Browse files Browse the repository at this point in the history
Render oblique ligatures with curve shapes
  • Loading branch information
ahankinson authored Feb 24, 2025
2 parents 081ada1 + ee1cfff commit f094ea4
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 23 deletions.
1 change: 1 addition & 0 deletions include/vrv/bboxdevicecontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class BBoxDeviceContext : public DeviceContext {
void DrawQuadBezierPath(Point bezier[3]) override;
void DrawCubicBezierPath(Point bezier[4]) override;
void DrawCubicBezierPathFilled(Point bezier1[4], Point bezier2[4]) override;
void DrawBentParallelogramFilled(Point side[4], int height) override;
void DrawCircle(int x, int y, int radius) override;
void DrawEllipse(int x, int y, int width, int height) override;
void DrawEllipticArc(int x, int y, int width, int height, double start, double end) override;
Expand Down
1 change: 1 addition & 0 deletions include/vrv/devicecontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ class DeviceContext {
virtual void DrawQuadBezierPath(Point bezier[3]) = 0;
virtual void DrawCubicBezierPath(Point bezier[4]) = 0;
virtual void DrawCubicBezierPathFilled(Point bezier1[4], Point bezier2[4]) = 0;
virtual void DrawBentParallelogramFilled(Point side[4], int height) = 0;
virtual void DrawCircle(int x, int y, int radius) = 0;
virtual void DrawEllipse(int x, int y, int width, int height) = 0;
virtual void DrawEllipticArc(int x, int y, int width, int height, double start, double end) = 0;
Expand Down
4 changes: 4 additions & 0 deletions include/vrv/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ enum option_FOOTER { FOOTER_none = 0, FOOTER_auto, FOOTER_encoded, FOOTER_always

enum option_HEADER { HEADER_none = 0, HEADER_auto, HEADER_encoded };

enum option_LIGATURE_OBL { LIGATURE_OBL_auto = 0, LIGATURE_OBL_straight, LIGATURE_OBL_curved };

enum option_MULTIRESTSTYLE {
MULTIRESTSTYLE_auto = 0,
MULTIRESTSTYLE_default,
Expand Down Expand Up @@ -148,6 +150,7 @@ class Option {
static const std::map<int, std::string> s_fontFallback;
static const std::map<int, std::string> s_footer;
static const std::map<int, std::string> s_header;
static const std::map<int, std::string> s_ligatureOblique;
static const std::map<int, std::string> s_multiRestStyle;
static const std::map<int, std::string> s_pedalStyle;
static const std::map<int, std::string> s_systemDivider;
Expand Down Expand Up @@ -845,6 +848,7 @@ class Options {

OptionIntMap m_durationEquivalence;
OptionBool m_ligatureAsBracket;
OptionIntMap m_ligatureOblique;
OptionBool m_mensuralScoreUp;
OptionBool m_mensuralResponsiveView;
OptionBool m_mensuralToCmn;
Expand Down
1 change: 1 addition & 0 deletions include/vrv/svgdevicecontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class SvgDeviceContext : public DeviceContext {
void DrawQuadBezierPath(Point bezier[3]) override;
void DrawCubicBezierPath(Point bezier[4]) override;
void DrawCubicBezierPathFilled(Point bezier1[4], Point bezier2[4]) override;
void DrawBentParallelogramFilled(Point side[4], int height) override;
void DrawCircle(int x, int y, int radius) override;
void DrawEllipse(int x, int y, int width, int height) override;
void DrawEllipticArc(int x, int y, int width, int height, double start, double end) override;
Expand Down
2 changes: 1 addition & 1 deletion include/vrv/view.h
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ class View {
void CalcBrevisPoints(
Note *note, Staff *staff, Point *topLeft, Point *bottomRight, int sides[4], int shape, bool isMensuralBlack);
void CalcObliquePoints(Note *note1, Note *note2, Staff *staff, Point points[4], int sides[4], int shape,
bool isMensuralBlack, bool firstHalf);
bool isMensuralBlack, bool firstHalf, bool straight);

/**
* Internal methods for drawing a BeamSegment
Expand Down
5 changes: 5 additions & 0 deletions src/bboxdevicecontext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@ void BBoxDeviceContext::DrawCubicBezierPathFilled(Point bezier1[4], Point bezier
this->UpdateBB(pos.x, pos.y, pos.x + width, pos.y + height);
}

void BBoxDeviceContext::DrawBentParallelogramFilled(Point side[4], int height)
{
this->UpdateBB(side[0].x, side[0].y, side[3].x, side[3].y + height);
}

void BBoxDeviceContext::DrawCircle(int x, int y, int radius)
{
this->DrawEllipse(x - radius, y - radius, 2 * radius, 2 * radius);
Expand Down
7 changes: 7 additions & 0 deletions src/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ const std::map<int, std::string> Option::s_footer
const std::map<int, std::string> Option::s_header
= { { HEADER_none, "none" }, { HEADER_auto, "auto" }, { HEADER_encoded, "encoded" } };

const std::map<int, std::string> Option::s_ligatureOblique
= { { LIGATURE_OBL_auto, "auto" }, { LIGATURE_OBL_straight, "straight" }, { LIGATURE_OBL_curved, "curved" } };

const std::map<int, std::string> Option::s_multiRestStyle = { { MULTIRESTSTYLE_auto, "auto" },
{ MULTIRESTSTYLE_default, "default" }, { MULTIRESTSTYLE_block, "block" }, { MULTIRESTSTYLE_symbols, "symbols" } };

Expand Down Expand Up @@ -1832,6 +1835,10 @@ Options::Options()
m_ligatureAsBracket.Init(false);
this->Register(&m_ligatureAsBracket, "ligatureAsBracket", &m_mensural);

m_ligatureOblique.SetInfo("Ligature oblique", "Ligature oblique shape");
m_ligatureOblique.Init(LIGATURE_OBL_auto, &Option::s_ligatureOblique);
this->Register(&m_ligatureOblique, "ligatureOblique", &m_mensural);

m_mensuralResponsiveView.SetInfo(
"Mensural reduced view", "Convert mensural content to a more responsive view reduced to the seleceted markup");
m_mensuralResponsiveView.Init(false);
Expand Down
13 changes: 13 additions & 0 deletions src/svgdevicecontext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,19 @@ void SvgDeviceContext::DrawCubicBezierPathFilled(Point bezier1[4], Point bezier2
pathChild.append_attribute("stroke-width") = m_penStack.top().GetWidth();
}

void SvgDeviceContext::DrawBentParallelogramFilled(Point side[4], int height)
{
pugi::xml_node pathChild = AddChild("path");
pathChild.append_attribute("d") = StringFormat("M%d,%d C%d,%d %d,%d %d,%d L%d,%d C%d,%d %d,%d %d,%d Z", side[0].x,
side[0].y, side[1].x, side[1].y, side[2].x, side[2].y, side[3].x, side[3].y, side[3].x, side[3].y + height,
side[2].x, side[2].y + height, side[1].x, side[1].y + height, side[0].x, side[0].y + height)
.c_str();
pathChild.append_attribute("stroke") = this->GetColor(m_penStack.top().GetColor()).c_str();
pathChild.append_attribute("stroke-linecap") = "round";
pathChild.append_attribute("stroke-linejoin") = "round";
pathChild.append_attribute("stroke-width") = m_penStack.top().GetWidth();
}

void SvgDeviceContext::DrawCircle(int x, int y, int radius)
{
this->DrawEllipse(x - radius, y - radius, 2 * radius, 2 * radius);
Expand Down
80 changes: 58 additions & 22 deletions src/view_mensural.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,11 +350,17 @@ void View::DrawLigatureNote(DeviceContext *dc, LayerElement *element, Layer *lay
bool oblique = ((shape & LIGATURE_OBLIQUE) || (prevShape & LIGATURE_OBLIQUE));
bool obliqueEnd = (prevShape & LIGATURE_OBLIQUE);
bool stackedEnd = (shape & LIGATURE_STACKED);

int stemWidth = m_doc->GetDrawingStemWidth(staff->m_drawingStaffSize);
int strokeWidth = 2.8 * stemWidth;
/** end code duplicated */

bool straight = true;
switch (m_doc->GetOptions()->m_ligatureOblique.GetValue()) {
case LIGATURE_OBL_auto: straight = !isMensuralBlack; break;
case LIGATURE_OBL_straight: straight = true; break;
case LIGATURE_OBL_curved: straight = false; break;
}

Point points[4];
Point *topLeft = &points[0];
Point *bottomLeft = &points[1];
Expand All @@ -372,24 +378,52 @@ void View::DrawLigatureNote(DeviceContext *dc, LayerElement *element, Layer *lay
// First half of the oblique - checking the nextNote is there just in case, but is should
if ((shape & LIGATURE_OBLIQUE) && nextNote) {
// return;
CalcObliquePoints(note, nextNote, staff, points, sides, shape, isMensuralBlack, true);
CalcObliquePoints(note, nextNote, staff, points, sides, shape, isMensuralBlack, true, straight);
}
// Second half of the oblique - checking the prevNote is there just in case, but is should
else if ((prevShape & LIGATURE_OBLIQUE) && prevNote) {
CalcObliquePoints(prevNote, note, staff, points, sides, prevShape, isMensuralBlack, false);
CalcObliquePoints(prevNote, note, staff, points, sides, prevShape, isMensuralBlack, false, straight);
}
else {
assert(false);
}
}

if (!fillNotehead) {
// double the bases of rectangles
this->DrawObliquePolygon(dc, topLeft->x, topLeft->y, topRight->x, topRight->y, -strokeWidth);
this->DrawObliquePolygon(dc, bottomLeft->x, bottomLeft->y, bottomRight->x, bottomRight->y, strokeWidth);
// Oblique polygons
if (straight) {
if (!fillNotehead) {
this->DrawObliquePolygon(dc, topLeft->x, topLeft->y, topRight->x, topRight->y, -strokeWidth);
this->DrawObliquePolygon(dc, bottomLeft->x, bottomLeft->y, bottomRight->x, bottomRight->y, strokeWidth);
}
else {
this->DrawObliquePolygon(dc, topLeft->x, topLeft->y, topRight->x, topRight->y, bottomLeft->y - topLeft->y);
}
}
// Bent parallelograms
else {
this->DrawObliquePolygon(dc, topLeft->x, topLeft->y, topRight->x, topRight->y, bottomLeft->y - topLeft->y);
const int thickness = topLeft->y - bottomLeft->y;
// The curved side points (two ends and two control points)
Point curvedSide[4];
curvedSide[0] = ToDeviceContext(*topLeft);
curvedSide[3] = ToDeviceContext(*topRight);
//
const int width = (curvedSide[3].x - curvedSide[0].x);
const int height = (curvedSide[3].y - curvedSide[0].y);
curvedSide[1] = curvedSide[3];
curvedSide[1].x -= (width * 0.7);
curvedSide[1].y -= (height * 0.7) + (height * 0.07);
curvedSide[2] = curvedSide[3];
curvedSide[2].x -= (width * 0.3);
curvedSide[2].y -= (height * 0.3) + (height * 0.07);

if (!fillNotehead) {
dc->DrawBentParallelogramFilled(curvedSide, strokeWidth);
for (Point &point : curvedSide) point.y += thickness - strokeWidth;
dc->DrawBentParallelogramFilled(curvedSide, strokeWidth);
}
else {
dc->DrawBentParallelogramFilled(curvedSide, thickness);
}
}

// Do not draw a left connector with obliques
Expand Down Expand Up @@ -617,13 +651,17 @@ void View::CalcBrevisPoints(
}

void View::CalcObliquePoints(Note *note1, Note *note2, Staff *staff, Point points[4], int sides[4], int shape,
bool isMensuralBlack, bool firstHalf)
bool isMensuralBlack, bool firstHalf, bool straight)
{
assert(note1);
assert(note2);
assert(staff);

const int stemWidth = m_doc->GetDrawingStemWidth(staff->m_drawingStaffSize);
const int noteDiff = note1->PitchDifferenceTo(note2);

// Adjustment for end points according to the note diff
const int yAdjust = noteDiff * stemWidth / 5;

Point *topLeft = &points[0];
Point *bottomLeft = &points[1];
Expand All @@ -648,35 +686,33 @@ void View::CalcObliquePoints(Note *note1, Note *note2, Staff *staff, Point point
sides[3] = sides2[3];

// With oblique it is best visually to move them up / down - more with (white) ligatures with serif
double adjustmentFactor = (isMensuralBlack) ? 0.5 : 1.8;
// double adjustmentFactor = (isMensuralBlack) ? 2.5 : 1.8;
double slope = 0.0;
if (bottomRight->x != bottomLeft->x)
slope = (double)(bottomRight->y - bottomLeft->y) / (double)(bottomRight->x - bottomLeft->x);
int adjustment = (int)(slope * stemWidth) * adjustmentFactor;
topLeft->y -= adjustment;
bottomLeft->y -= adjustment;
topRight->y += adjustment;
bottomRight->y += adjustment;

slope = 0.0;
// recalculate slope after adjustment
if (bottomRight->x != bottomLeft->x)
slope = (double)(bottomRight->y - bottomLeft->y) / (double)(bottomRight->x - bottomLeft->x);

int length = (bottomRight->x - bottomLeft->x) / 2;
if (!straight) slope *= 0.85;

if (firstHalf) {
// make sure there are some pixels of overlap
length += 10;
// make sure there is one pixel of overlap
length += 1;
bottomRight->x = bottomLeft->x + length;
topRight->x = bottomRight->x;
bottomRight->y = bottomLeft->y + (int)(length * slope);
topRight->y = topLeft->y + (int)(length * slope);
//
topLeft->y += yAdjust;
bottomLeft->y += yAdjust;
}
else {
bottomLeft->x = bottomLeft->x + length;
topLeft->x = bottomLeft->x;
bottomLeft->y = bottomLeft->y + (int)(length * slope);
topLeft->y = topLeft->y + (int)(length * slope);
//
topRight->y -= yAdjust;
bottomRight->y -= yAdjust;
}
}

Expand Down

0 comments on commit f094ea4

Please sign in to comment.