Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds feature to edit tangents of Cubic Hermite progressions #5924

Merged
merged 89 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from 84 commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
77d69e2
First commit
IanCaio Oct 13, 2020
fb2107d
Implements inValue/outValue on Automation Nodes
IanCaio Oct 14, 2020
28611b8
Fixes and refactor code on AutomationEditor.cpp
IanCaio Oct 15, 2020
e5c9669
Avoids unnecessary check in putValue
IanCaio Oct 15, 2020
ab0aeb0
Adds upgrade routine for the automation nodes
IanCaio Oct 15, 2020
2ed2a66
Allows dragging outValues on the Automation Editor
IanCaio Oct 16, 2020
7b6729b
Small fix inside AutomationPattern.cpp
IanCaio Oct 16, 2020
a800943
Creates separate files for AutomationNode
IanCaio Oct 16, 2020
3932106
Adds more members to AutomationNode
IanCaio Oct 16, 2020
9f55d2c
Removes unused code from AutomationEditor
IanCaio Oct 16, 2020
c42c46b
Changes to the outValues now update the tangents
IanCaio Oct 16, 2020
504143c
Keeps discrete jumps when flipping patterns
IanCaio Oct 16, 2020
d67942b
Allows reseting outValues on the AutomationEditor
IanCaio Oct 16, 2020
7c269bd
Makes so dragging nodes keep the outValue
IanCaio Oct 16, 2020
64fcec0
Fixes code style on modified code
IanCaio Oct 16, 2020
578367e
Adds a QProperty for the node outValue color
IanCaio Oct 16, 2020
c4960a1
Adds doxygen comments on methods
IanCaio Oct 16, 2020
5c94591
Refactor flipX and flipY methods
IanCaio Oct 16, 2020
939596a
Add a new edit mode to Automation Editor
IanCaio Oct 16, 2020
5a38f74
Improves the Draw OutValue edit mode
IanCaio Oct 17, 2020
7925196
Adds mutex to AutomationPattern
IanCaio Oct 17, 2020
9e325dc
Veratil's review changes
IanCaio Oct 19, 2020
61d20ff
Changes the inValue sphere to be draw first
IanCaio Oct 19, 2020
ff92b56
Merge branch 'master' into feature/automationProgression
IanCaio Oct 21, 2020
3d4be23
Changes comments and variables names
IanCaio Oct 21, 2020
caa43ad
Changes QProperty variables to use MEMBER
IanCaio Oct 22, 2020
61ef27a
Overloads some AutomationNode's operators
IanCaio Oct 22, 2020
932a46e
Improves getNodeAt method
IanCaio Oct 22, 2020
a64942a
Changes behavior of setDragValue
IanCaio Oct 22, 2020
3b6f238
Moves getNodeAt to an upper level, reducing lines
IanCaio Oct 22, 2020
53f4afa
Changes icon of Draw OutValue edit mode
IanCaio Oct 22, 2020
108323d
Merge branch 'master' into feature/automationProgression
IanCaio Oct 24, 2020
203d3b5
Removes unnecessary non-const methods
IanCaio Nov 12, 2020
1531668
Merge branch 'master' into feature/automationProgression
IanCaio Nov 12, 2020
5184d79
Fixes formatting and doxygen comments
IanCaio Nov 13, 2020
9805321
Adds helper macros for AutomationNodes
IanCaio Nov 13, 2020
055c95f
Removes tabs from ternary operator
IanCaio Nov 13, 2020
1c3502c
Update files to use AutomationNode macros
IanCaio Nov 13, 2020
3471b8f
Merge branch 'master' into feature/automationProgression
IanCaio Nov 26, 2020
7559a41
Addresses my own code review
IanCaio Nov 26, 2020
2cf33e1
Addresses Veratil's review
IanCaio Nov 26, 2020
e0c3563
Adds helper MACROs for node tangents
IanCaio Nov 26, 2020
85a0ca3
Merge branch 'master' into feature/automationProgression
IanCaio Dec 5, 2020
cbf73dc
Fixes header inclusion order
IanCaio Dec 5, 2020
de6d7c9
Removes mutex from AutomationEditor
IanCaio Dec 6, 2020
7d1f196
Locks mutex on AutomationPattern copy assignment
IanCaio Dec 6, 2020
128c723
Changes resetOutValue() so it generates tangents
IanCaio Dec 6, 2020
adc223b
Changes some methods to use for loops
IanCaio Dec 11, 2020
169cc3b
Move removeNodes/resetNodes to AutomationPattern
IanCaio Dec 11, 2020
033bfab
Optimizes loop inside putValue/putValues
IanCaio Dec 11, 2020
fe53845
Adds comment to mysterious calculation
IanCaio Dec 12, 2020
1f28a45
Merge branch 'master' into feature/automationProgression
IanCaio Dec 16, 2020
1e1e5f8
Reduces indentation on getNodeAt
IanCaio Dec 16, 2020
6bec055
Applies Veratil suggestions
IanCaio Dec 16, 2020
1d23334
Updates comment from changed code
IanCaio Dec 16, 2020
8c5f9d4
Uses lambda functions to extract code
IanCaio Dec 16, 2020
1d1d57c
Fixes behavior of AutomationPattern::flipX
IanCaio Dec 20, 2020
d9862c5
Changes pattern XML for backwards compatibility
IanCaio Dec 20, 2020
2d1fbd0
Fixes bug on AutomationPattern::resetNodes
IanCaio Dec 20, 2020
0f3da48
Adds drawing of automation node tangents
IanCaio Dec 21, 2020
fca7c21
Adds mode to edit tangents
IanCaio Dec 26, 2020
4b4ac2b
Adds comment about copy-assignment
IanCaio Jan 7, 2021
93c0c65
Merge branch 'master' into feature/automationProgression
IanCaio Jan 30, 2021
5a66667
Changes flipY logic and address PhysSong review
IanCaio Jan 30, 2021
2d06c78
Merge branch 'feature/automationProgression' into feature/automationP…
IanCaio Feb 25, 2021
4b259e3
Implements the dragging of node tangents
IanCaio Feb 25, 2021
9cdd7a3
Saves tangent information from automation patterns
IanCaio Feb 25, 2021
fe3e42d
Merge branch 'master' into feature/automationProgressionImprov
IanCaio Feb 26, 2021
aa1ef73
Implements the locking of tangents
IanCaio Feb 27, 2021
eac1bf5
Merge branch 'master' into feature/automationProgressionImprov
IanCaio Feb 28, 2021
1e605ef
Checks only for inTan on the LoadSettings
IanCaio Feb 28, 2021
437e853
Adds a convenient way to reset multiple tangents
IanCaio Feb 28, 2021
4a3ff37
Fixes small bug on generateTangents
IanCaio Mar 1, 2021
dda3416
Fixes bug with Edit Tangents shortcut
IanCaio Mar 3, 2021
3234578
Disables Edit Tan mode on other progressions
IanCaio Mar 3, 2021
8d65acb
Keep tangents when dragging nodes
IanCaio Mar 3, 2021
9394358
Changes tangent calculation behavior
IanCaio Mar 3, 2021
3016a29
Fixes undo/redo with tangents editing
IanCaio Mar 4, 2021
48a5468
Merge branch 'master' into feature/automationProgressionImprov
IanCaio Apr 18, 2021
0dfb18c
Merge branch 'master' into feature/automationProgressionImprov
PhysSong Aug 2, 2022
ced95d4
Merge branch 'master' into feature/automationProgressionImprov
PhysSong Nov 13, 2022
ffb3fcd
Uses function to check if tangents can be edited
IanCaio Jul 6, 2023
c1eab65
Merge branch 'master' into feature/automationProgressionImprov
IanCaio Aug 28, 2023
5660d0a
Merge branch 'master' into feature/automationProgressionImprov
IanCaio Sep 22, 2023
4ebf466
Fixes bug reported by zonkmachine
IanCaio Sep 24, 2023
bd135ee
A couple review requests forgotten
IanCaio Sep 24, 2023
7a02ed0
Address Sakertooth's review
IanCaio Sep 30, 2023
8195c7b
Adresses Sakertooth's review
IanCaio Sep 30, 2023
e65cda3
Address Sakertooth's review
IanCaio Sep 30, 2023
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
Binary file added data/themes/classic/edit_tangent.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions data/themes/classic/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ lmms--gui--AutomationEditor {
qproperty-backgroundShade: rgba(255, 255, 255, 15);
qproperty-nodeInValueColor: rgba(255, 119, 175, 150);
qproperty-nodeOutValueColor: rgba(129, 231, 181, 150);
qproperty-nodeTangentLineColor: rgba(200, 200, 200, 255);
qproperty-crossColor: rgb( 255, 51, 51 );
/* Grid colors */
qproperty-lineColor: rgba(128, 128, 128, 80);
Expand Down
Binary file added data/themes/default/edit_tangent.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions data/themes/default/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ lmms--gui--AutomationEditor {
qproperty-backgroundShade: rgba(255, 255, 255, 15);
qproperty-nodeInValueColor: rgba(103, 73, 194, 150);
qproperty-nodeOutValueColor: rgba(125, 40, 40, 150);
qproperty-nodeTangentLineColor: rgba(200, 200, 200, 255);
qproperty-crossColor: rgba(215, 210, 254, 150);
/* Grid colors */
qproperty-lineColor: #292929;
Expand Down
23 changes: 23 additions & 0 deletions include/AutomationClip.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class TimePos;
namespace gui
{
class AutomationClipView;
class AutomationEditor;
} // namespace gui


Expand Down Expand Up @@ -111,6 +112,8 @@ class LMMS_EXPORT AutomationClip : public Clip

void resetNodes(const int tick0, const int tick1);

void resetTangents(const int tick0, const int tick1);

void recordValue(TimePos time, float value);

TimePos setDragValue( const TimePos & time,
Expand Down Expand Up @@ -151,6 +154,17 @@ class LMMS_EXPORT AutomationClip : public Clip
return m_timeMap.isEmpty() == false;
}

static bool supportsTangentEditing(ProgressionType pType)
{
// Update function if we have new progression types that support tangent editing
return pType == ProgressionType::CubicHermite;
}

inline bool canEditTangents() const
{
return supportsTangentEditing(m_progressionType);
}

float valueAt( const TimePos & _time ) const;
float *valuesAfter( const TimePos & _time ) const;

Expand Down Expand Up @@ -219,6 +233,9 @@ public slots:
bool m_dragging;
bool m_dragKeepOutValue; // Should we keep the current dragged node's outValue?
float m_dragOutValue; // The outValue of the dragged node's
bool m_dragLockedTan; // If the dragged node has it's tangents locked
float m_dragInTan; // The dragged node's inTangent
float m_dragOutTan; // The dragged node's outTangent

bool m_isRecording;
float m_lastRecordedValue;
Expand All @@ -230,6 +247,7 @@ public slots:

friend class gui::AutomationClipView;
friend class AutomationNode;
friend class gui::AutomationEditor;

} ;

Expand Down Expand Up @@ -261,6 +279,11 @@ inline float OUTTAN(AutomationClip::TimemapIterator it)
return it->getOutTangent();
}

