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
5 changes: 5 additions & 0 deletions regression-tests/g-issue150.gmn
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{[
(* meas. 1 *) \clef<"g2"> \key<0> \meter<"4/4", autoBarlines="off", autoMeasuresNum="system"> \harmony<"G7sus", position="above", dy=-0.6hs> \beamsOff \tieBegin:1 g1/1 \harmony<"Gm9", position="above", dy=-0.6hs, dx=-0.50000000dur> \bar<measNum=2>
(* meas. 2 *) \harmony<"G7sus", position="above", dy=-0.6hs> \stemsUp \beamsOff g1/2. \tieEnd:1 \harmony<"Gm9", position="above", dy=-0.6hs, dx=-0.25000000dur> _/4 \bar<measNum=3>
(* meas. 3 *) \harmony<"Am/C", position="above", dy=-0.6hs> \stemsAuto \beamsOff c1/1 \harmony<"B&/C", position="above", dy=-0.6hs, dx=-0.50000000dur> \staff<1> \endBar ]
}
70 changes: 68 additions & 2 deletions src/engine/abstract/TagParameterFloat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include <string>
#include <regex>
#include <iomanip>

#include "TagParameterFloat.h"
#include "ARUnits.h"
Expand All @@ -32,6 +33,8 @@ TagParameterFloat::TagParameterFloat(const TagParameterFloat & tpf)
{
fUnittag = tpf.fUnittag;
fValue = tpf.fValue;
fIsDuration = tpf.fIsDuration;
fDuration = tpf.fDuration;
fUnit = tpf.getUnit();
}

Expand All @@ -44,18 +47,25 @@ void TagParameterFloat::set( const TagParameterFloat & in )
fUnit = in.getUnit();
fUnittag = in.fUnittag;
fValue = in.fValue;
fIsDuration = in.fIsDuration;
fDuration = in.fDuration;
}


void TagParameterFloat::print(std::ostream& out)
{
TagParameter::print (out);
const char* u = TagIsUnitTag() ? getUnit() : "";
out << getValue() << u;
if (fIsDuration) {
out << std::string(fDuration) << "dur";
}
else out << getValue() << u;
}

void TagParameterFloat::reset(float inFloatValue, const char * inUnit)
{
fIsDuration = false;
fDuration = Fraction(0,1);
fValue = inFloatValue;
string u (inUnit);
if (u.size()) {
Expand All @@ -70,6 +80,8 @@ void TagParameterFloat::reset(float inFloatValue, const char * inUnit)

const TYPE_FLOATPARAMETER TagParameterFloat::getValue(float curLSPACE) const
{
if (fIsDuration)
return (float) fDuration;
if (fUnittag) {
string tmpunit;
if (fUnit.empty()) {
Expand All @@ -95,6 +107,43 @@ const TYPE_FLOATPARAMETER TagParameterFloat::getValue(float curLSPACE) const
return fValue;
}

static bool parseDots (int dots, Fraction& value)
{
if (!dots) return true;
Fraction add = value;
for (int i=0; i<dots; i++) {
add = add * Frac_1_2;
value += add;
}
return true;
}

static bool parseDurationString(const std::string& val, Fraction& out)
{
// syntax: [sign]num/den[.]*dur or [sign]decimal dur (dur suffix required)
std::regex fracExp("^\\s*([+-]?)(\\d+)/(\\d+)(\\.*)dur\\s*$");
std::regex decExp("^\\s*([+-]?\\d+(?:\\.\\d+)?)dur\\s*$");
std::smatch match;

if (std::regex_search(val, match, fracExp)) {
long num = std::stol(match[2]);
long den = std::stol(match[3]);
if (den == 0) return false;
int dots = (int)match[4].str().size();
out = Fraction((int)num, (int)den);
parseDots(dots, out);
if (match[1].str() == "-")
out = out * -1;
return true;
}
if (std::regex_search(val, match, decExp)) {
double d = std::stod(match[1]);
out = Fraction(d);
return true;
}
return false;
}

void TagParameterFloat::setValue(const char * p)
{
string val = p;
Expand All @@ -103,26 +152,43 @@ void TagParameterFloat::setValue(const char * p)
std::smatch match;

fUnit.clear();
fIsDuration = false;
fDuration = Fraction(0,1);
if (regex_search (val, match, e)) {
string vstr = match[1];
fValue = (float)atof(vstr.c_str());
fUnit = match[2];
fUnittag = true;
}
else fValue = (float)atof(p);
else {
Fraction dur;
if (parseDurationString(val, dur)) {
fDuration = dur;
fIsDuration = true;
fUnittag = false;
fValue = (float) dur;
}
else fValue = (float)atof(p);
}
}

void TagParameterFloat::setUnit(const char * unit)
{
fUnit = unit;
fUnittag = true;
if (fUnit == "dur") {
fIsDuration = true;
fDuration = Fraction(fValue);
}
}

bool TagParameterFloat::copyValue(const TagParameter * tp)
{
const TagParameterFloat * tpf = TagParameterFloat::cast(tp);
if (!tpf) return false;

fIsDuration = tpf->fIsDuration;
fDuration = tpf->fDuration;
if (fUnittag)
fUnit = tpf->getUnit();
else if (tpf->getUnit()[0] != 0)
Expand Down
5 changes: 5 additions & 0 deletions src/engine/abstract/TagParameterFloat.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/

#include "TagParameter.h"
#include "Fraction.h"

typedef float TYPE_FLOATPARAMETER;

Expand Down Expand Up @@ -43,6 +44,8 @@ class TagParameterFloat : public TagParameter
virtual bool isFloat() const { return true; }

const TYPE_FLOATPARAMETER getValue( float curLSPACE = 50.0f ) const;
bool isDuration() const { return fIsDuration; }
const Fraction & getDuration() const { return fDuration; }

static bool convertValue(float value, double &toValue, const char * unit, float curLSPACE = 50.0f);

Expand All @@ -63,6 +66,8 @@ class TagParameterFloat : public TagParameter
// enum { kUnitLen=3 };
std::string fUnit;
TYPE_FLOATPARAMETER fValue;
bool fIsDuration = false;
Fraction fDuration;

private:
using TagParameter::set;
Expand Down
107 changes: 105 additions & 2 deletions src/engine/graphic/GRStaff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ using namespace std;
#include "GRMusic.h"
#include "GRNote.h"
#include "GRRest.h"
#include "GRTag.h"
#include "GRRepeatBegin.h"
#include "GRRepeatEnd.h"
#include "GRRod.h"
Expand Down Expand Up @@ -2003,13 +2004,22 @@ void GRStaff::FinishStaff()
date = e->getRelativeTimePosition();
duration = e->getDuration();
}
}
}
}
setDuration (date - getRelativeTimePosition() + duration);
vector<GRPositionTag *>::iterator i;
for (i=ptags.begin(); i!=ptags.end(); i++) {
(*i)->FinishPTag (this);
}
// apply duration-based dx offsets after position tags have set their anchors
{
GuidoPos p = mCompElements.GetHeadPosition();
while (p) {
GRNotationElement * e = mCompElements.GetNext(p);
GRTag * tag = dynamic_cast<GRTag *>(e);
if (tag) tag->applyDurationDx(this);
}
}
if (mStaffState.fMultiVoiceCollisions) checkMultiVoiceNotesCollision();
updateBoundingBox();
// GRStaffOnOffVisitor v;
Expand Down Expand Up @@ -2430,10 +2440,103 @@ float GRStaff::getXEndPosition(TYPE_TIMEPOSITION pos, TYPE_DURATION dur) const
}
delete elmtsAtEndOfDuration;
}
}
}
return x;
}

