Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
81 changes: 64 additions & 17 deletions gui/CustomCurve.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "CustomCurve.h"

#include <ImGui/imgui.h>
#include <algorithm>
#include <sstream>
#include <string>
Expand Down Expand Up @@ -27,21 +28,63 @@ void CustomCurve::ApplyCurveConstraints() {
}

// Cubic Bezier derivatives
static ImVec2 BezierFirstOrderDerivative(ImVec2 p0, ImVec2 p1, ImVec2 p2, ImVec2 p3, float t) {
static ImVec2 CubicBezierFirstOrderDerivative(ImVec2 p0, ImVec2 p1, ImVec2 p2, ImVec2 p3, float t) {
float u = (1 - t);
float w1 = 3 * (u * u);
float w2 = 6 * (u * t);
float w3 = 3 * (t * t);
return ((p1 - p0) * w1) + ((p2 - p1) * w2) + ((p3 - p2) * w3);
}

static ImVec2 BezierSecondOrderDerivative(ImVec2 p0, ImVec2 p1, ImVec2 p2, ImVec2 p3, float t) {
static ImVec2 CubicBezierSecondOrderDerivative(ImVec2 p0, ImVec2 p1, ImVec2 p2, ImVec2 p3, float t) {
float u = (1 - t);
float w1 = 6 * u;
float w2 = 6 * t;
return (p2 - p1 * 2 + p0) * w1 + (p3 - p2 * 2 + p1) * w2;
}

static ImVec2 QuadraticBezierFirstOrderDerivative(ImVec2 p0, ImVec2 p1, ImVec2 p2, float t) {
float u = (1 - t);
float w1 = 2 * u;
float w2 = 2 * t;
return (p0 - p1) * w1 + (p2 - p1) * w2;
}

static ImVec2 QuadraticBezierSecondOrderDerivative(ImVec2 p0, ImVec2 p1, ImVec2 p2, float t) {
return ( p2 - p1 *2.f + p0 ) * 2;
}

static ImVec2 BezierFirstOrderDerivative(ImVec2 p0, ControlPoint_Vec2 p1, ControlPoint_Vec2 p2, ControlPoint_Vec2 p3,float t) {
if (p1.enabled && p2.enabled)
return CubicBezierFirstOrderDerivative(p0, p1, p2, p3, t);

if (p1.enabled || p2.enabled)
return QuadraticBezierFirstOrderDerivative(p0, p1.enabled ? p1 : p2, p3, t);

return p0 - p3;
}

static ImVec2 BezierSecondOrderDerivative(ImVec2 p0, ControlPoint_Vec2 p1, ControlPoint_Vec2 p2, ControlPoint_Vec2 p3, float t) {
if (p1.enabled && p2.enabled)
return CubicBezierSecondOrderDerivative(p0, p1, p2, p3, t);
if (p1.enabled || p2.enabled)
return QuadraticBezierSecondOrderDerivative(p0, p1.enabled ? p1 : p2, p3, t);
return ImVec2(0, 0);
}

static ImVec2 BezierCalc(ImVec2 startPoint, ControlPoint_Vec2 controlPoint1, ControlPoint_Vec2 controlPoint2,
ImVec2 endPoint, float t) {
if (controlPoint1.enabled && controlPoint2.enabled) {
return ImBezierCubicCalc(startPoint, controlPoint1, controlPoint2, endPoint, t);
}

if (controlPoint1.enabled || controlPoint2.enabled) {
return ImBezierQuadraticCalc(startPoint, controlPoint1.enabled ? controlPoint1 : controlPoint2, endPoint, t);
}

return ImLerp(startPoint, endPoint, t);
}

// Spreads points based on the rate of change and other things
// Sorry for the shear number of magic numbers in this function, there is just a lot to configure
int CustomCurve::ExportCurveToLUT(double *LUT_data_x, double *LUT_data_y) const {
Expand All @@ -66,7 +109,7 @@ int CustomCurve::ExportCurveToLUT(double *LUT_data_x, double *LUT_data_y) const
curve_arc_len[idx++] = 0;
for (int j = 1; j < PRE_LUT_ARRAY_SIZE; j++) {
const double t = j / (PRE_LUT_ARRAY_SIZE - 1.0);
ImVec2 p = ImBezierCubicCalc(points[i], control_points[i][0], control_points[i][1], points[i + 1], t);
ImVec2 p = BezierCalc(points[i], control_points[i][0], control_points[i][1], points[i + 1], t);
ImVec2 dist = p - last_p;
dist.y *= 30; // The graph is stretched horizontally, this accounts for that
curve_arc_len[idx] = curve_arc_len[idx - 1] + std::sqrt(ImLengthSqr(dist));
Expand Down Expand Up @@ -127,7 +170,7 @@ int CustomCurve::ExportCurveToLUT(double *LUT_data_x, double *LUT_data_y) const
int start = edge_idx * PRE_LUT_ARRAY_SIZE;
t = linear_map_t(tg_arc_len, start);

auto p = ImBezierCubicCalc(points[edge_idx], control_points[edge_idx][0], control_points[edge_idx][1],
auto p = BezierCalc(points[edge_idx], control_points[edge_idx][0], control_points[edge_idx][1],
points[edge_idx + 1], t);
auto dp = BezierFirstOrderDerivative(points[edge_idx], control_points[edge_idx][0], control_points[edge_idx][1],
points[edge_idx + 1], t);
Expand Down Expand Up @@ -180,7 +223,7 @@ int CustomCurve::ExportCurveToLUT(double *LUT_data_x, double *LUT_data_y) const
}

// ctrl_p -> control_point
// Format: point[i].x;point[i].y;ctrl_p[i][0].x,ctrl_p[i][0].y;ctrl_p[i][1].x;ctrl_p[i][1].y;point[i+1].x;point[i+1].y;ctrl_p[i][0].x;ctrl_p[i][0].y;ctrl_p[i][1].x;ctrl_p[i][1].y;point[i+1].x.y;...point[n-1].x;point[n-1];
// Format: point[i].x;point[i].y;ctrl_p[i][0].x,ctrl_p[i][0].y;ctrl_p[i][0].enabled;ctrl_p[i][1].x;ctrl_p[i][1].y;ctrl_p[i][1].enabled;point[i+1].x;point[i+1].y;ctrl_p[i][0].x;ctrl_p[i][0].y;ctrl_p[i][0].enabled;ctrl_p[i][1].x;ctrl_p[i][1].y;ctrl_p[i][1].enabled;point[i+1].x.y;...point[n-1].x;point[n-1];
std::string CustomCurve::ExportCustomCurve() const {
std::stringstream out_stream;

Expand All @@ -196,7 +239,7 @@ std::string CustomCurve::ExportCustomCurve() const {
// Print control points
if (i < points.size() - 1) {
for (auto & cp : control_points[i]) {
out_stream << cp.x << ";" << cp.y << ";";
out_stream << cp.x << ";" << cp.y << ";" << cp.enabled << ";";
}
out_stream.seekp(-1, std::ios_base::cur); // remove the last comma
out_stream << ";";
Expand Down Expand Up @@ -225,22 +268,22 @@ bool CustomCurve::ImportCustomCurve(const std::string& data) {
float p_x = 0, p_y = 0;
double p = 0;
while (idx < MAX_LUT_ARRAY_SIZE && ss >> p) {
if (idx % 2 == 0)
if ((idx % 8 == 0) || (idx % 8 == 2) || (idx % 8 == 5))
p_x = p;
else {
else if ((idx % 8 == 1) || (idx % 8 == 3) || (idx % 8 == 6)) {
p_y = p;
if (idx % 6 == 1) {
if (idx % 8 == 1) {
points.emplace_back(p_x, p_y);
}
else {
if (idx % 6 == 3)
control_points.emplace_back();
auto j = idx % 6 - 2;
control_points.back()[j / 2] = {p_x, p_y};
}
else if ((idx % 8 == 4) || (idx % 8 == 7)) {
if (idx % 8 == 4) {
control_points.emplace_back();
}
auto j = (idx % 8) % 2;
control_points.back()[j] = {p_x, p_y, (bool)p};
}


char nextC = ss.peek();
if (nextC == ';' || nextC == ',')
ss.ignore();
Expand All @@ -249,11 +292,15 @@ bool CustomCurve::ImportCustomCurve(const std::string& data) {
}
}
catch (std::exception& exception) {
points.clear();
control_points.clear();
return false;
}

// 1 element is not enough for a linear interpolation, and n = 3(i - 2) + 4 (where i is the number of nodes), idx 2x because of x/y coordinate
if (idx <= 2 || idx % 6 - 2 != 0) {
if (idx <= 2 || idx % 8 - 2 != 0) {
points.clear();
control_points.clear();
return false;
}

Expand Down Expand Up @@ -347,7 +394,7 @@ void CustomCurve::UpdateLUT() {
for (int i = 0; i < points.size() - 1; i++) {
for (int j = 0; j < BEZIER_FRAG_SEGMENTS; ++j) {
float t = (float) j / (BEZIER_FRAG_SEGMENTS - 1);
ImVec2 p = ImBezierCubicCalc(points[i], control_points[i][0], control_points[i][1], points[i + 1], t);
ImVec2 p = BezierCalc(points[i], control_points[i][0], control_points[i][1], points[i + 1], t);
LUT_points[i * BEZIER_FRAG_SEGMENTS + j] = p;
if (last_point.x > p.x) {
// Bad Curve
Expand Down
11 changes: 10 additions & 1 deletion gui/CustomCurve.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,19 @@ struct Ex_Vec2 : ImVec2 {
Ex_Vec2() : ImVec2(0, 0) {}
};

struct ControlPoint_Vec2 : ImVec2 {
bool enabled = true;

ControlPoint_Vec2(float x, float y) : ImVec2(x, y) {}
ControlPoint_Vec2(float x, float y, bool enabled) : ImVec2(x, y), enabled(enabled) {}
ControlPoint_Vec2(ImVec2 vec) : ImVec2(vec) {}
ControlPoint_Vec2() : ImVec2(0, 0) {}
};

class CustomCurve {
public:
std::deque<Ex_Vec2> points{{5, 1}, {50, 2}}; // actual points
std::deque<std::array<ImVec2, 2> > control_points{std::array<ImVec2, 2>({ImVec2{40, 1}, ImVec2{20, 2}})};
std::deque<std::array<ControlPoint_Vec2, 2> > control_points{std::array<ControlPoint_Vec2, 2>({ImVec2{40, 1}, ImVec2{20, 2}})};
std::vector<ImPlotPoint> LUT_points{};

CustomCurve() = default;
Expand Down
Loading
Loading