inline float LOCKEDTAN(AutomationClip::TimemapIterator it)
{
return it->lockedTangents();
}

inline int POS(AutomationClip::TimemapIterator it)
{
return it.key();
Expand Down
24 changes: 22 additions & 2 deletions include/AutomationEditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class AutomationEditor : public QWidget, public JournallingObject
Q_PROPERTY(QColor lineColor MEMBER m_lineColor)
Q_PROPERTY(QColor nodeInValueColor MEMBER m_nodeInValueColor)
Q_PROPERTY(QColor nodeOutValueColor MEMBER m_nodeOutValueColor)
Q_PROPERTY(QColor nodeTangentLineColor MEMBER m_nodeTangentLineColor)
Q_PROPERTY(QBrush scaleColor MEMBER m_scaleColor)
Q_PROPERTY(QBrush graphColor MEMBER m_graphColor)
Q_PROPERTY(QColor crossColor MEMBER m_crossColor)
Expand Down Expand Up @@ -91,7 +92,8 @@ class AutomationEditor : public QWidget, public JournallingObject
{
Draw,
Erase,
DrawOutValues
DrawOutValues,
EditTangents
};

public slots:
Expand All @@ -118,6 +120,8 @@ public slots:
inline void drawLevelTick(QPainter & p, int tick, float value);

timeMap::iterator getNodeAt(int x, int y, bool outValue = false, int r = 5);
// Get the closest node to the x position (for the drag tangent)
timeMap::iterator getClosestNode(int x);

void drawLine( int x0, float y0, int x1, float y1 );
bool fineTuneValue(timeMap::iterator node, bool editingOutValue);
Expand Down Expand Up @@ -153,7 +157,9 @@ protected slots:
EraseValues,
MoveOutValue,
ResetOutValues,
DrawLine
DrawLine,
MoveTangent,
ResetTangents
} ;