// ----------------------------------------------------------------------------
float GRStaff::getXForTime(const TYPE_TIMEPOSITION& tp) const
{
const NEPointerList& elts = getElements();
const GRNotationElement* prev = nullptr;
const GRNotationElement* next = nullptr;

GuidoPos pos = elts.GetHeadPosition();
while (pos) {
const GRNotationElement* e = elts.GetNext(pos);
TYPE_TIMEPOSITION et = e->getRelativeTimePosition();
if (et <= tp) prev = e;
if (et >= tp) { next = e; break; }
}
if (!prev) prev = next;
if (!next) next = prev;
if (!prev) return 0;

float xPrev = prev->getPosition().x;
float xNext = next ? next->getPosition().x : xPrev;
TYPE_TIMEPOSITION tPrev = prev->getRelativeTimePosition();
TYPE_TIMEPOSITION tNext = next ? next->getRelativeTimePosition() : tPrev;

if (tNext == tPrev) return xPrev;
if (tp <= tPrev) return xPrev;
if (tp >= tNext) return xNext;

double ratio = double(tp - tPrev) / double(tNext - tPrev);
return xPrev + (float)((xNext - xPrev) * ratio);
}

// ----------------------------------------------------------------------------
void GRStaff::getMeasureBounds(const TYPE_TIMEPOSITION& tp, TYPE_TIMEPOSITION& start, TYPE_TIMEPOSITION& end) const
{
start = DURATION_0;
end = getDuration();

TYPE_TIMEPOSITION prevBar = DURATION_0;
bool prevSet = false;
TYPE_TIMEPOSITION nextBar = end;
bool nextSet = false;

GuidoPos p = mCompElements.GetHeadPosition();
while (p) {
const GRNotationElement* e = mCompElements.GetNext(p);
const GRBar* b = e->isGRBar();
if (!b) continue;

const TYPE_TIMEPOSITION bt = e->getRelativeTimePosition();
if (bt <= tp) {
if (!prevSet || bt > prevBar) {
prevBar = bt;
prevSet = true;
}
}
else {
if (!nextSet || bt < nextBar) {
nextBar = bt;
nextSet = true;
}
}
}

if (prevSet) start = prevBar;
if (nextSet) end = nextBar;

// If we only know the next bar, derive the measure start from the meter
if (!prevSet && nextSet) {
TYPE_DURATION mdur (1,1);
const ARMeter* meter = getCurMeter();
if (meter) {
TYPE_DURATION meterDur = meter->getMeterDuration();
if (meterDur > DURATION_0)
mdur = meterDur;
}
start = nextBar - mdur;
if (start < DURATION_0)
start = DURATION_0;
}

// fallback: if no bar ahead (or zero-length), derive from meter
if (end <= start) {
const ARMeter* meter = getCurMeter();
if (meter) {
TYPE_DURATION mdur = meter->getMeterDuration();
if (mdur > DURATION_0)
end = start + mdur;
}
if (end <= start) // ensure progress
end = start + TYPE_DURATION(1,1);
}
}

