Added <ostd/Spline.hpp> class

This commit is contained in:
Sylar 2025-11-11 19:13:13 +01:00
parent 339d077d50
commit c1b86b5553
4 changed files with 243 additions and 1 deletions

View file

@ -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

View file

@ -1 +1 @@
1907
1908

158
src/ostd/Spline.cpp Executable file
View 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
View 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