// some constants...
Expand All @@ -173,6 +179,7 @@ protected slots:
static QPixmap * s_toolDraw;
static QPixmap * s_toolErase;
static QPixmap * s_toolDrawOut;
static QPixmap * s_toolEditTangents;
static QPixmap * s_toolMove;
static QPixmap * s_toolYFlip;
static QPixmap * s_toolXFlip;
Expand Down Expand Up @@ -215,6 +222,11 @@ protected slots:
// Time position (key) of automation node whose outValue is being dragged
int m_draggedOutValueKey;

// The tick from the node whose tangent is being dragged
int m_draggedTangentTick;
// Whether the tangent being dragged is the InTangent or OutTangent
bool m_draggedOutTangent;

EditMode m_editMode;

bool m_mouseDownLeft;
Expand All @@ -225,6 +237,7 @@ protected slots:

void drawCross(QPainter & p );
void drawAutomationPoint( QPainter & p, timeMap::iterator it );
void drawAutomationTangents(QPainter & p, timeMap::iterator it);
IanCaio marked this conversation as resolved.
Show resolved Hide resolved
bool inPatternEditor();

QColor m_barLineColor;
Expand All @@ -233,6 +246,7 @@ protected slots:
QBrush m_graphColor;
QColor m_nodeInValueColor;
QColor m_nodeOutValueColor;
QColor m_nodeTangentLineColor;
QBrush m_scaleColor;
QColor m_crossColor;
QColor m_backgroundShade;
Expand Down Expand Up @@ -285,8 +299,14 @@ protected slots:

