From c1b86b5553baae0d9725fbb47f7a24bf176e9e66 Mon Sep 17 00:00:00 2001 From: Sylar Date: Tue, 11 Nov 2025 19:13:13 +0100 Subject: [PATCH] Added class --- CMakeLists.txt | 1 + other/build.nr | 2 +- src/ostd/Spline.cpp | 158 ++++++++++++++++++++++++++++++++++++++++++++ src/ostd/Spline.hpp | 83 +++++++++++++++++++++++ 4 files changed, 243 insertions(+), 1 deletion(-) create mode 100755 src/ostd/Spline.cpp create mode 100755 src/ostd/Spline.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ca6bc14..14f1b63 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/other/build.nr b/other/build.nr index c9ac2c6..78d529c 100644 --- a/other/build.nr +++ b/other/build.nr @@ -1 +1 @@ -1907 +1908 diff --git a/src/ostd/Spline.cpp b/src/ostd/Spline.cpp new file mode 100755 index 0000000..1ea4799 --- /dev/null +++ b/src/ostd/Spline.cpp @@ -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(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(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)); + } + } +} diff --git a/src/ostd/Spline.hpp b/src/ostd/Spline.hpp new file mode 100755 index 0000000..3b0ab24 --- /dev/null +++ b/src/ostd/Spline.hpp @@ -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 +#include +#include + +namespace ostd +{ + struct tSplineNode + { + Vec2 position; + float length; + }; + + class RenderCore; + class Spline : public BaseObject + { + public: + using EventHandlerCallback = std::function; + using DrawCallback = std::function; + + 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 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