Added <ostd/Spline.hpp> class
This commit is contained in:
parent
339d077d50
commit
c1b86b5553
4 changed files with 243 additions and 1 deletions
|
|
@ -51,6 +51,7 @@ list(APPEND OSTD_SOURCE_FILES
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/ostd/Random.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/ostd/Random.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/ostd/Serial.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/ostd/Serial.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/ostd/SineWave.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/ostd/SineWave.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/ostd/Spline.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/ostd/StringEditor.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/ostd/StringEditor.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/ostd/String.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/ostd/String.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/ostd/TextStyleParser.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/ostd/TextStyleParser.cpp
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
1907
|
1908
|
||||||
|
|
|
||||||
158
src/ostd/Spline.cpp
Executable file
158
src/ostd/Spline.cpp
Executable file
|
|
@ -0,0 +1,158 @@
|
||||||
|
#include "Spline.hpp"
|
||||||
|
#include "WindowBase.hpp"
|
||||||
|
|
||||||
|
namespace ostd
|
||||||
|
{
|
||||||
|
Spline::Spline(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Spline::handleSignal(tSignal& signal)
|
||||||
|
{
|
||||||
|
if (!isEditable()) return;
|
||||||
|
if (isUsingCustomEventHandler() && m_eventHandlerCallback)
|
||||||
|
{
|
||||||
|
m_eventHandlerCallback(signal);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (signal.ID == tBuiltinSignals::MousePressed)
|
||||||
|
{
|
||||||
|
ogfx::MouseEventData& evt = static_cast<ogfx::MouseEventData&>(signal.userData);
|
||||||
|
for (auto& node : m_points)
|
||||||
|
{
|
||||||
|
if (ostd::Rectangle(node.position.x - 20, node.position.y - 20, 40, 40).contains((float)evt.position_x, (float)evt.position_y))
|
||||||
|
{
|
||||||
|
m_selectedNode = &node;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (signal.ID == tBuiltinSignals::MouseReleased)
|
||||||
|
{
|
||||||
|
if (m_selectedNode != nullptr)
|
||||||
|
updateTotalLength();
|
||||||
|
m_selectedNode = nullptr;
|
||||||
|
}
|
||||||
|
else if (signal.ID == tBuiltinSignals::MouseMoved)
|
||||||
|
{
|
||||||
|
ogfx::MouseEventData& evt = static_cast<ogfx::MouseEventData&>(signal.userData);
|
||||||
|
if (m_selectedNode != nullptr)
|
||||||
|
m_selectedNode->position = { (float)evt.position_x, (float)evt.position_y };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tSplineNode Spline::getPoint(float t)
|
||||||
|
{
|
||||||
|
int32_t p0, p1, p2, p3;
|
||||||
|
if (!isLooping())
|
||||||
|
{
|
||||||
|
p1 = (int32_t)t + 1;
|
||||||
|
p2 = p1 + 1;
|
||||||
|
p3 = p2 + 1;
|
||||||
|
p0 = p1 - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p1 = (int32_t)t;
|
||||||
|
p2 = (p1 + 1) % m_points.size();
|
||||||
|
p3 = (p2 + 1) % m_points.size();
|
||||||
|
p0 = p1 >= 1 ? p1 - 1 : m_points.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
t = t - (int32_t)t;
|
||||||
|
|
||||||
|
float tt = t * t;
|
||||||
|
float ttt = tt * t;
|
||||||
|
|
||||||
|
float q1 = -ttt + 2.0f*tt - t;
|
||||||
|
float q2 = 3.0f*ttt - 5.0f*tt + 2.0f;
|
||||||
|
float q3 = -3.0f*ttt + 4.0f*tt + t;
|
||||||
|
float q4 = ttt - tt;
|
||||||
|
|
||||||
|
float tx = 0.5f * (m_points[p0].position.x * q1 + m_points[p1].position.x * q2 + m_points[p2].position.x * q3 + m_points[p3].position.x * q4);
|
||||||
|
float ty = 0.5f * (m_points[p0].position.y * q1 + m_points[p1].position.y * q2 + m_points[p2].position.y * q3 + m_points[p3].position.y * q4);
|
||||||
|
|
||||||
|
return { { tx, ty }, 0.0f };
|
||||||
|
}
|
||||||
|
|
||||||
|
tSplineNode Spline::getGradient(float t)
|
||||||
|
{
|
||||||
|
int32_t p0, p1, p2, p3;
|
||||||
|
if (!isLooping())
|
||||||
|
{
|
||||||
|
p1 = (int32_t)t + 1;
|
||||||
|
p2 = p1 + 1;
|
||||||
|
p3 = p2 + 1;
|
||||||
|
p0 = p1 - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p1 = (int32_t)t;
|
||||||
|
p2 = (p1 + 1) % m_points.size();
|
||||||
|
p3 = (p2 + 1) % m_points.size();
|
||||||
|
p0 = p1 >= 1 ? p1 - 1 : m_points.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
t = t - (int32_t)t;
|
||||||
|
|
||||||
|
float tt = t * t;
|
||||||
|
float ttt = tt * t;
|
||||||
|
|
||||||
|
float q1 = -3.0f * tt + 4.0f*t - 1;
|
||||||
|
float q2 = 9.0f*tt - 10.0f*t;
|
||||||
|
float q3 = -9.0f*tt + 8.0f*t + 1.0f;
|
||||||
|
float q4 = 3.0f*tt - 2.0f*t;
|
||||||
|
|
||||||
|
float tx = 0.5f * (m_points[p0].position.x * q1 + m_points[p1].position.x * q2 + m_points[p2].position.x * q3 + m_points[p3].position.x * q4);
|
||||||
|
float ty = 0.5f * (m_points[p0].position.y * q1 + m_points[p1].position.y * q2 + m_points[p2].position.y * q3 + m_points[p3].position.y * q4);
|
||||||
|
|
||||||
|
return { { tx, ty }, 0.0f };
|
||||||
|
}
|
||||||
|
|
||||||
|
float Spline::updateSegmentLength(int32_t node)
|
||||||
|
{
|
||||||
|
float fLength = 0.0f;
|
||||||
|
float fStepSize = 0.005;
|
||||||
|
|
||||||
|
tSplineNode old_point, new_point;
|
||||||
|
old_point = getPoint((float)node);
|
||||||
|
|
||||||
|
for (float t = 0; t < 1.0f; t += fStepSize)
|
||||||
|
{
|
||||||
|
new_point = getGradient((float)node + t);
|
||||||
|
fLength += std::sqrt((new_point.position.x - old_point.position.x)*(new_point.position.x - old_point.position.x)
|
||||||
|
+ (new_point.position.y - old_point.position.y)*(new_point.position.y - old_point.position.y));
|
||||||
|
old_point = new_point;
|
||||||
|
}
|
||||||
|
return fLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Spline::getNormalisedOffset(float p)
|
||||||
|
{
|
||||||
|
int32_t i = 0;
|
||||||
|
while (p > m_points[i].length)
|
||||||
|
{
|
||||||
|
p -= m_points[i].length;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return (float)i + (p / m_points[i].length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Spline::connectSignals(void)
|
||||||
|
{
|
||||||
|
if (m_signalsConnected) return;
|
||||||
|
connectSignal(tBuiltinSignals::MouseMoved);
|
||||||
|
connectSignal(tBuiltinSignals::MousePressed);
|
||||||
|
connectSignal(tBuiltinSignals::MouseReleased);
|
||||||
|
m_signalsConnected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Spline::updateTotalLength(void)
|
||||||
|
{
|
||||||
|
m_totalLength = 0.0f;
|
||||||
|
for (int i = 0; i < m_points.size(); i++)
|
||||||
|
{
|
||||||
|
m_totalLength += (m_points[i].length = updateSegmentLength(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
83
src/ostd/Spline.hpp
Executable file
83
src/ostd/Spline.hpp
Executable file
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
License
|
||||||
|
~~~~~~~
|
||||||
|
Copyright (C) 2018 Javidx9
|
||||||
|
GNU GPLv3 https://github.com/OneLoneCoder/videos/blob/master/LICENSE
|
||||||
|
|
||||||
|
Author
|
||||||
|
~~~~~~
|
||||||
|
Twitter: @javidx9
|
||||||
|
Blog: www.onelonecoder.com
|
||||||
|
Original works located at:
|
||||||
|
https://www.github.com/onelonecoder
|
||||||
|
https://www.onelonecoder.com
|
||||||
|
https://www.youtube.com/javidx9
|
||||||
|
|
||||||
|
Videos Explaining Splines:
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
https://youtu.be/9_aJGUTePYo
|
||||||
|
https://youtu.be/DzjtU4WLYNs
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SPLINE_HPP__
|
||||||
|
#define __SPLINE_HPP__
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <ostd/Geometry.hpp>
|
||||||
|
#include <ostd/Signals.hpp>
|
||||||
|
|
||||||
|
namespace ostd
|
||||||
|
{
|
||||||
|
struct tSplineNode
|
||||||
|
{
|
||||||
|
Vec2 position;
|
||||||
|
float length;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RenderCore;
|
||||||
|
class Spline : public BaseObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using EventHandlerCallback = std::function<void(tSignal&)>;
|
||||||
|
using DrawCallback = std::function<void(float resolution, float lineWidth, float controlPointSize)>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Spline(void);
|
||||||
|
void handleSignal(tSignal& signal) override;
|
||||||
|
tSplineNode getPoint(float t);
|
||||||
|
tSplineNode getGradient(float t);
|
||||||
|
float updateSegmentLength(int32_t node);
|
||||||
|
float getNormalisedOffset(float p);
|
||||||
|
void connectSignals(void);
|
||||||
|
void updateTotalLength(void);
|
||||||
|
inline void draw(float resolution = 0.01f, float lineWidth = 4.0f, float controlPointSize = 9.0f) { if (m_drawCallback) m_drawCallback(resolution, lineWidth, controlPointSize); }
|
||||||
|
inline bool exists(void) { return m_points.size() > 1; }
|
||||||
|
inline void addPoint(Vec2 position) { m_points.push_back({ position, 0.0f }); updateTotalLength(); }
|
||||||
|
inline float getTotalLength(void) { return m_totalLength; }
|
||||||
|
inline bool isEditable(void) { return m_editable; }
|
||||||
|
inline bool isLooping(void) { return m_looping; }
|
||||||
|
inline void setEditable(bool e) { m_editable = e; }
|
||||||
|
inline void setLooping(bool l) { m_looping = l; }
|
||||||
|
inline uint32_t getPointCount(void) { return m_points.size(); }
|
||||||
|
inline void clear(void) { m_points.clear(); }
|
||||||
|
inline bool isEnabled(void) { return m_enabled; }
|
||||||
|
inline void enable(bool e = true) { m_enabled = e; }
|
||||||
|
inline bool isUsingCustomEventHandler(void) { return m_useCustomEventHandler; }
|
||||||
|
inline void useCustomEventHandler(bool use = true, EventHandlerCallback callback = nullptr);
|
||||||
|
inline void setDrawCallback(DrawCallback drawCallback) { m_drawCallback = drawCallback; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::vector<tSplineNode> m_points;
|
||||||
|
float m_totalLength = 0.0f;
|
||||||
|
tSplineNode* m_selectedNode { nullptr };
|
||||||
|
bool m_editable { false };
|
||||||
|
bool m_looping { true };
|
||||||
|
bool m_enabled { true };
|
||||||
|
bool m_signalsConnected { false };
|
||||||
|
bool m_useCustomEventHandler { false };
|
||||||
|
EventHandlerCallback m_eventHandlerCallback { nullptr };
|
||||||
|
DrawCallback m_drawCallback { nullptr };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
Loading…
Reference in a new issue