private slots:
void updateWindowTitle();
void setProgressionType(int progType);

private:
QAction* m_drawAction;
QAction* m_eraseAction;
QAction* m_drawOutAction;
QAction* m_editTanAction;

QAction* m_discreteAction;
QAction* m_linearAction;
QAction* m_cubicHermiteAction;
Expand Down
21 changes: 21 additions & 0 deletions include/AutomationNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,22 @@ class AutomationNode
m_outTangent = tangent;
}

/**
* @brief Checks if the tangents from the node are locked
*/
inline const bool lockedTangents() const
{
return m_lockedTangents;
}

/**
* @brief Locks or Unlocks the tangents from this node
*/
inline void setLockedTangents(bool b)
{
m_lockedTangents = b;
}

/**
* @brief Sets the clip this node belongs to
* @param AutomationClip* clip that m_clip will be
Expand Down Expand Up @@ -152,6 +168,11 @@ class AutomationNode
// outValue are equal, inTangent and outTangent are equal too.
float m_inTangent;
float m_outTangent;

// If the tangents were edited manually, this will be true. That way
// the tangents from this node will not be recalculated. It's set back
// to false if the tangents are reset.
bool m_lockedTangents;
};

} // namespace lmms
Expand Down
102 changes: 99 additions & 3 deletions src/core/AutomationClip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,37 @@ void AutomationClip::resetNodes(const int tick0, const int tick1)



/**
* @brief Resets the tangents from the nodes between the given ticks
* @param Int first tick of the range
* @param Int second tick of the range
*/
void AutomationClip::resetTangents(const int tick0, const int tick1)
{
if (tick0 == tick1)
{
auto it = m_timeMap.find(TimePos(tick0));
if (it != m_timeMap.end())
{
it.value().setLockedTangents(false);
generateTangents(it, 1);
}
return;
}

TimePos start = TimePos(qMin(tick0, tick1));
TimePos end = TimePos(qMax(tick0, tick1));
IanCaio marked this conversation as resolved.
Show resolved Hide resolved

for (auto it = m_timeMap.lowerBound(start), endIt = m_timeMap.upperBound(end); it != endIt; ++it)
{
it.value().setLockedTangents(false);
generateTangents(it, 1);
}
}




