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/Serial.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/String.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