void GRStaff::setOnOff(bool on, TYPE_TIMEPOSITION tp)
{
// fOnOffList.push_back (make_pair(tp,on));
Expand Down
2 changes: 2 additions & 0 deletions src/engine/graphic/GRStaff.h
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ class GRStaff : public GRCompositeNotationElement
GRStaffState & getGRStaffState() { return mStaffState; }
const GRStaffState & getGRStaffState() const { return mStaffState; }
float getXEndPosition(TYPE_TIMEPOSITION pos, TYPE_DURATION dur) const;
float getXForTime(const TYPE_TIMEPOSITION& tp) const;
void getMeasureBounds(const TYPE_TIMEPOSITION& tp, TYPE_TIMEPOSITION& start, TYPE_TIMEPOSITION& end) const;
const ARMeter * getCurMeter() const { return mStaffState.curmeter; }

virtual float getNotePosition(TYPE_PITCH pit, TYPE_REGISTER oct) const;
Expand Down
59 changes: 58 additions & 1 deletion src/engine/graphic/GRTag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include "TagParameterFloat.h"
#include "GRDefine.h"
#include "GRTag.h"
#include "GRStaff.h"
#include "GRNotationElement.h"

GRTag::GRTag( const ARMusicalTag * artag, float curLSPACE )
: isautotag(0), sconst(SCONST_DEFAULT)
Expand All @@ -41,7 +43,13 @@ GRTag::GRTag( const ARMusicalTag * artag, float curLSPACE )

const TagParameterFloat * dx = artag->getDX();
const TagParameterFloat * dy = artag->getDY();
if (dx) mTagOffset.x = (GCoord) dx->getValue(curLSPACE);
if (dx) {
if (dx->isDuration()) {
fHasDurationDx = true;
fDxDuration = dx->getDuration();
}
else mTagOffset.x = (GCoord) dx->getValue(curLSPACE);
}
if (dy) mTagOffset.y -= (GCoord) dy->getValue(curLSPACE);

const TagParameterFloat * tps = artag->getSize();
Expand All @@ -65,3 +73,52 @@ bool GRTag::IsStateTag() const { return (fTagType == STAFFTAG); }
int GRTag::getIsAuto() const { return isautotag; }
bool GRTag::operator==(const GRTag & tag) const { return false; }

void GRTag::applyDurationDx(GRStaff * grstaff)
{
if (!fHasDurationDx || fDurationDxApplied || !grstaff)
return;

GRNotationElement * ne = dynamic_cast<GRNotationElement*>(this);
if (!ne) return;

TYPE_TIMEPOSITION baseTime = ne->getRelativeTimePosition();
TYPE_TIMEPOSITION measStart, measEnd;
grstaff->getMeasureBounds(baseTime, measStart, measEnd);

// derive spatial offset from time offset
const TYPE_TIMEPOSITION targetTime = baseTime + fDxDuration;
TYPE_TIMEPOSITION targetMeasStart = measStart;
TYPE_TIMEPOSITION targetMeasEnd = measEnd;

// If the target time crosses a barline, compute offsets in the measure where it lands.
if (targetTime < measStart || targetTime > measEnd) {
grstaff->getMeasureBounds(targetTime, targetMeasStart, targetMeasEnd);
}

const float baseX = grstaff->getXForTime(baseTime);

float targetX = baseX;
if (targetTime >= targetMeasStart && targetTime <= targetMeasEnd) {
targetX = grstaff->getXForTime(targetTime);
}
else if (targetMeasEnd != targetMeasStart) {
const double ratio = double(targetTime - targetMeasStart) /
double(targetMeasEnd - targetMeasStart);
const float targetStartX = grstaff->getXForTime(targetMeasStart);
const float targetEndX = grstaff->getXForTime(targetMeasEnd);
const float targetWidth = targetEndX - targetStartX;
targetX = targetStartX + float(ratio * targetWidth);
}
const float dx = targetX - baseX;

// std::cerr << "[duration-dx] baseTime=" << double(baseTime)
// << " targetTime=" << double(targetTime)
// << " baseMeas=[" << double(measStart) << "," << double(measEnd) << "]"
// << " targetMeas=[" << double(targetMeasStart) << "," << double(targetMeasEnd) << "]"
// << " baseX=" << baseX << " targetX=" << targetX
// << " dx=" << dx << std::endl;

mTagOffset.x += dx;

fDurationDxApplied = true;
}
Loading
Loading