void AutomationClip::recordValue(TimePos time, float value)
{
QMutexLocker m(&m_clipMutex);
Expand Down Expand Up @@ -467,16 +498,31 @@ TimePos AutomationClip::setDragValue(
// inValue
m_dragKeepOutValue = false;

// We will set the tangents back to what they were if the node had
// its tangents locked
m_dragLockedTan = false;

// Check if we already have a node on the position we are dragging
// and if we do, store the outValue so the discrete jump can be kept
// and information about the tangents
timeMap::iterator it = m_timeMap.find(newTime);
if (it != m_timeMap.end())
{
// If we don't have a discrete jump, the outValue will be the
// same as the inValue
if (OFFSET(it) != 0)
{
m_dragKeepOutValue = true;
m_dragOutValue = OUTVAL(it);
}
// For the tangents, we will only keep them if the tangents were
// locked
if (LOCKEDTAN(it))
{
m_dragLockedTan = true;
m_dragInTan = INTAN(it);
m_dragOutTan = OUTTAN(it);
}
}

this->removeNode(newTime);
Expand All @@ -489,12 +535,31 @@ TimePos AutomationClip::setDragValue(

generateTangents();

TimePos returnedPos;

if (m_dragKeepOutValue)
{
return this->putValues(time, value, m_dragOutValue, quantPos, controlKey);
returnedPos = this->putValues(time, value, m_dragOutValue, quantPos, controlKey);
}
else
{
returnedPos = this->putValue(time, value, quantPos, controlKey);
sakertooth marked this conversation as resolved.
Show resolved Hide resolved
}

return this->putValue(time, value, quantPos, controlKey);
// Set the tangents on the newly created node if they were locked
// before dragging
if (m_dragLockedTan)
{
timeMap::iterator it = m_timeMap.find(returnedPos);
if (it != m_timeMap.end())
{
it.value().setInTangent(m_dragInTan);
it.value().setOutTangent(m_dragOutTan);
it.value().setLockedTangents(true);
}
}

return returnedPos;
}


Expand Down Expand Up @@ -783,6 +848,9 @@ void AutomationClip::saveSettings( QDomDocument & _doc, QDomElement & _this )
element.setAttribute("pos", POS(it));
element.setAttribute("value", INVAL(it));
element.setAttribute("outValue", OUTVAL(it));
element.setAttribute("inTan", INTAN(it));
element.setAttribute("outTan", OUTTAN(it));
element.setAttribute("lockedTan", static_cast<int>(LOCKEDTAN(it)));
_this.appendChild( element );
}

Expand All @@ -804,6 +872,11 @@ void AutomationClip::loadSettings( const QDomElement & _this )
{
QMutexLocker m(&m_clipMutex);

// Legacy compatibility: Previously tangents were not stored in
// the project file. So if any node doesn't have tangent information
// we will generate the tangents
bool shouldGenerateTangents = false;
sakertooth marked this conversation as resolved.
Show resolved Hide resolved

clear();

movePosition( _this.attribute( "pos" ).toInt() );
Expand All @@ -828,6 +901,22 @@ void AutomationClip::loadSettings( const QDomElement & _this )
float timeMapOutValue = LocaleHelper::toFloat(element.attribute("outValue"));

m_timeMap[timeMapPos] = AutomationNode(this, timeMapInValue, timeMapOutValue, timeMapPos);

// Load tangents if there is information about it (it's enough to check for either inTan or outTan)
if (element.hasAttribute("inTan"))
{
float inTan = LocaleHelper::toFloat(element.attribute("inTan"));
float outTan = LocaleHelper::toFloat(element.attribute("outTan"));
bool lockedTan = static_cast<bool>(element.attribute("lockedTan", "0").toInt());

m_timeMap[timeMapPos].setInTangent(inTan);
m_timeMap[timeMapPos].setOutTangent(outTan);
m_timeMap[timeMapPos].setLockedTangents(lockedTan);
}
else
{
shouldGenerateTangents = true;
}
}
else if( element.tagName() == "object" )
{
Expand All @@ -851,7 +940,8 @@ void AutomationClip::loadSettings( const QDomElement & _this )
{
changeLength( len );
}
generateTangents();

if (shouldGenerateTangents) { generateTangents(); }
}


Expand Down Expand Up @@ -1108,6 +1198,12 @@ void AutomationClip::generateTangents(timeMap::iterator it, int numToGenerate)

for (int i = 0; i < numToGenerate && it != m_timeMap.end(); ++i, ++it)
{
// Skip the node if it has locked tangents (were manually edited)
if (LOCKEDTAN(it))
{
continue;
}

if (it + 1 == m_timeMap.end())
{
// Previously, the last value's tangent was always set to 0. That logic was kept for both tangents
Expand Down
Loading
Loading