diff --git a/include/vrv/devicecontext.h b/include/vrv/devicecontext.h index bf13577346..6edc296b83 100644 --- a/include/vrv/devicecontext.h +++ b/include/vrv/devicecontext.h @@ -271,6 +271,11 @@ class DeviceContext { */ virtual void SetCustomGraphicColor(const std::string &color) {} + /** + * Method for adding custom graphic data-* attributes + */ + virtual void SetCustomGraphicAttributes(const std::string &data, const std::string &value) {} + /** * @name Methods for re-starting and ending a graphic for objects drawn in separate steps * The methods can be used to the output together, for example for a Beam diff --git a/include/vrv/facsimile.h b/include/vrv/facsimile.h index 00f86a29c7..0a7e401a55 100644 --- a/include/vrv/facsimile.h +++ b/include/vrv/facsimile.h @@ -47,6 +47,25 @@ class Facsimile : public Object, public AttTyped { const Zone *FindZoneByID(const std::string &zoneId) const; int GetMaxX() const; int GetMaxY() const; + + //----------// + // Functors // + //----------// + + /** + * Interface for class functor visitation + */ + ///@{ + FunctorCode Accept(Functor &functor) override; + FunctorCode Accept(ConstFunctor &functor) const override; + FunctorCode AcceptEnd(Functor &functor) override; + FunctorCode AcceptEnd(ConstFunctor &functor) const override; + ///@} + +protected: + // +private: + // }; } // namespace vrv diff --git a/include/vrv/facsimilefunctor.h b/include/vrv/facsimilefunctor.h index 7ccf8f3e77..b52811a9e5 100644 --- a/include/vrv/facsimilefunctor.h +++ b/include/vrv/facsimilefunctor.h @@ -75,6 +75,11 @@ class SyncFromFacsimileFunctor : public Functor { Measure *m_currentNeumeLine; /** map to store the zone corresponding to a staff */ std::map m_staffZones; + // + int m_pageMarginTop; + int m_pageMarginLeft; + // + double m_ppuFactor; }; //---------------------------------------------------------------------------- @@ -90,14 +95,14 @@ class SyncToFacsimileFunctor : public Functor { * @name Constructors, destructors */ ///@{ - SyncToFacsimileFunctor(Doc *doc); + SyncToFacsimileFunctor(Doc *doc, double ppuFactor); virtual ~SyncToFacsimileFunctor() = default; ///@} /* * Abstract base implementation */ - bool ImplementsEndInterface() const override { return false; } + bool ImplementsEndInterface() const override { return true; } /* * Functor interface @@ -106,6 +111,7 @@ class SyncToFacsimileFunctor : public Functor { FunctorCode VisitLayerElement(LayerElement *layerElement) override; FunctorCode VisitMeasure(Measure *measure) override; FunctorCode VisitPage(Page *page) override; + FunctorCode VisitPageEnd(Page *page) override; FunctorCode VisitPb(Pb *pb) override; FunctorCode VisitSb(Sb *sb) override; FunctorCode VisitStaff(Staff *staff) override; @@ -133,6 +139,10 @@ class SyncToFacsimileFunctor : public Functor { // int m_pageMarginTop; int m_pageMarginLeft; + // A flag indicating we are dealing with a neume line + bool m_currentNeumeLine; + // + double m_ppuFactor; }; } // namespace vrv diff --git a/include/vrv/functorinterface.h b/include/vrv/functorinterface.h index 1ee9384bf6..f24743f583 100644 --- a/include/vrv/functorinterface.h +++ b/include/vrv/functorinterface.h @@ -41,6 +41,7 @@ class EditorialElement; class Ending; class Expansion; class F; +class Facsimile; class Fb; class Fermata; class Fing; @@ -52,6 +53,7 @@ class GenericLayerElement; class Gliss; class GraceAligner; class GraceGrp; +class Graphic; class GrpSym; class Hairpin; class HalfmRpt; @@ -118,6 +120,7 @@ class StaffAlignment; class StaffDef; class StaffGrp; class Stem; +class Surface; class Svg; class Syl; class Syllable; @@ -142,6 +145,7 @@ class TupletBracket; class TupletNum; class Turn; class Verse; +class Zone; //---------------------------------------------------------------------------- // FunctorInterface @@ -458,6 +462,20 @@ class FunctorInterface { virtual FunctorCode VisitTextElementEnd(TextElement *textElement); ///@} + /** + * @name Visit facsimle elements + */ + ///@{ + virtual FunctorCode VisitFacsimile(Facsimile *facsimile); + virtual FunctorCode VisitFacsimileEnd(Facsimile *facsimile); + virtual FunctorCode VisitGraphic(Graphic *graphic); + virtual FunctorCode VisitGraphicEnd(Graphic *graphic); + virtual FunctorCode VisitSurface(Surface *surface); + virtual FunctorCode VisitSurfaceEnd(Surface *surface); + virtual FunctorCode VisitZone(Zone *zone); + virtual FunctorCode VisitZoneEnd(Zone *zone); + ///@} + /** * @name Visit horizontal aligners */ @@ -817,6 +835,20 @@ class ConstFunctorInterface { virtual FunctorCode VisitTextElementEnd(const TextElement *textElement); ///@} + /** + * @name Visit facsimle elements + */ + ///@{ + virtual FunctorCode VisitFacsimile(const Facsimile *facsimile); + virtual FunctorCode VisitFacsimileEnd(const Facsimile *facsimile); + virtual FunctorCode VisitGraphic(const Graphic *graphic); + virtual FunctorCode VisitGraphicEnd(const Graphic *graphic); + virtual FunctorCode VisitSurface(const Surface *surface); + virtual FunctorCode VisitSurfaceEnd(const Surface *surface); + virtual FunctorCode VisitZone(const Zone *zone); + virtual FunctorCode VisitZoneEnd(const Zone *zone); + ///@} + /** * @name Visit horizontal aligners */ diff --git a/include/vrv/graphic.h b/include/vrv/graphic.h index 7e1b263e68..4bf765c7dc 100644 --- a/include/vrv/graphic.h +++ b/include/vrv/graphic.h @@ -48,6 +48,20 @@ class Graphic : public Object, public AttPointing, public AttWidth, public AttHe int GetDrawingHeight(int unit, int staffSize) const; ///@} + //----------// + // Functors // + //----------// + + /** + * Interface for class functor visitation + */ + ///@{ + FunctorCode Accept(Functor &functor) override; + FunctorCode Accept(ConstFunctor &functor) const override; + FunctorCode AcceptEnd(Functor &functor) override; + FunctorCode AcceptEnd(ConstFunctor &functor) const override; + ///@} + protected: // private: diff --git a/include/vrv/miscfunctor.h b/include/vrv/miscfunctor.h index fbfe04cfc3..c14c997e0a 100644 --- a/include/vrv/miscfunctor.h +++ b/include/vrv/miscfunctor.h @@ -25,7 +25,7 @@ class ApplyPPUFactorFunctor : public Functor { * @name Constructors, destructors */ ///@{ - ApplyPPUFactorFunctor(); + ApplyPPUFactorFunctor(Page *page = NULL); virtual ~ApplyPPUFactorFunctor() = default; ///@} @@ -42,7 +42,9 @@ class ApplyPPUFactorFunctor : public Functor { FunctorCode VisitMeasure(Measure *measure) override; FunctorCode VisitPage(Page *page) override; FunctorCode VisitStaff(Staff *staff) override; + FunctorCode VisitSurface(Surface *surface) override; FunctorCode VisitSystem(System *system) override; + FunctorCode VisitZone(Zone *zone) override; ///@} protected: diff --git a/include/vrv/staff.h b/include/vrv/staff.h index d49d48f0c8..41db500ec8 100644 --- a/include/vrv/staff.h +++ b/include/vrv/staff.h @@ -43,7 +43,34 @@ class LedgerLine { * Add a dash to the ledger line object. * If necessary merges overlapping dashes. */ - void AddDash(int left, int right, int extension); + void AddDash(int left, int right, int extension, const Object *event); + + class Dash { + public: + int m_x1; + int m_x2; + ListOfConstObjects m_events; + + // Constructor + Dash(int x1, int x2, const Object *object) + { + m_x1 = x1; + m_x2 = x2; + m_events.push_back(object); + } + + // Merge function to merge another Dash object into this one + void MergeWith(const Dash &other) + { + // Keep the first int from this Dash object, and the second int from the other + this->m_x1 = std::min(other.m_x1, this->m_x1); + this->m_x2 = std::max(other.m_x2, this->m_x2); + // Append the list from other to this + if (!other.m_events.empty()) { + this->m_events.insert(this->m_events.end(), other.m_events.begin(), other.m_events.end()); + } + } + }; protected: // @@ -53,7 +80,8 @@ class LedgerLine { /** * A list of dashes relative to the staff position. */ - std::list> m_dashes; + // std::list> m_dashes; + std::list m_dashes; protected: // @@ -207,8 +235,8 @@ class Staff : public Object, * If necessary creates the ledger line array. */ ///@{ - void AddLedgerLineAbove(int count, int left, int right, int extension, bool cueSize); - void AddLedgerLineBelow(int count, int left, int right, int extension, bool cueSize); + void AddLedgerLineAbove(int count, int left, int right, int extension, bool cueSize, const Object *event); + void AddLedgerLineBelow(int count, int left, int right, int extension, bool cueSize, const Object *event); ///@} /** @@ -248,7 +276,7 @@ class Staff : public Object, /** * Add the ledger line dashes to the legderline array. */ - void AddLedgerLines(ArrayOfLedgerLines &lines, int count, int left, int right, int extension); + void AddLedgerLines(ArrayOfLedgerLines &lines, int count, int left, int right, int extension, const Object *event); public: /** diff --git a/include/vrv/surface.h b/include/vrv/surface.h index f6cc1eb8ee..046c26c66d 100644 --- a/include/vrv/surface.h +++ b/include/vrv/surface.h @@ -44,6 +44,20 @@ class Surface : public Object, public AttTyped, public AttCoordinated, public At int GetMaxX() const; int GetMaxY() const; + //----------// + // Functors // + //----------// + + /** + * Interface for class functor visitation + */ + ///@{ + FunctorCode Accept(Functor &functor) override; + FunctorCode Accept(ConstFunctor &functor) const override; + FunctorCode AcceptEnd(Functor &functor) override; + FunctorCode AcceptEnd(ConstFunctor &functor) const override; + ///@} + protected: // private: diff --git a/include/vrv/svgdevicecontext.h b/include/vrv/svgdevicecontext.h index 0139e26ed8..2b35ee72f5 100644 --- a/include/vrv/svgdevicecontext.h +++ b/include/vrv/svgdevicecontext.h @@ -131,7 +131,12 @@ class SvgDeviceContext : public DeviceContext { /** * Method for changing the color of a custom graphic */ - virtual void SetCustomGraphicColor(const std::string &color) override; + void SetCustomGraphicColor(const std::string &color) override; + + /** + * Method for adding custom graphic data-* attributes + */ + void SetCustomGraphicAttributes(const std::string &data, const std::string &value) override; /** * @name Methods for re-starting and ending a graphic for objects drawn in separate steps diff --git a/include/vrv/zone.h b/include/vrv/zone.h index c9e3e44ebc..f8a35cb355 100644 --- a/include/vrv/zone.h +++ b/include/vrv/zone.h @@ -43,6 +43,20 @@ class Zone : public Object, public AttTyped, public AttCoordinated, public AttCo int GetLogicalUly() const; int GetLogicalLry() const; + //----------// + // Functors // + //----------// + + /** + * Interface for class functor visitation + */ + ///@{ + FunctorCode Accept(Functor &functor) override; + FunctorCode Accept(ConstFunctor &functor) const override; + FunctorCode AcceptEnd(Functor &functor) override; + FunctorCode AcceptEnd(ConstFunctor &functor) const override; + ///@} + protected: // private: diff --git a/src/calcledgerlinesfunctor.cpp b/src/calcledgerlinesfunctor.cpp index bef0616bf2..57fc206fb5 100644 --- a/src/calcledgerlinesfunctor.cpp +++ b/src/calcledgerlinesfunctor.cpp @@ -81,11 +81,12 @@ void CalcLedgerLinesFunctor::CalcForLayerElement( left -= width / 2; } + const LayerElement *event = (m_doc->GetOptions()->m_svgHtml5.GetValue()) ? layerElement : NULL; if (linesAbove > 0) { - staff->AddLedgerLineAbove(linesAbove, left, right, extension, drawingCueSize); + staff->AddLedgerLineAbove(linesAbove, left, right, extension, drawingCueSize, event); } else { - staff->AddLedgerLineBelow(linesBelow, left, right, extension, drawingCueSize); + staff->AddLedgerLineBelow(linesBelow, left, right, extension, drawingCueSize, event); } } @@ -121,15 +122,14 @@ void CalcLedgerLinesFunctor::AdjustLedgerLines( // For each dash on the inner line (both cue and normal) we construct an adjustment with zero delta // and sort them std::vector adjustments; - using DashType = std::pair; if (!lines.empty()) { - for (const DashType &dash : lines.at(0).m_dashes) { - adjustments.push_back({ dash.first, dash.second, false, 0 }); + for (const LedgerLine::Dash &dash : lines.at(0).m_dashes) { + adjustments.push_back({ dash.m_x1, dash.m_x2, false, 0 }); } } if (!cueLines.empty()) { - for (const DashType &dash : cueLines.at(0).m_dashes) { - adjustments.push_back({ dash.first, dash.second, true, 0 }); + for (const LedgerLine::Dash &dash : cueLines.at(0).m_dashes) { + adjustments.push_back({ dash.m_x1, dash.m_x2, true, 0 }); } } @@ -175,13 +175,13 @@ void CalcLedgerLinesFunctor::AdjustLedgerLines( if (adjustment.delta > 0) { ArrayOfLedgerLines &linesToAdjust = adjustment.isCue ? cueLines : lines; for (LedgerLine &line : linesToAdjust) { - std::list::iterator iterDash - = std::find_if(line.m_dashes.begin(), line.m_dashes.end(), [&adjustment](const DashType &dash) { - return ((dash.first >= adjustment.left) && (dash.second <= adjustment.right)); - }); + std::list::iterator iterDash = std::find_if( + line.m_dashes.begin(), line.m_dashes.end(), [&adjustment](const LedgerLine::Dash &dash) { + return ((dash.m_x1 >= adjustment.left) && (dash.m_x2 <= adjustment.right)); + }); if (iterDash != line.m_dashes.end()) { - iterDash->first += adjustment.delta; - iterDash->second -= adjustment.delta; + iterDash->m_x1 += adjustment.delta; + iterDash->m_x2 -= adjustment.delta; } } } diff --git a/src/doc.cpp b/src/doc.cpp index f5664d709e..0919507f34 100644 --- a/src/doc.cpp +++ b/src/doc.cpp @@ -1410,20 +1410,20 @@ void Doc::SyncFromFacsimileDoc() void Doc::SyncToFacsimileDoc() { + double ppuFactor = 1.0; // Create a new facsimile object if we do not have one already if (!this->HasFacsimile()) { Facsimile *facsimile = new Facsimile(); this->SetFacsimile(facsimile); + m_facsimile->SetType("transcription"); + // We use the scale option to determine the ppu and adjust the ppu factor accordingly + // For example, with scale 50 and the default unit, the ppu will be 4.5 (instead of 9.0) + ppuFactor = (double)m_options->m_scale.GetValue() / 100.0; } - if (!m_facsimile->FindDescendantByType(SURFACE)) { - m_facsimile->AddChild(new Surface()); - } - this->ScoreDefSetCurrentDoc(); - m_facsimile->SetType("transcription"); - m_facsimile->ClearChildren(); + this->ScoreDefSetCurrentDoc(); - SyncToFacsimileFunctor syncToFacimileFunctor(this); + SyncToFacsimileFunctor syncToFacimileFunctor(this, ppuFactor); this->Process(syncToFacimileFunctor); } @@ -2143,8 +2143,7 @@ int Doc::GetAdjustedDrawingPageHeight() const // Take into account the PPU when getting the page height in facsimile if (this->IsTranscription() || this->IsFacs()) { - const int factor = DEFINITION_FACTOR / m_drawingPage->GetPPUFactor(); - return m_drawingPage->m_pageHeight / factor; + return m_drawingPage->m_pageHeight * m_drawingPage->GetPPUFactor() / DEFINITION_FACTOR; } int contentHeight = m_drawingPage->GetContentHeight(); @@ -2157,8 +2156,7 @@ int Doc::GetAdjustedDrawingPageWidth() const // Take into account the PPU when getting the page width in facsimile if (this->IsTranscription() || this->IsFacs()) { - const int factor = DEFINITION_FACTOR / m_drawingPage->GetPPUFactor(); - return m_drawingPage->m_pageWidth / factor; + return m_drawingPage->m_pageWidth * m_drawingPage->GetPPUFactor() / DEFINITION_FACTOR; } int contentWidth = m_drawingPage->GetContentWidth(); diff --git a/src/facsimile.cpp b/src/facsimile.cpp index e3f48762f9..887a678c6c 100644 --- a/src/facsimile.cpp +++ b/src/facsimile.cpp @@ -14,6 +14,7 @@ //---------------------------------------------------------------------------- #include "comparison.h" +#include "functor.h" #include "surface.h" #include "vrv.h" #include "zone.h" @@ -80,4 +81,28 @@ int Facsimile::GetMaxY() const return max; } +//---------------------------------------------------------------------------- +// Functor methods +//---------------------------------------------------------------------------- + +FunctorCode Facsimile::Accept(Functor &functor) +{ + return functor.VisitFacsimile(this); +} + +FunctorCode Facsimile::Accept(ConstFunctor &functor) const +{ + return functor.VisitFacsimile(this); +} + +FunctorCode Facsimile::AcceptEnd(Functor &functor) +{ + return functor.VisitFacsimileEnd(this); +} + +FunctorCode Facsimile::AcceptEnd(ConstFunctor &functor) const +{ + return functor.VisitFacsimileEnd(this); +} + } // namespace vrv diff --git a/src/facsimilefunctor.cpp b/src/facsimilefunctor.cpp index f70d6804d9..d19f84bb92 100644 --- a/src/facsimilefunctor.cpp +++ b/src/facsimilefunctor.cpp @@ -36,17 +36,21 @@ SyncFromFacsimileFunctor::SyncFromFacsimileFunctor(Doc *doc) : Functor() m_view.SetDoc(doc); m_currentPage = NULL; m_currentSystem = NULL; + m_pageMarginTop = 0; + m_pageMarginLeft = 0; + m_ppuFactor = 1.0; } FunctorCode SyncFromFacsimileFunctor::VisitLayerElement(LayerElement *layerElement) { - if (!layerElement->Is({ ACCID, CLEF, CUSTOS, DIVLINE, LIQUESCENT, NC, NOTE, REST, SYL })) return FUNCTOR_CONTINUE; + if (!layerElement->Is({ ACCID, BARLINE, CLEF, CUSTOS, DIVLINE, LIQUESCENT, NC, NOTE, REST, SYL })) + return FUNCTOR_CONTINUE; Zone *zone = layerElement->GetZone(); assert(zone); - layerElement->m_drawingFacsX = m_view.ToLogicalX(zone->GetUlx() * DEFINITION_FACTOR); - if (layerElement->Is({ ACCID, SYL })) { - layerElement->m_drawingFacsY = m_view.ToLogicalY(zone->GetUly() * DEFINITION_FACTOR); + layerElement->m_drawingFacsX = m_view.ToLogicalX(zone->GetUlx() * DEFINITION_FACTOR - m_pageMarginLeft); + if (m_currentNeumeLine && layerElement->Is({ ACCID, SYL })) { + layerElement->m_drawingFacsY = m_view.ToLogicalY(zone->GetUly() * DEFINITION_FACTOR - m_pageMarginTop); } return FUNCTOR_CONTINUE; @@ -61,8 +65,8 @@ FunctorCode SyncFromFacsimileFunctor::VisitMeasure(Measure *measure) else { Zone *zone = measure->GetZone(); assert(zone); - measure->m_drawingFacsX1 = m_view.ToLogicalX(zone->GetUlx() * DEFINITION_FACTOR); - measure->m_drawingFacsX2 = m_view.ToLogicalX(zone->GetLrx() * DEFINITION_FACTOR); + measure->m_drawingFacsX1 = m_view.ToLogicalX(zone->GetUlx() * DEFINITION_FACTOR - m_pageMarginLeft); + measure->m_drawingFacsX2 = m_view.ToLogicalX(zone->GetLrx() * DEFINITION_FACTOR - m_pageMarginLeft); } return FUNCTOR_CONTINUE; @@ -80,7 +84,10 @@ FunctorCode SyncFromFacsimileFunctor::VisitPage(Page *page) FunctorCode SyncFromFacsimileFunctor::VisitPageEnd(Page *page) { // Used for adjusting staff size in neon - filled in VisitStaff - if (m_staffZones.empty()) return FUNCTOR_CONTINUE; + if (!m_staffZones.empty()) { + // Since we multiply all values by the DEFINITION_FACTOR, set it as PPU for neon facs + m_ppuFactor = DEFINITION_FACTOR; + } // The staff size is calculated based on the zone height and takes into acocunt the rotation for (auto &[staff, zone] : m_staffZones) { @@ -92,8 +99,7 @@ FunctorCode SyncFromFacsimileFunctor::VisitPageEnd(Page *page) staff->SetDrawingRotation(rotate); } - // Since we multiply all values by the DEFINITION_FACTOR, set it as PPU - m_currentPage->SetPPUFactor(DEFINITION_FACTOR); + m_currentPage->SetPPUFactor(m_ppuFactor); if (m_currentPage->GetPPUFactor() != 1.0) { ApplyPPUFactorFunctor applyPPUFactor; m_currentPage->Process(applyPPUFactor); @@ -117,12 +123,35 @@ FunctorCode SyncFromFacsimileFunctor::VisitPb(Pb *pb) if (surface && surface->HasLrx() && surface->HasLry()) { m_currentPage->m_pageHeight = surface->GetLry() * DEFINITION_FACTOR; m_currentPage->m_pageWidth = surface->GetLrx() * DEFINITION_FACTOR; + // Read the ppu factor from surface@type + std::string surfaceType = surface->GetType(); + if (surfaceType.starts_with("ppu:")) { + std::string ppuFactorStr = surfaceType.substr(surfaceType.find(":") + 1); + if (vrv::IsValidDouble(ppuFactorStr)) { + m_ppuFactor = std::strtod(ppuFactorStr.c_str(), NULL); + m_ppuFactor *= DEFINITION_FACTOR / m_doc->GetOptions()->m_unit.GetValue(); + } + } + // Read margins + if (zone && zone->HasUlx() && zone->HasUly() && zone->HasLrx() && zone->HasLry()) { + m_pageMarginTop = zone->GetUly() * DEFINITION_FACTOR; + m_pageMarginLeft = zone->GetUlx() * DEFINITION_FACTOR; + m_currentPage->m_pageMarginTop = m_pageMarginTop; + // Calculate the bottom margin looking at the surface lry and zone lry + m_currentPage->m_pageMarginBottom = m_currentPage->m_pageHeight - zone->GetLry() * DEFINITION_FACTOR; + m_currentPage->m_pageMarginLeft = m_pageMarginLeft; + // Calculate the right margin looking at the surface lrx and zone lrx + m_currentPage->m_pageMarginRight = m_currentPage->m_pageWidth - zone->GetLrx() * DEFINITION_FACTOR; + ; + m_doc->UpdatePageDrawingSizes(); + } } // Fallback on zone else { m_currentPage->m_pageHeight = zone->GetLry() * DEFINITION_FACTOR; m_currentPage->m_pageWidth = zone->GetLrx() * DEFINITION_FACTOR; } + // Update the page size to have to View::ToLogicalX/Y valid m_doc->UpdatePageDrawingSizes(); @@ -136,8 +165,8 @@ FunctorCode SyncFromFacsimileFunctor::VisitSb(Sb *sb) Zone *zone = sb->GetZone(); assert(zone); - m_currentSystem->m_drawingFacsX = m_view.ToLogicalX(zone->GetUlx() * DEFINITION_FACTOR); - m_currentSystem->m_drawingFacsY = m_view.ToLogicalY(zone->GetUly() * DEFINITION_FACTOR); + m_currentSystem->m_drawingFacsX = m_view.ToLogicalX(zone->GetUlx() * DEFINITION_FACTOR - m_pageMarginLeft); + m_currentSystem->m_drawingFacsY = m_view.ToLogicalY(zone->GetUly() * DEFINITION_FACTOR - m_pageMarginTop); return FUNCTOR_CONTINUE; } @@ -146,12 +175,12 @@ FunctorCode SyncFromFacsimileFunctor::VisitStaff(Staff *staff) { Zone *zone = staff->GetZone(); assert(zone); - staff->m_drawingFacsY = m_view.ToLogicalY(zone->GetUly() * DEFINITION_FACTOR); + staff->m_drawingFacsY = m_view.ToLogicalY(zone->GetUly() * DEFINITION_FACTOR - m_pageMarginTop); // neon specific code - set the position of the pseudo measure (neume line) if (m_currentNeumeLine) { - m_currentNeumeLine->m_drawingFacsX1 = m_view.ToLogicalX(zone->GetUlx() * DEFINITION_FACTOR); - m_currentNeumeLine->m_drawingFacsX2 = m_view.ToLogicalX(zone->GetLrx() * DEFINITION_FACTOR); + m_currentNeumeLine->m_drawingFacsX1 = m_view.ToLogicalX(zone->GetUlx() * DEFINITION_FACTOR - m_pageMarginLeft); + m_currentNeumeLine->m_drawingFacsX2 = m_view.ToLogicalX(zone->GetLrx() * DEFINITION_FACTOR - m_pageMarginLeft); m_staffZones[staff] = zone; // The staff slope is going up. The y left position needs to be adjusted accordingly @@ -177,24 +206,27 @@ FunctorCode SyncFromFacsimileFunctor::VisitSystem(System *system) // SyncToFacsimileFunctor //---------------------------------------------------------------------------- -SyncToFacsimileFunctor::SyncToFacsimileFunctor(Doc *doc) : Functor() +SyncToFacsimileFunctor::SyncToFacsimileFunctor(Doc *doc, double ppuFactor) : Functor() { m_doc = doc; + m_ppuFactor = ppuFactor; m_view.SetDoc(doc); m_surface = NULL; m_currentPage = NULL; m_currentSystem = NULL; m_pageMarginTop = 0; m_pageMarginLeft = 0; + m_currentNeumeLine = false; } FunctorCode SyncToFacsimileFunctor::VisitLayerElement(LayerElement *layerElement) { - if (!layerElement->Is({ ACCID, CLEF, CUSTOS, DIVLINE, LIQUESCENT, NC, NOTE, REST, SYL })) return FUNCTOR_CONTINUE; + if (!layerElement->Is({ ACCID, BARLINE, CLEF, CUSTOS, DIVLINE, LIQUESCENT, NC, NOTE, REST, SYL })) + return FUNCTOR_CONTINUE; Zone *zone = this->GetZone(layerElement, layerElement->GetClassName()); zone->SetUlx(m_view.ToDeviceContextX(layerElement->GetDrawingX()) / DEFINITION_FACTOR + m_pageMarginLeft); - if (layerElement->Is({ ACCID, SYL })) { + if (m_currentNeumeLine && layerElement->Is({ ACCID, SYL })) { zone->SetUly(m_view.ToDeviceContextY(layerElement->GetDrawingY()) / DEFINITION_FACTOR + m_pageMarginTop); } @@ -208,34 +240,75 @@ FunctorCode SyncToFacsimileFunctor::VisitMeasure(Measure *measure) zone->SetLrx( m_view.ToDeviceContextX(measure->GetDrawingX() + measure->GetWidth()) / DEFINITION_FACTOR + m_pageMarginLeft); + m_currentNeumeLine = measure->IsNeumeLine(); + return FUNCTOR_CONTINUE; } FunctorCode SyncToFacsimileFunctor::VisitPage(Page *page) { + // This is required since processing Pb will create or select the Surface + if (!page->FindDescendantByType(PB)) { + LogWarning("Page without skipped when synching to facsimile"); + return FUNCTOR_SIBLINGS; + } + m_currentPage = page; m_doc->SetDrawingPage(page->GetIdx()); page->LayOut(); - // - m_surface = new Surface(); - assert(m_doc->GetFacsimile()); - m_doc->GetFacsimile()->AddChild(m_surface); - m_surface->SetLrx(m_doc->m_drawingPageWidth / DEFINITION_FACTOR); - m_surface->SetLry(m_doc->m_drawingPageHeight / DEFINITION_FACTOR); - // Because the facsimile output zone positions include the margins, we will add them to each zone - m_pageMarginTop = m_doc->m_drawingPageMarginTop / DEFINITION_FACTOR; - m_pageMarginLeft = m_doc->m_drawingPageMarginLeft / DEFINITION_FACTOR; + // From a previously loaded facsimile + if (page->GetPPUFactor() != 1.0) { + m_ppuFactor = page->GetPPUFactor(); + } + // When generating a facsimile with a scale option + else { + page->SetPPUFactor(m_ppuFactor); + } + + return FUNCTOR_CONTINUE; +} + +FunctorCode SyncToFacsimileFunctor::VisitPageEnd(Page *page) +{ + if (m_ppuFactor != 1.0) { + ApplyPPUFactorFunctor applyPPUFactor(m_currentPage); + m_surface->Process(applyPPUFactor); + m_surface->SetType( + StringFormat("ppu:%f", m_ppuFactor * m_doc->GetOptions()->m_unit.GetValue() / DEFINITION_FACTOR)); + } return FUNCTOR_CONTINUE; } FunctorCode SyncToFacsimileFunctor::VisitPb(Pb *pb) { - Zone *zone = this->GetZone(pb, pb->GetClassName()); + Zone *zone = pb->GetZone(); + // Assume to be creating the facsimile data from scratch + if (!pb->GetZone()) { + // Also create a new surface + m_surface = new Surface(); + assert(m_doc->GetFacsimile()); + m_doc->GetFacsimile()->AddChild(m_surface); + zone = this->GetZone(pb, pb->GetClassName()); + } + // We already have some facsimile data - retrieve the surface ancestor + else { + m_surface = vrv_cast(zone->GetFirstAncestor(SURFACE)); + } + assert(m_surface); + assert(zone); + + m_surface->SetLrx(m_doc->m_drawingPageWidth / DEFINITION_FACTOR); + m_surface->SetLry(m_doc->m_drawingPageHeight / DEFINITION_FACTOR); + // Because the facsimile output zone positions include the margins, we will add them to each zone + m_pageMarginTop = m_doc->m_drawingPageMarginTop / DEFINITION_FACTOR; + m_pageMarginLeft = m_doc->m_drawingPageMarginLeft / DEFINITION_FACTOR; + // The Pb zone values are currently not used in SyncFromFacsimileFunctor because the // page sizes are synced from the parent Surface and zone positions include margins zone->SetUlx(m_pageMarginLeft); zone->SetUly(m_pageMarginTop); + // Use the page content size to factor in the bottom and right margins zone->SetLrx(m_doc->m_drawingPageContentWidth / DEFINITION_FACTOR + m_pageMarginLeft); zone->SetLry(m_doc->m_drawingPageContentHeight / DEFINITION_FACTOR + m_pageMarginTop); @@ -268,8 +341,11 @@ FunctorCode SyncToFacsimileFunctor::VisitSystem(System *system) Zone *SyncToFacsimileFunctor::GetZone(FacsimileInterface *interface, std::string type) { + assert(m_surface); + if (interface->GetZone()) { // Here we should probably check if the zone is a child of m_surface + assert(interface->GetZone()->GetParent() == m_surface); return interface->GetZone(); } else { diff --git a/src/functorinterface.cpp b/src/functorinterface.cpp index 92d82a8dee..202645cbbb 100644 --- a/src/functorinterface.cpp +++ b/src/functorinterface.cpp @@ -35,6 +35,7 @@ #include "ending.h" #include "expansion.h" #include "f.h" +#include "facsimile.h" #include "fb.h" #include "fermata.h" #include "fig.h" @@ -43,6 +44,7 @@ #include "genericlayerelement.h" #include "gliss.h" #include "gracegrp.h" +#include "graphic.h" #include "grpsym.h" #include "hairpin.h" #include "halfmrpt.h" @@ -101,6 +103,7 @@ #include "staffdef.h" #include "staffgrp.h" #include "stem.h" +#include "surface.h" #include "svg.h" #include "syl.h" #include "syllable.h" @@ -119,6 +122,7 @@ #include "tuplet.h" #include "turn.h" #include "verse.h" +#include "zone.h" namespace vrv { @@ -1326,6 +1330,46 @@ FunctorCode FunctorInterface::VisitTextElementEnd(TextElement *textElement) return this->VisitObjectEnd(textElement); } +FunctorCode FunctorInterface::VisitFacsimile(Facsimile *facsimile) +{ + return this->VisitObject(facsimile); +} + +FunctorCode FunctorInterface::VisitFacsimileEnd(Facsimile *facsimile) +{ + return this->VisitObjectEnd(facsimile); +} + +FunctorCode FunctorInterface::VisitGraphic(Graphic *graphic) +{ + return this->VisitObject(graphic); +} + +FunctorCode FunctorInterface::VisitGraphicEnd(Graphic *graphic) +{ + return this->VisitObjectEnd(graphic); +} + +FunctorCode FunctorInterface::VisitSurface(Surface *surface) +{ + return this->VisitObject(surface); +} + +FunctorCode FunctorInterface::VisitSurfaceEnd(Surface *surface) +{ + return this->VisitObjectEnd(surface); +} + +FunctorCode FunctorInterface::VisitZone(Zone *zone) +{ + return this->VisitObject(zone); +} + +FunctorCode FunctorInterface::VisitZoneEnd(Zone *zone) +{ + return this->VisitObjectEnd(zone); +} + FunctorCode FunctorInterface::VisitAlignment(Alignment *alignment) { return this->VisitObject(alignment); @@ -2620,6 +2664,46 @@ FunctorCode ConstFunctorInterface::VisitTextElementEnd(const TextElement *textEl return this->VisitObjectEnd(textElement); } +FunctorCode ConstFunctorInterface::VisitFacsimile(const Facsimile *facsimile) +{ + return this->VisitObject(facsimile); +} + +FunctorCode ConstFunctorInterface::VisitFacsimileEnd(const Facsimile *facsimile) +{ + return this->VisitObjectEnd(facsimile); +} + +FunctorCode ConstFunctorInterface::VisitGraphic(const Graphic *graphic) +{ + return this->VisitObject(graphic); +} + +FunctorCode ConstFunctorInterface::VisitGraphicEnd(const Graphic *graphic) +{ + return this->VisitObjectEnd(graphic); +} + +FunctorCode ConstFunctorInterface::VisitSurface(const Surface *surface) +{ + return this->VisitObject(surface); +} + +FunctorCode ConstFunctorInterface::VisitSurfaceEnd(const Surface *surface) +{ + return this->VisitObjectEnd(surface); +} + +FunctorCode ConstFunctorInterface::VisitZone(const Zone *zone) +{ + return this->VisitObject(zone); +} + +FunctorCode ConstFunctorInterface::VisitZoneEnd(const Zone *zone) +{ + return this->VisitObjectEnd(zone); +} + FunctorCode ConstFunctorInterface::VisitAlignment(const Alignment *alignment) { return this->VisitObject(alignment); diff --git a/src/graphic.cpp b/src/graphic.cpp index 03f04040e1..e2a9645369 100644 --- a/src/graphic.cpp +++ b/src/graphic.cpp @@ -14,6 +14,7 @@ //---------------------------------------------------------------------------- #include "comparison.h" +#include "functor.h" #include "vrv.h" namespace vrv { @@ -59,4 +60,28 @@ int Graphic::GetDrawingHeight(int unit, int staffSize) const return (this->GetHeight().GetVu() * unit); } +//---------------------------------------------------------------------------- +// Functor methods +//---------------------------------------------------------------------------- + +FunctorCode Graphic::Accept(Functor &functor) +{ + return functor.VisitGraphic(this); +} + +FunctorCode Graphic::Accept(ConstFunctor &functor) const +{ + return functor.VisitGraphic(this); +} + +FunctorCode Graphic::AcceptEnd(Functor &functor) +{ + return functor.VisitGraphicEnd(this); +} + +FunctorCode Graphic::AcceptEnd(ConstFunctor &functor) const +{ + return functor.VisitGraphicEnd(this); +} + } // namespace vrv diff --git a/src/miscfunctor.cpp b/src/miscfunctor.cpp index f10936c906..58754e1a2c 100644 --- a/src/miscfunctor.cpp +++ b/src/miscfunctor.cpp @@ -12,8 +12,10 @@ #include "layer.h" #include "page.h" #include "staff.h" +#include "surface.h" #include "system.h" #include "verse.h" +#include "zone.h" //---------------------------------------------------------------------------- @@ -23,13 +25,15 @@ namespace vrv { // ApplyPPUFactorFunctor //---------------------------------------------------------------------------- -ApplyPPUFactorFunctor::ApplyPPUFactorFunctor() : Functor() +ApplyPPUFactorFunctor::ApplyPPUFactorFunctor(Page *page) : Functor() { - m_page = NULL; + m_page = page; } FunctorCode ApplyPPUFactorFunctor::VisitLayerElement(LayerElement *layerElement) { + assert(m_page); + if (layerElement->IsScoreDefElement()) return FUNCTOR_SIBLINGS; if (layerElement->m_drawingFacsX != VRV_UNSET) layerElement->m_drawingFacsX /= m_page->GetPPUFactor(); @@ -40,6 +44,8 @@ FunctorCode ApplyPPUFactorFunctor::VisitLayerElement(LayerElement *layerElement) FunctorCode ApplyPPUFactorFunctor::VisitMeasure(Measure *measure) { + assert(m_page); + if (measure->m_drawingFacsX1 != VRV_UNSET) measure->m_drawingFacsX1 /= m_page->GetPPUFactor(); if (measure->m_drawingFacsX2 != VRV_UNSET) measure->m_drawingFacsX2 /= m_page->GetPPUFactor(); @@ -61,13 +67,29 @@ FunctorCode ApplyPPUFactorFunctor::VisitPage(Page *page) FunctorCode ApplyPPUFactorFunctor::VisitStaff(Staff *staff) { + assert(m_page); + if (staff->m_drawingFacsY != VRV_UNSET) staff->m_drawingFacsY /= m_page->GetPPUFactor(); return FUNCTOR_CONTINUE; } +FunctorCode ApplyPPUFactorFunctor::VisitSurface(Surface *surface) +{ + assert(m_page); + + if (surface->HasUlx()) surface->SetUlx(surface->GetUlx() * m_page->GetPPUFactor()); + if (surface->HasUly()) surface->SetUly(surface->GetUly() * m_page->GetPPUFactor()); + if (surface->HasLrx()) surface->SetLrx(surface->GetLrx() * m_page->GetPPUFactor()); + if (surface->HasLry()) surface->SetLry(surface->GetLry() * m_page->GetPPUFactor()); + + return FUNCTOR_CONTINUE; +} + FunctorCode ApplyPPUFactorFunctor::VisitSystem(System *system) { + assert(m_page); + if (system->m_drawingFacsX != VRV_UNSET) system->m_drawingFacsX /= m_page->GetPPUFactor(); if (system->m_drawingFacsY != VRV_UNSET) system->m_drawingFacsY /= m_page->GetPPUFactor(); system->m_systemLeftMar *= m_page->GetPPUFactor(); @@ -76,6 +98,18 @@ FunctorCode ApplyPPUFactorFunctor::VisitSystem(System *system) return FUNCTOR_CONTINUE; } +FunctorCode ApplyPPUFactorFunctor::VisitZone(Zone *zone) +{ + assert(m_page); + + if (zone->HasUlx()) zone->SetUlx(zone->GetUlx() * m_page->GetPPUFactor()); + if (zone->HasUly()) zone->SetUly(zone->GetUly() * m_page->GetPPUFactor()); + if (zone->HasLrx()) zone->SetLrx(zone->GetLrx() * m_page->GetPPUFactor()); + if (zone->HasLry()) zone->SetLry(zone->GetLry() * m_page->GetPPUFactor()); + + return FUNCTOR_CONTINUE; +} + //---------------------------------------------------------------------------- // GetAlignmentLeftRightFunctor //---------------------------------------------------------------------------- diff --git a/src/staff.cpp b/src/staff.cpp index 2a71ace62d..eee4fc0a3e 100644 --- a/src/staff.cpp +++ b/src/staff.cpp @@ -239,23 +239,24 @@ int Staff::CalcPitchPosYRel(const Doc *doc, int loc) const return (loc - staffLocOffset) * doc->GetDrawingUnit(m_drawingStaffSize); } -void Staff::AddLedgerLineAbove(int count, int left, int right, int extension, bool cueSize) +void Staff::AddLedgerLineAbove(int count, int left, int right, int extension, bool cueSize, const Object *event) { - this->AddLedgerLines(cueSize ? m_ledgerLinesAboveCue : m_ledgerLinesAbove, count, left, right, extension); + this->AddLedgerLines(cueSize ? m_ledgerLinesAboveCue : m_ledgerLinesAbove, count, left, right, extension, event); } -void Staff::AddLedgerLineBelow(int count, int left, int right, int extension, bool cueSize) +void Staff::AddLedgerLineBelow(int count, int left, int right, int extension, bool cueSize, const Object *event) { - this->AddLedgerLines(cueSize ? m_ledgerLinesBelowCue : m_ledgerLinesBelow, count, left, right, extension); + this->AddLedgerLines(cueSize ? m_ledgerLinesBelowCue : m_ledgerLinesBelow, count, left, right, extension, event); } -void Staff::AddLedgerLines(ArrayOfLedgerLines &lines, int count, int left, int right, int extension) +void Staff::AddLedgerLines( + ArrayOfLedgerLines &lines, int count, int left, int right, int extension, const Object *event) { assert(left < right); if ((int)lines.size() < count) lines.resize(count); for (int i = 0; i < count; ++i) { - lines.at(i).AddDash(left, right, extension); + lines.at(i).AddDash(left, right, extension, event); } } @@ -298,27 +299,28 @@ int Staff::GetNearestInterStaffPosition(int y, const Doc *doc, data_STAFFREL pla // LedgerLine //---------------------------------------------------------------------------- -void LedgerLine::AddDash(int left, int right, int extension) +void LedgerLine::AddDash(int left, int right, int extension, const Object *event) { assert(left < right); - std::list>::iterator iter; + std::list::iterator iter; // First add the dash for (iter = m_dashes.begin(); iter != m_dashes.end(); ++iter) { - if (iter->first > left) break; + if (iter->m_x1 > left) break; } - m_dashes.insert(iter, { left, right }); + m_dashes.insert(iter, LedgerLine::Dash(left, right, event)); // Merge dashes which overlap by more than 1.5 extensions // => Dashes belonging to the same chord overlap at least by two extensions and will get merged // => Overlapping dashes of adjacent notes will not get merged - std::list>::iterator previous = m_dashes.begin(); + std::list::iterator previous = m_dashes.begin(); iter = m_dashes.begin(); ++iter; while (iter != m_dashes.end()) { - if (previous->second > iter->first + 1.5 * extension) { - previous->second = std::max(iter->second, previous->second); + if (previous->m_x1 > iter->m_x1 + 1.5 * extension) { + previous->MergeWith(*iter); + previous->m_x2 = std::max(iter->m_x2, previous->m_x2); iter = m_dashes.erase(iter); } else { diff --git a/src/surface.cpp b/src/surface.cpp index 3c0b01d1b4..0f8e0fdf44 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -15,6 +15,7 @@ #include "comparison.h" #include "facsimile.h" +#include "functor.h" #include "graphic.h" #include "vrv.h" #include "zone.h" @@ -85,4 +86,28 @@ int Surface::GetMaxY() const return max; } +//---------------------------------------------------------------------------- +// Functor methods +//---------------------------------------------------------------------------- + +FunctorCode Surface::Accept(Functor &functor) +{ + return functor.VisitSurface(this); +} + +FunctorCode Surface::Accept(ConstFunctor &functor) const +{ + return functor.VisitSurface(this); +} + +FunctorCode Surface::AcceptEnd(Functor &functor) +{ + return functor.VisitSurfaceEnd(this); +} + +FunctorCode Surface::AcceptEnd(ConstFunctor &functor) const +{ + return functor.VisitSurfaceEnd(this); +} + } // namespace vrv diff --git a/src/svgdevicecontext.cpp b/src/svgdevicecontext.cpp index 1cec345599..1d01a5d5b1 100644 --- a/src/svgdevicecontext.cpp +++ b/src/svgdevicecontext.cpp @@ -438,6 +438,11 @@ void SvgDeviceContext::SetCustomGraphicColor(const std::string &color) m_currentNode.append_attribute("fill") = color.c_str(); } +void SvgDeviceContext::SetCustomGraphicAttributes(const std::string &data, const std::string &value) +{ + m_currentNode.append_attribute(("data-" + data).c_str()) = value.c_str(); +} + void SvgDeviceContext::EndResumedGraphic(Object *object, View *view) { m_svgNodeStack.pop_back(); diff --git a/src/view_element.cpp b/src/view_element.cpp index efa5fd74b6..c4166e20b9 100644 --- a/src/view_element.cpp +++ b/src/view_element.cpp @@ -1746,7 +1746,7 @@ void View::DrawSyl(DeviceContext *dc, LayerElement *element, Layer *layer, Staff return; } - if (!m_doc->IsFacs() && !m_doc->IsNeumeLines()) { + if (!m_doc->IsFacs() && !m_doc->IsTranscription() && !m_doc->IsNeumeLines()) { syl->SetDrawingYRel(this->GetSylYRel(syl->m_drawingVerse, staff)); } diff --git a/src/view_page.cpp b/src/view_page.cpp index d8e7797b34..235e6a908a 100644 --- a/src/view_page.cpp +++ b/src/view_page.cpp @@ -11,6 +11,7 @@ #include #include +#include //---------------------------------------------------------------------------- @@ -1375,10 +1376,35 @@ void View::DrawLedgerLines(DeviceContext *dc, Staff *staff, const ArrayOfLedgerL dc->SetPen(m_currentColor, ToDeviceContextX(lineWidth), AxSOLID); dc->SetBrush(m_currentColor, AxSOLID); + bool svgHtml5 = (m_doc->GetOptions()->m_svgHtml5.GetValue()); + for (const LedgerLine &line : lines) { - for (const std::pair &dash : line.m_dashes) { - dc->DrawLine(ToDeviceContextX(x + dash.first), ToDeviceContextY(y), ToDeviceContextX(x + dash.second), + for (const LedgerLine::Dash &dash : line.m_dashes) { + if (svgHtml5) { + // Add the custom graphic only with html5 + dc->StartCustomGraphic("lineDash"); + // Function to concatenate IDs from the list of Object events + auto concatenateIDs = [](const ListOfConstObjects &objects) { + // Get a list of strings + auto ids = objects + | std::views::transform([](const Object *object) { return ("#" + object->GetID() + " "); }); + // Concatenate IDs + // Once we have C++ 23 we can add the space above and do + // std::ranges::to(std::views::join(ids)); + // But for now use a stringstream + std::stringstream sstream; + std::copy(ids.begin(), ids.end(), std::ostream_iterator(sstream)); + return sstream.str(); + }; + std::string events = concatenateIDs(dash.m_events); + if (!events.empty()) events.pop_back(); // Remove extra space added by the concatenation + dc->SetCustomGraphicAttributes("related", events); + } + + dc->DrawLine(ToDeviceContextX(x + dash.m_x1), ToDeviceContextY(y), ToDeviceContextX(x + dash.m_x2), ToDeviceContextY(y)); + + if (svgHtml5) dc->EndCustomGraphic(); } y += ySpace; } diff --git a/src/zone.cpp b/src/zone.cpp index b5838cd767..1861a40e1b 100644 --- a/src/zone.cpp +++ b/src/zone.cpp @@ -14,6 +14,7 @@ //---------------------------------------------------------------------------- #include "comparison.h" +#include "functor.h" #include "vrv.h" namespace vrv { @@ -59,4 +60,28 @@ int Zone::GetLogicalLry() const return (this->GetLry()); } +//---------------------------------------------------------------------------- +// Functor methods +//---------------------------------------------------------------------------- + +FunctorCode Zone::Accept(Functor &functor) +{ + return functor.VisitZone(this); +} + +FunctorCode Zone::Accept(ConstFunctor &functor) const +{ + return functor.VisitZone(this); +} + +FunctorCode Zone::AcceptEnd(Functor &functor) +{ + return functor.VisitZoneEnd(this); +} + +FunctorCode Zone::AcceptEnd(ConstFunctor &functor) const +{ + return functor.VisitZoneEnd(this); +} + } // namespace vrv