aboutsummaryrefslogtreecommitdiff
path: root/tools/CurveEditor/source/Internal/Curve
diff options
context:
space:
mode:
authorBryan Galdrikian <[email protected]>2017-02-24 09:32:20 -0800
committerBryan Galdrikian <[email protected]>2017-02-24 09:32:20 -0800
commite1bf674c16e3c8472b29574159c789cd3f0c64e0 (patch)
tree9f0cfce09c71a2c27ff19589fcad6cd83504477c /tools/CurveEditor/source/Internal/Curve
parentfirst commit (diff)
downloadblast-e1bf674c16e3c8472b29574159c789cd3f0c64e0.tar.xz
blast-e1bf674c16e3c8472b29574159c789cd3f0c64e0.zip
Updating to [email protected] and [email protected] with a new directory structure.
NvBlast folder is gone, files have been moved to top level directory. README is changed to reflect this.
Diffstat (limited to 'tools/CurveEditor/source/Internal/Curve')
-rw-r--r--tools/CurveEditor/source/Internal/Curve/BezierSpline.cpp143
-rw-r--r--tools/CurveEditor/source/Internal/Curve/BezierSpline.h57
-rw-r--r--tools/CurveEditor/source/Internal/Curve/CatmullRomSpline.cpp205
-rw-r--r--tools/CurveEditor/source/Internal/Curve/CatmullRomSpline.h44
-rw-r--r--tools/CurveEditor/source/Internal/Curve/Curve.cpp1114
-rw-r--r--tools/CurveEditor/source/Internal/Curve/Spline.cpp151
-rw-r--r--tools/CurveEditor/source/Internal/Curve/Spline.h43
7 files changed, 1757 insertions, 0 deletions
diff --git a/tools/CurveEditor/source/Internal/Curve/BezierSpline.cpp b/tools/CurveEditor/source/Internal/Curve/BezierSpline.cpp
new file mode 100644
index 0000000..03ab848
--- /dev/null
+++ b/tools/CurveEditor/source/Internal/Curve/BezierSpline.cpp
@@ -0,0 +1,143 @@
+#include "BezierSpline.h"
+
+namespace nvidia {
+namespace CurveEditor {
+
+std::vector<QPointF> BezierSpline::sample(const std::vector<BezierSplinePoint>& splinePoints, long segmentCount)
+{
+ std::vector<QPointF> samplePoints;
+
+ if (segmentCount < 1)
+ return samplePoints;
+
+ long lPointCount = (long)splinePoints.size();
+ float fLengthStep = 1.0 / segmentCount;
+ float ft;
+ QPointF p0;
+ QPointF p1;
+ QPointF p2;
+ QPointF p3;
+ QPointF p01;
+ QPointF p11;
+ QPointF p21;
+ QPointF p02;
+ QPointF p12;
+ QPointF p03;
+
+ for (int i = 1; i < lPointCount; i++)
+ {
+ const BezierSplinePoint& pt1 = splinePoints[i - 1];
+ const BezierSplinePoint& pt2 = splinePoints[i];
+
+ // start point of each Bezier curve segment
+ samplePoints.push_back(pt1.valuePoint);
+
+ // sample each point on each Bezier curve segment by percent value and De Casteljau algorithm
+ // the sample points are not including start point and end poit of the Bezier curve segement
+ for (int j = 1; j < segmentCount; j++)
+ {
+ ft = fLengthStep * j;
+
+ p0 = pt1.valuePoint;
+ p1 = pt1.ctrlPoint1;
+ p2 = pt2.ctrlPoint0;
+ p3 = pt2.valuePoint;
+
+ p01 = p0 + (p1 - p0) * ft;
+ p11 = p1 + (p2 - p1) * ft;
+ p21 = p2 + (p3 - p2) * ft;
+
+ p02 = p01 + (p11 - p01) * ft;\
+ p12 = p11 + (p21 - p11) * ft;
+ p03 = p02 + (p12 - p02) * ft;
+
+ samplePoints.push_back(p03);
+ }
+
+ if (i == lPointCount - 1)
+ {// end point of last Bezier curve segment
+ samplePoints.push_back(pt2.valuePoint);
+ }
+ }
+ return samplePoints;
+}
+
+BezierSpline::BezierSpline(long segmentCount)
+ : Spline(segmentCount)
+ , _controlPoints()
+{
+}
+
+bool BezierSpline::getControlPoint(int index, BezierSplinePoint &pt)
+{
+ long lPointCount = (long)_controlPoints.size();
+
+ if (lPointCount == 0)
+ {
+ return false;
+ }
+
+ index = index < 0 ? 0 : (index >= lPointCount ? lPointCount - 1: index);
+
+ pt = _controlPoints[index];
+
+ return true;
+}
+
+void BezierSpline::appendControlPoint(BezierSplinePoint pt)
+{
+ _controlPoints.push_back(pt);
+
+ _needSample = true;
+}
+
+void BezierSpline::insertControlPoint(int index, BezierSplinePoint pt)
+{
+ long lPointCount = (long)_controlPoints.size();
+
+ index = index < 0 ? 0 : (index >= lPointCount ? lPointCount: index);
+
+ _controlPoints.insert(_controlPoints.begin() + index, pt);
+
+ _needSample = true;
+}
+
+bool BezierSpline::setControlPoint(int index, BezierSplinePoint pt)
+{
+ long lPointCount = (long)_controlPoints.size();
+
+ if (0 <= index && index < lPointCount)
+ {
+ _controlPoints[index] = pt;
+ _needSample = true;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool BezierSpline::removeControlPoint(int index)
+{
+ long lPointCount = (long)_controlPoints.size();
+
+ if (0 <= index && index < lPointCount)
+ {
+ _controlPoints.erase(_controlPoints.begin() + index);
+ _needSample = true;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void BezierSpline::_doSample()
+{
+ _samplePoints = sample(_controlPoints, _segmentCount);
+}
+
+} // namespace CurveEditor
+} // namespace nvidia \ No newline at end of file
diff --git a/tools/CurveEditor/source/Internal/Curve/BezierSpline.h b/tools/CurveEditor/source/Internal/Curve/BezierSpline.h
new file mode 100644
index 0000000..ba5da46
--- /dev/null
+++ b/tools/CurveEditor/source/Internal/Curve/BezierSpline.h
@@ -0,0 +1,57 @@
+#ifndef BEZIERSPLINE_H
+#define BEZIERSPLINE_H
+#include <vector>
+#include <QtCore/QPointF>
+#include "Spline.h"
+
+namespace nvidia {
+namespace CurveEditor {
+
+class BezierSplinePoint
+{
+public:
+ QPointF ctrlPoint0;
+ QPointF valuePoint;
+ QPointF ctrlPoint1;
+};
+
+class BezierSpline : public Spline
+{
+public:
+ static std::vector<QPointF> sample(const std::vector<BezierSplinePoint>& splinePoints, long segmentCount = 100);
+
+ BezierSpline(long segmentCount = 100);
+
+ virtual ~BezierSpline() {}
+
+ inline long getControlPointCount() { return (long)_controlPoints.size(); }
+
+ // index <= 0, return the first point;
+ // index >= point count, return the last point.
+ bool getControlPoint(int index, BezierSplinePoint &pt);
+
+ // add a spline point to last
+ void appendControlPoint(BezierSplinePoint pt);
+
+ // insert a spline point at the identified position.
+ // index <= 0, inert to fisrt;
+ // index >= point count, insert to last.
+ void insertControlPoint(int index, BezierSplinePoint pt);
+
+ // change a spline point value at the identified position.
+ bool setControlPoint(int index, BezierSplinePoint pt);
+
+ // remove the identified spline point.
+ bool removeControlPoint(int index);
+
+private:
+ virtual void _doSample();
+
+private:
+ std::vector<BezierSplinePoint> _controlPoints;
+};
+
+} // namespace CurveEditor
+} // namespace nvidia
+
+#endif // BEZIERSPLINE_H
diff --git a/tools/CurveEditor/source/Internal/Curve/CatmullRomSpline.cpp b/tools/CurveEditor/source/Internal/Curve/CatmullRomSpline.cpp
new file mode 100644
index 0000000..abe80bb
--- /dev/null
+++ b/tools/CurveEditor/source/Internal/Curve/CatmullRomSpline.cpp
@@ -0,0 +1,205 @@
+#include "CatmullRomSpline.h"
+
+namespace nvidia {
+namespace CurveEditor {
+
+float CatmullRomSpline::splineInterpolate(float v0, float v1, float v2, float v3, float u)
+{
+ float basis[4];
+
+ {
+ _computeSplineBasis(u, basis);
+ return
+ basis[0] * v0 +
+ basis[1] * v1 +
+ basis[2] * v2 +
+ basis[3] * v3;
+ }
+}
+
+QPointF CatmullRomSpline::splineInterpolate(const QPointF& p0, const QPointF& p1, const QPointF& p2, const QPointF& p3, float u)
+{
+ float basis[4];
+
+ {
+ _computeSplineBasis(u, basis);
+ return
+ QPointF(basis[0] * p0.x() +
+ basis[1] * p1.x() +
+ basis[2] * p2.x() +
+ basis[3] * p3.x(),
+ basis[0] * p0.y() +
+ basis[1] * p1.y() +
+ basis[2] * p2.y() +
+ basis[3] * p3.y());
+ }
+}
+
+std::vector<QPointF> CatmullRomSpline::sample(const std::vector<QPointF>& inControlPoints, int numSegments)
+{
+ std::vector<QPointF> samplePoints;
+ int numPoints = (int)inControlPoints.size();
+
+ for (int i = 0; i < numPoints - 1; i++)
+ {
+ QPointF p0, p1, p2, p3;
+
+ if (i == 0)
+ {
+ p0 = inControlPoints[0], p1 = inControlPoints[0], p2 = inControlPoints[1];
+ if (2 < numPoints)
+ p3 = inControlPoints[2];
+ else
+ p3 = inControlPoints[1];
+
+ }
+ else if (0 < i && i < numPoints - 2)
+ {
+ p0 = inControlPoints[i-1], p1 = inControlPoints[i], p2 = inControlPoints[i+1], p3 = inControlPoints[i+2];
+ }
+ else
+ {
+ if (2 < numPoints)
+ p0 = inControlPoints[i-1];
+ else
+ p0 = inControlPoints[i];
+ p1 = inControlPoints[i], p2 = inControlPoints[i+1], p3 = inControlPoints[i+1];
+ }
+
+ for (int j = 0; j <= numSegments; j++)
+ {
+ float u = j / float(numSegments);
+
+ samplePoints.push_back(splineInterpolate(p0, p1, p2, p3, u));
+ }
+ }
+
+ return samplePoints;
+}
+
+void CatmullRomSpline::_computeSplineBasis(float u, float* basis)
+{
+ float u2 = u * u;
+ float u3 = u2 * u;
+
+ basis[0] = -0.5f * u3 + 1.0f * u2 - 0.5f * u + 0.0f;
+ basis[1] = 1.5f * u3 - 2.5f * u2 + 0.0f * u + 1.0f;
+ basis[2] = -1.5f * u3 + 2.0f * u2 + 0.5f * u + 0.0f;
+ basis[3] = 0.5f * u3 - 0.5f * u2 + 0.0f * u + 0.0f;
+}
+
+CatmullRomSpline::CatmullRomSpline(long segmentCount)
+ : Spline(segmentCount)
+ , _controlPoints()
+{
+ _segmentCount = segmentCount < 1 ? 1 : segmentCount;
+}
+
+CatmullRomSpline::CatmullRomSpline(int numControlPoints, long segmentCount)
+ : Spline(segmentCount)
+ , _controlPoints()
+{
+ for (int i = 0; i < numControlPoints; ++i)
+ {
+ _controlPoints.push_back(QPointF(i / float(numControlPoints - 1), 1.0f));
+ }
+}
+
+CatmullRomSpline::~CatmullRomSpline(void)
+{
+}
+
+void CatmullRomSpline::setControlPoint(int index, const QPointF& point)
+{
+ std::vector<QPointF>::iterator itr = _controlPoints.begin();
+ std::advance(itr, index);
+ *itr = point;
+}
+
+QPointF CatmullRomSpline::getControlPoint(int index)
+{
+ std::vector<QPointF>::iterator itr = _controlPoints.begin();
+ std::advance(itr, index);
+ return *itr;
+}
+
+std::vector<QPointF> CatmullRomSpline::getControlPoints()
+{
+ return _controlPoints;
+}
+
+void CatmullRomSpline::reset(void)
+{
+ int i = 0;
+ for (std::vector<QPointF>::iterator itr = _controlPoints.begin(); itr != _controlPoints.end(); ++itr, ++i)
+ {
+ itr->setX(i / float(_controlPoints.size() - 1));
+ itr->setY(1.0f);
+ }
+}
+
+void CatmullRomSpline::insertControlPointAt(int index)
+{
+ QPointF p0, p1, p2, p3;
+
+ QPointF newPoint;
+ std::vector<QPointF>::iterator itr = _controlPoints.begin();
+
+ if (index == 0)
+ {
+ return;
+ }
+ else if (index == 1)
+ {
+ p0 = _controlPoints[0], p1 = _controlPoints[0], p2 = _controlPoints[1];
+ if (_controlPoints.size() > 2)
+ p3 = _controlPoints[2];
+ else
+ p3 = _controlPoints[1];
+ newPoint = splineInterpolate(p0, p1, p2, p3, 0.5);
+ }
+ else if (index > (int)(_controlPoints.size() - 1))
+ {
+ return;
+ }
+ else if (index == (_controlPoints.size() - 1))
+ {
+ if (_controlPoints.size() > 2)
+ p0 = _controlPoints[index-2];
+ else
+ p0 = _controlPoints[index-1];
+ p1 = _controlPoints[index-1], p2 = _controlPoints[index], p3 = _controlPoints[index];
+ newPoint = splineInterpolate(p0, p1, p2, p3, 0.5);
+ }
+ else
+ {
+ p0 = _controlPoints[index - 2], p1 = _controlPoints[index - 1], p2 = _controlPoints[index], p3 = _controlPoints[index + 1];
+ newPoint = splineInterpolate(p0, p1, p2, p3, 0.5);
+ }
+
+ std::advance(itr, index);
+ _controlPoints.insert(itr, newPoint);
+}
+
+void CatmullRomSpline::removeControlPoints(std::vector<int>& points)
+{
+ for (std::vector<int>::iterator itr = points.begin(); itr != points.end(); ++itr)
+ {
+ std::vector<QPointF>::iterator itrToRemove = _controlPoints.begin();
+ std::advance(itrToRemove, *itr);
+ _controlPoints.erase(itrToRemove);
+
+ for (std::vector<int>::iterator itrRight = itr + 1; itrRight != points.end(); ++itrRight)
+ {
+ --(*itrRight);
+ }
+ }
+}
+
+void CatmullRomSpline::_doSample()
+{
+ _samplePoints = sample(_controlPoints, _segmentCount);
+}
+
+} // namespace CurveEditor
+} // namespace nvidia \ No newline at end of file
diff --git a/tools/CurveEditor/source/Internal/Curve/CatmullRomSpline.h b/tools/CurveEditor/source/Internal/Curve/CatmullRomSpline.h
new file mode 100644
index 0000000..71573a9
--- /dev/null
+++ b/tools/CurveEditor/source/Internal/Curve/CatmullRomSpline.h
@@ -0,0 +1,44 @@
+#ifndef CatmullRomSpline_h__
+#define CatmullRomSpline_h__
+
+#include <QtCore/QPoint>
+#include <vector>
+#include "Spline.h"
+
+namespace nvidia {
+namespace CurveEditor {
+
+class CatmullRomSpline : public Spline
+{
+public:
+ static float splineInterpolate(float v0, float v1, float v2, float v3, float u);
+ static QPointF splineInterpolate(const QPointF& p0, const QPointF& p1, const QPointF& p2, const QPointF& p3, float u);
+ static std::vector<QPointF> sample(const std::vector<QPointF>& inControlPoints, int numSegments = 100);
+private:
+ static void _computeSplineBasis(float u, float* basis);
+
+public:
+ CatmullRomSpline(long segmentCount = 100);
+ CatmullRomSpline(int numControlPoints, long segmentCount = 100);
+ virtual ~CatmullRomSpline(void);
+
+ int getControlPointCount() { return (int)_controlPoints.size(); }
+ // add a spline point to last
+ void appendControlPoint(QPointF point) { _controlPoints.push_back(point); }
+ void setControlPoint(int index, const QPointF& point);
+ QPointF getControlPoint(int index);
+ std::vector<QPointF> getControlPoints();
+ void reset(void);
+ void insertControlPointAt(int index);
+ void removeControlPoints(std::vector<int>& points);
+
+private:
+ virtual void _doSample();
+private:
+ std::vector<QPointF> _controlPoints;
+};
+
+} // namespace CurveEditor
+} // namespace nvidia
+
+#endif // CatmullRomSpline_h__
diff --git a/tools/CurveEditor/source/Internal/Curve/Curve.cpp b/tools/CurveEditor/source/Internal/Curve/Curve.cpp
new file mode 100644
index 0000000..01d6351
--- /dev/null
+++ b/tools/CurveEditor/source/Internal/Curve/Curve.cpp
@@ -0,0 +1,1114 @@
+#include "Curve.h"
+#include "BezierSpline.h"
+#include "CatmullRomSpline.h"
+#include <QtCore/QDebug>
+#include <QtWidgets/QMessageBox>
+#include <QtGui/QImage>
+#pragma warning( disable : 4312 )
+#pragma warning( disable : 4996 )
+#include "../../../external/stb_image/stb_image.c"
+
+namespace nvidia {
+namespace CurveEditor {
+
+Curve::Curve(long segmentCount)
+ : _needSample(true)
+ , _initValueRange(false)
+ , _minValue()
+ , _maxValue(1.0f, 1.0f)
+ , _segmentCount(1)
+ , _samplePoints()
+ , _controlPoints()
+ , _defaultControlPoints()
+{
+ _segmentCount = segmentCount < 1 ? 1 : segmentCount;
+}
+
+void Curve::initValueRange(const QPointF& min, const QPointF& max)
+{
+ if (min.x() >= max.x() || min.y() >max.y())
+ {
+ QMessageBox::critical(nullptr, QObject::tr("critical"), QObject::tr("Max value must be larger than min value when initilize value range of curve"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
+ return ;
+ }
+
+ _minValue = min;
+ _maxValue = max;
+ _initValueRange = true;
+}
+
+QPointF Curve::getMinValue()
+{
+ return _minValue;
+}
+
+QPointF Curve::getMaxValue()
+{
+ return _maxValue;
+}
+
+void Curve::appendControlPoint(ControlPoint& controlPoint, bool asDefaultToo)
+{
+ if (!_initValueRange)
+ {
+ QMessageBox::critical(nullptr, QObject::tr("critical"), QObject::tr("Curve must be initilized its value range before being used!"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
+ return;
+ }
+
+ if (controlPoint.value.x() < _minValue.x()
+ || controlPoint.value.x() > _maxValue.x()
+ || controlPoint.value.y() < _minValue.y()
+ || controlPoint.value.y() > _maxValue.y())
+ {
+ QMessageBox::critical(nullptr, QObject::tr("critical"), QObject::tr("The value of the control point to add isn't in value range of the curve to add!"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
+ return;
+ }
+
+ _controlPoints.push_back(controlPoint);
+
+ if (asDefaultToo)
+ {
+ _defaultControlPoints.push_back(controlPoint);
+ }
+
+ _needSample = true;
+}
+
+int Curve::appendControlPoint(float x)
+{
+ size_t count = _controlPoints.size();
+ for (int i = 0; i < count - 1; ++i)
+ {
+ ControlPoint& ctrlPntLeft = _controlPoints[i];
+ ControlPoint& ctrlPntRight = _controlPoints[i + 1];
+ if (ctrlPntLeft.value.x() <= x && x <= ctrlPntRight.value.x())
+ {
+ std::vector<ControlPoint>::iterator itr = _controlPoints.begin();
+ std::advance(itr, i+1);
+ ControlPoint pnt(x, getPointByX(x).y(), ctrlPntLeft.mode, ctrlPntLeft.splineData);
+ _controlPoints.insert(itr, pnt);
+ _needSample = true;
+ return i + 1;
+ }
+ }
+
+ return -1;
+}
+
+void Curve::setControlPoint(int index, const ControlPoint& point)
+{
+ if (index < 0 || index > (_controlPoints.size() - 1) )
+ {
+ QMessageBox::critical(nullptr, QObject::tr("critical"), QObject::tr("Meet invalid index when setting control point for curve!"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
+ return;
+ }
+
+ if (point.value.x() < _minValue.x()
+ || point.value.x() > _maxValue.x()
+ || point.value.y() < _minValue.y()
+ || point.value.y() > _maxValue.y())
+ {
+ QMessageBox::critical(nullptr, QObject::tr("critical"), QObject::tr("The setting value isn't in value range of the curve!"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
+ return;
+ }
+ std::vector<ControlPoint>::iterator itr = _controlPoints.begin();
+ std::advance(itr, index);
+ *itr = point;
+ _needSample = true;
+}
+
+void Curve::reset(void)
+{
+ if (_defaultControlPoints.size() > 0)
+ {
+ _controlPoints = _defaultControlPoints;
+ _needSample = true;
+ }
+}
+
+void Curve::insertControlPointAt(int index)
+{
+ if (index < 0)
+ index = 0;
+ else if (index > (_controlPoints.size() - 1) )
+ index = (int)(_controlPoints.size() - 1);
+
+ std::vector<ControlPoint>::iterator itrToAdd = _controlPoints.begin();
+ std::advance(itrToAdd, index);
+
+ ControlPoint& ctrlPntFront = _controlPoints[index - 1];
+ ControlPoint& ctrlPntBehind = _controlPoints[index];
+
+ switch (ctrlPntFront.mode)
+ {
+ case eDiscret:
+ {
+ ControlPoint ctrlPntToAdd;
+ ctrlPntToAdd.mode = ctrlPntFront.mode;
+ ctrlPntToAdd.value.setX((ctrlPntFront.value.x() + ctrlPntBehind.value.x()) / 2);
+ ctrlPntToAdd.value.setY(ctrlPntFront.value.y());
+ _controlPoints.insert(itrToAdd, ctrlPntToAdd);
+ break;
+ }
+ case eLinear:
+ {
+ ControlPoint ctrlPntToAdd;
+ ctrlPntToAdd.mode = ctrlPntFront.mode;
+ ctrlPntToAdd.value.setX((ctrlPntFront.value.x() + ctrlPntBehind.value.x()) / 2);
+ ctrlPntToAdd.value.setY(ctrlPntFront.value.y() + (ctrlPntBehind.value.y() - ctrlPntFront.value.y()) / 2);
+ _controlPoints.insert(itrToAdd, ctrlPntToAdd);
+ break;
+ }
+ case eBezierSpline:
+ {
+ BezierSplinePoint bezierPnt0;
+ BezierSplinePoint bezierPnt1;
+
+ float xStride = ctrlPntBehind.value.x() - ctrlPntFront.value.x();
+ bezierPnt0.ctrlPoint0 = ctrlPntFront.value;
+ bezierPnt0.valuePoint = ctrlPntFront.value;
+ float xDelta = ctrlPntFront.splineData.outLen * xStride;
+ bezierPnt0.ctrlPoint1 = ctrlPntFront.value + QPointF(xDelta, xDelta * ctrlPntFront.splineData.outTan);
+
+ if (eBezierSpline == ctrlPntBehind.mode)
+ {
+ xDelta = ctrlPntBehind.splineData.inLen * xStride;
+ bezierPnt1.ctrlPoint0 = ctrlPntBehind.value - QPointF(xDelta, xDelta * ctrlPntBehind.splineData.inTan);
+ bezierPnt1.valuePoint = ctrlPntBehind.value;
+ bezierPnt1.ctrlPoint1 = ctrlPntBehind.value;
+ }
+ else
+ {
+ bezierPnt1.ctrlPoint0 = ctrlPntBehind.value;
+ bezierPnt1.valuePoint = ctrlPntBehind.value;
+ bezierPnt1.ctrlPoint1 = ctrlPntBehind.value;
+ }
+
+ BezierSpline spline(100);
+ spline.appendControlPoint(bezierPnt0);
+ spline.appendControlPoint(bezierPnt1);
+ QPointF pntOnSpline;
+ spline.getPointByX(ctrlPntFront.value.x() + xStride / 2, pntOnSpline);
+
+ ControlPoint ctrlPntToAdd;
+ ctrlPntToAdd.mode = eBezierSpline;
+ ctrlPntToAdd.value = pntOnSpline;
+ ctrlPntToAdd.splineData.inLen = 1.0;
+ ctrlPntToAdd.splineData.inTan = 0.0;
+ ctrlPntToAdd.splineData.outLen = 1.0;
+ ctrlPntToAdd.splineData.outTan = 0.0;
+ _controlPoints.insert(itrToAdd, ctrlPntToAdd);
+ break;
+ }
+ case eCatmullRomSpline:
+ {
+ CatmullRomSpline spline;
+
+ int i = index - 1;
+ for (; i >= 0; --i)
+ {
+ if (eCatmullRomSpline != _controlPoints[i].mode)
+ {
+ ++i;
+ break;
+ }
+ }
+
+ if (i < 0)
+ i = 0;
+
+ for (; i < (int)_controlPoints.size(); ++i)
+ {
+ spline.appendControlPoint(_controlPoints[i].value);
+ if (eCatmullRomSpline != _controlPoints[i].mode)
+ break;
+ }
+
+ QPointF pntOnSpline;
+ spline.getPointByX((ctrlPntFront.value.x() + ctrlPntBehind.value.x()) / 2, pntOnSpline);
+ ControlPoint ctrlPntToAdd;
+ ctrlPntToAdd.mode = eCatmullRomSpline;
+ ctrlPntToAdd.value = pntOnSpline;
+ _controlPoints.insert(itrToAdd, ctrlPntToAdd);
+ break;
+ }
+ default:
+ break;
+ }
+
+ _needSample = true;
+}
+
+void Curve::removeControlPoint(int index)
+{
+ std::vector<ControlPoint>::iterator itr = _controlPoints.begin();
+ std::advance(itr, index);
+ _controlPoints.erase(itr);
+ _needSample = true;
+}
+
+void Curve::removeControlPoints(std::vector<int>& indexes)
+{
+ for (std::vector<int>::iterator itr = indexes.begin(); itr != indexes.end(); ++itr)
+ {
+ std::vector<ControlPoint>::iterator itrToRemove = _controlPoints.begin();
+ std::advance(itrToRemove, *itr);
+ _controlPoints.erase(itrToRemove);
+
+ for (std::vector<int>::iterator itrRight = itr + 1; itrRight != indexes.end(); ++itrRight)
+ {
+ --(*itrRight);
+ }
+ }
+
+ _needSample = true;
+}
+
+QPointF Curve::getPointByX(float x)
+{
+ if (!_initValueRange)
+ {
+ QMessageBox::critical(nullptr, QObject::tr("critical"), QObject::tr("Curve must be initilized its value range before being used!"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
+ return QPointF(0, 0);
+ }
+
+ if (x < _controlPoints[0].value.x())
+ return _controlPoints[0].value;
+
+ if (x >= _controlPoints[_controlPoints.size() - 1].value.x())
+ return _controlPoints[_controlPoints.size() - 1].value;
+
+ long lPointCount = (long)_samplePoints.size();
+
+ if(lPointCount < 2)
+ {
+ return QPointF(0,0);
+ }
+
+ if (_needSample)
+ _sample();
+
+ QPointF point;
+
+ for (int i = 0; i < lPointCount - 1; i++)
+ {
+ if(_samplePoints[i].x() <= x && _samplePoints[i + 1].x() > x)
+ {
+ point.setX( x );
+ float fRate = (x - _samplePoints[i].x())/ (_samplePoints[i + 1].x() - _samplePoints[i].x());
+ point.setY(_samplePoints[i].y() + (_samplePoints[i+1].y() - _samplePoints[i].y()) * fRate);
+ return point;
+ }
+ }
+
+ return QPointF(0,0);
+}
+
+std::vector<QPointF> Curve::getSamplePoints()
+{
+ if (!_initValueRange)
+ {
+ QMessageBox::critical(nullptr, QObject::tr("critical"), QObject::tr("Curve must be initilized its value range before being used!"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
+ return std::vector<QPointF>();
+ }
+
+ if (_needSample)
+ _sample();
+
+ return _samplePoints;
+}
+
+Curve Curve::resampleCurve(int resamplePnts, long segmentCount)
+{
+ if (!_initValueRange)
+ {
+ QMessageBox::critical(nullptr, QObject::tr("critical"), QObject::tr("Curve must be initilized its value range before being used!"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
+ return Curve();
+ }
+
+ Curve resmapleCurve(segmentCount);
+ if (resamplePnts < 2)
+ return resmapleCurve;
+ else
+ {
+ int segment = resamplePnts - 1;
+ float xStart = _controlPoints[0].value.x();
+ float xEnd = _controlPoints[_controlPoints.size() - 1].value.x();
+ float strideX = (xEnd - xStart) / segment;
+ for (int i = 0; i <= segment; ++i)
+ {
+ float x = xStart + strideX * i;
+ QPointF pnt = getPointByX(x);
+ ControlPoint& ctrlPntNear = _getNearCtrlPnt(x);
+
+ ControlPoint ctrlPntNew(pnt.x(), pnt.y(), ctrlPntNear.mode);
+
+ if (eBezierSpline == ctrlPntNear.mode)
+ {
+ ctrlPntNew.splineData = ctrlPntNear.splineData;
+ }
+
+ resmapleCurve.appendControlPoint(ctrlPntNew);
+ }
+ }
+
+ return resmapleCurve;
+}
+
+Curve& Curve::operator = (const Curve& right)
+{
+ if (this == &right)
+ return *this;
+
+ _needSample = right._needSample;
+ _initValueRange = right._initValueRange;
+ _minValue = right._minValue;
+ _maxValue = right._maxValue;
+ _segmentCount = right._segmentCount;
+ _samplePoints = right._samplePoints;
+ _controlPoints = right._controlPoints;
+
+ return *this;
+}
+
+void Curve::_sample()
+{
+ _samplePoints.clear();
+
+ for (size_t i = 1; i < _controlPoints.size(); ++i)
+ {
+ ControlPoint& ctrlPntFront = _controlPoints[i-1];
+ ControlPoint& ctrlPntBehind = _controlPoints[i];
+
+ switch (ctrlPntFront.mode)
+ {
+ case eDiscret:
+ {
+ _samplePoints.push_back(ctrlPntFront.value);
+ _samplePoints.push_back(QPointF(ctrlPntBehind.value.x(), ctrlPntFront.value.y()));
+ break;
+ }
+ case eLinear:
+ {
+ _samplePoints.push_back(ctrlPntFront.value);
+ _samplePoints.push_back(ctrlPntBehind.value);
+ break;
+ }
+ case eBezierSpline:
+ {
+ BezierSplinePoint bezierPnt0;
+ BezierSplinePoint bezierPnt1;
+
+ float xStride = ctrlPntBehind.value.x() - ctrlPntFront.value.x();
+ bezierPnt0.ctrlPoint0 = ctrlPntFront.value;
+ bezierPnt0.valuePoint = ctrlPntFront.value;
+ float xDelta = ctrlPntFront.splineData.outLen * xStride;
+ bezierPnt0.ctrlPoint1 = ctrlPntFront.value + QPointF(xDelta, xDelta * ctrlPntFront.splineData.outTan);
+
+ if (eBezierSpline == ctrlPntBehind.mode)
+ {
+ xDelta = ctrlPntBehind.splineData.inLen * xStride;
+ bezierPnt1.ctrlPoint0 = ctrlPntBehind.value - QPointF(xDelta, xDelta * ctrlPntBehind.splineData.inTan);
+ bezierPnt1.valuePoint = ctrlPntBehind.value;
+ bezierPnt1.ctrlPoint1 = ctrlPntBehind.value;
+ }
+ else
+ {
+ bezierPnt1.ctrlPoint0 = ctrlPntBehind.value;
+ bezierPnt1.valuePoint = ctrlPntBehind.value;
+ bezierPnt1.ctrlPoint1 = ctrlPntBehind.value;
+ }
+
+ BezierSpline spline(_segmentCount);
+ spline.appendControlPoint(bezierPnt0);
+ spline.appendControlPoint(bezierPnt1);
+
+ std::vector<QPointF> pnts = spline.getSamplePoints();
+ _samplePoints.insert(_samplePoints.end(), pnts.begin(), pnts.end());
+ break;
+ }
+ case eCatmullRomSpline:
+ {
+ CatmullRomSpline spline(_segmentCount);
+
+ for (i = i - 1; i < _controlPoints.size(); ++i)
+ {
+ spline.appendControlPoint(_controlPoints[i].value);
+ if (eCatmullRomSpline != _controlPoints[i].mode)
+ {
+ break;
+ }
+ }
+
+ std::vector<QPointF> pnts = spline.getSamplePoints();
+ _samplePoints.insert(_samplePoints.end(), pnts.begin(), pnts.end());
+ break;
+ }
+ }
+ }
+
+ _needSample = false;
+}
+
+ControlPoint& Curve::_getNearCtrlPnt(float x)
+{
+ size_t count = _controlPoints.size();
+ for (int i = 0; i < count - 1; ++i)
+ {
+ ControlPoint& ctrlPntLeft = _controlPoints[i];
+ ControlPoint& ctrlPntRight = _controlPoints[i + 1];
+ if (x < ctrlPntLeft.value.x())
+ return ctrlPntLeft;
+ else if (ctrlPntLeft.value.x() <= x && x <= ctrlPntRight.value.x())
+ {
+ if (abs(x - ctrlPntLeft.value.x()) <= abs(x - ctrlPntRight.value.x()))
+ {
+ return ctrlPntLeft;
+ }
+ else
+ {
+ return ctrlPntRight;
+ }
+ }
+ }
+ return _controlPoints[0];
+}
+
+ColorCurve::ColorCurve(long segmentCount)
+ : _needSample(true)
+ , _initValueRange(false)
+ , _minValue(0.0f)
+ , _maxValue(1.0f)
+ , _colorSamplePnts()
+ , _controlPoints()
+ , _defaultControlPoints()
+{
+ _segmentCount = segmentCount < 1 ? 1 : segmentCount;
+}
+
+void ColorCurve::initValueRange(float min, float max)
+{
+ if (min >= max )
+ {
+ QMessageBox::critical(nullptr, QObject::tr("critical"), QObject::tr("Max x must be larger than min x when initilize value range of color curve"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
+ return ;
+ }
+
+ _minValue = min;
+ _maxValue = max;
+ _initValueRange = true;
+}
+
+float ColorCurve::getMinValue()
+{
+ return _minValue;
+}
+
+float ColorCurve::getMaxValue()
+{
+ return _maxValue;
+}
+
+void ColorCurve::appendControlPoint(ColorControlPoint& controlPoint, bool asDefaultToo)
+{
+ if (!_initValueRange)
+ {
+ QMessageBox::critical(nullptr, QObject::tr("critical"), QObject::tr("Color curve must be initilized its value range before being used!"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
+ return;
+ }
+
+ if (controlPoint.x < _minValue
+ || controlPoint.x > _maxValue)
+ {
+ QMessageBox::critical(nullptr, QObject::tr("critical"), QObject::tr("The x value of the control point to add isn't in x value range of the color curve to add!"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
+ return;
+ }
+
+ _controlPoints.push_back(controlPoint);
+
+ if (asDefaultToo)
+ {
+ _defaultControlPoints.push_back(controlPoint);
+ }
+
+ if (!controlPoint.texturePath.empty())
+ {
+ _controlPoints[_controlPoints.size() - 1].textureAverageColor = _calcTextureAverageColor(controlPoint.texturePath.c_str());
+ }
+
+ _needSample = true;
+}
+
+void ColorCurve::setControlPoint(int index, const ColorControlPoint& point)
+{
+ if (index < 0 || index > (_controlPoints.size() - 1) )
+ {
+ QMessageBox::critical(nullptr, QObject::tr("critical"), QObject::tr("Meet invalid index when setting control point for color curve!"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
+ return;
+ }
+
+ if (point.x < _minValue
+ || point.x > _maxValue)
+ {
+ QMessageBox::critical(nullptr, QObject::tr("critical"), QObject::tr("The setting value isn't in x value range of the color curve!"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
+ return;
+ }
+ std::vector<ColorControlPoint>::iterator itr = _controlPoints.begin();
+ std::advance(itr, index);
+
+ if (!point.texturePath.empty() && point.texturePath != itr->texturePath)
+ {
+ *itr = point;
+ itr->textureAverageColor = _calcTextureAverageColor(point.texturePath.c_str());
+ }
+ else
+ *itr = point;
+
+ _needSample = true;
+}
+
+void ColorCurve::reset(void)
+{
+ if (_defaultControlPoints.size() > 0)
+ {
+ _controlPoints = _defaultControlPoints;
+ _needSample = true;
+ }
+}
+
+void rectifyColorValue(float& colorComponent)
+{
+ if (colorComponent < 0)
+ colorComponent = 0;
+ else if(colorComponent > 255)
+ {
+ colorComponent = 255;
+ }
+}
+
+void rectifyColorValue(QPointF& colorComponent)
+{
+ if (colorComponent.y() < 0)
+ colorComponent.setY(0);
+ else if(colorComponent.y() > 255)
+ {
+ colorComponent.setY(255);
+ }
+}
+
+float getBezierColorByX(ColorControlPoint& ctrlPntFront, ColorControlPoint& ctrlPntBehind, int component, float x)
+{
+ BezierSplinePoint bezierPnt0;
+ BezierSplinePoint bezierPnt1;
+
+ float y0 = 0.0f, y1 = 0.0f;
+ switch (component) {
+ case 0:
+ y0 = ctrlPntFront.color.red();
+ y1 = ctrlPntBehind.color.red();
+ break;
+ case 1:
+ y0 = ctrlPntFront.color.green();
+ y1 = ctrlPntBehind.color.green();
+ break;
+ case 2:
+ y0 = ctrlPntFront.color.blue();
+ y1 = ctrlPntBehind.color.blue();
+ break;
+ case 3:
+ y0 = ctrlPntFront.color.alpha();
+ y1 = ctrlPntBehind.color.alpha();
+ break;
+ default:
+ break;
+ }
+
+ QPointF pntFront = QPointF(ctrlPntFront.x, y0);
+ QPointF pntBehind = QPointF(ctrlPntBehind.x, y1);
+ float xStride = ctrlPntBehind.x - ctrlPntFront.x;
+ bezierPnt0.ctrlPoint0 = pntFront;
+ bezierPnt0.valuePoint = pntFront;
+ float xDelta = ctrlPntFront.splineData.outLen * xStride;
+ bezierPnt0.ctrlPoint1 = pntFront + QPointF(xDelta, xDelta * ctrlPntFront.splineData.outTan);
+
+ if (eBezierSpline == ctrlPntBehind.mode)
+ {
+ xDelta = ctrlPntBehind.splineData.inLen * xStride;
+ bezierPnt1.ctrlPoint0 = pntBehind - QPointF(xDelta, xDelta * ctrlPntBehind.splineData.inTan);
+ bezierPnt1.valuePoint = pntBehind;
+ bezierPnt1.ctrlPoint1 = pntBehind;
+ }
+ else
+ {
+ bezierPnt1.ctrlPoint0 = pntBehind;
+ bezierPnt1.valuePoint = pntBehind;
+ bezierPnt1.ctrlPoint1 = pntBehind;
+ }
+
+ BezierSpline spline(100);
+ spline.appendControlPoint(bezierPnt0);
+ spline.appendControlPoint(bezierPnt1);
+ QPointF pntOnSpline;
+ spline.getPointByX(x, pntOnSpline);
+
+ rectifyColorValue(pntOnSpline);
+ return pntOnSpline.y();
+}
+
+void ColorCurve::insertControlPointAt(int index)
+{
+ if (index < 0)
+ index = 0;
+ else if (index > (_controlPoints.size() - 1) )
+ index = (int)(_controlPoints.size() - 1);
+
+ std::vector<ColorControlPoint>::iterator itrToAdd = _controlPoints.begin();
+ std::advance(itrToAdd, index);
+
+ ColorControlPoint& ctrlPntFront = _controlPoints[index - 1];
+ ColorControlPoint& ctrlPntBehind = _controlPoints[index];
+
+ switch (ctrlPntFront.mode)
+ {
+ case eDiscret:
+ {
+ ColorControlPoint ctrlPntToAdd;
+ ctrlPntToAdd.mode = ctrlPntFront.mode;
+ ctrlPntToAdd.x = ((ctrlPntFront.x + ctrlPntBehind.x) / 2);
+ ctrlPntToAdd.color = ctrlPntFront.color;
+ _controlPoints.insert(itrToAdd, ctrlPntToAdd);
+ break;
+ }
+ case eLinear:
+ {
+ ColorControlPoint ctrlPntToAdd;
+ ctrlPntToAdd.mode = ctrlPntFront.mode;
+ ctrlPntToAdd.x = ((ctrlPntFront.x + ctrlPntBehind.x) / 2);
+ ctrlPntToAdd.color.setRed(ctrlPntFront.color.red() + (ctrlPntBehind.color.red() - ctrlPntFront.color.red()) / 2);
+ ctrlPntToAdd.color.setRed(ctrlPntFront.color.green() + (ctrlPntBehind.color.green() - ctrlPntFront.color.green()) / 2);
+ ctrlPntToAdd.color.setRed(ctrlPntFront.color.blue() + (ctrlPntBehind.color.blue() - ctrlPntFront.color.blue()) / 2);
+ ctrlPntToAdd.color.setRed(ctrlPntFront.color.alpha() + (ctrlPntBehind.color.alpha() - ctrlPntFront.color.alpha()) / 2);
+ _controlPoints.insert(itrToAdd, ctrlPntToAdd);
+ break;
+ }
+ case eBezierSpline:
+ {
+ ColorControlPoint ctrlPntToAdd;
+ ctrlPntToAdd.mode = eBezierSpline;
+ ctrlPntToAdd.x = (ctrlPntFront.x + ctrlPntBehind.x) / 2;
+ ctrlPntToAdd.color.setRed(getBezierColorByX(ctrlPntFront, ctrlPntBehind, 0, ctrlPntToAdd.x));
+ ctrlPntToAdd.color.setGreen(getBezierColorByX(ctrlPntFront, ctrlPntBehind, 1, ctrlPntToAdd.x));
+ ctrlPntToAdd.color.setBlue(getBezierColorByX(ctrlPntFront, ctrlPntBehind, 2, ctrlPntToAdd.x));
+ ctrlPntToAdd.color.setAlpha(getBezierColorByX(ctrlPntFront, ctrlPntBehind, 3, ctrlPntToAdd.x));
+ ctrlPntToAdd.splineData.inLen = 1.0;
+ ctrlPntToAdd.splineData.inTan = 0.0;
+ ctrlPntToAdd.splineData.outLen = 1.0;
+ ctrlPntToAdd.splineData.outTan = 0.0;
+ _controlPoints.insert(itrToAdd, ctrlPntToAdd);
+ break;
+ }
+ case eCatmullRomSpline:
+ {
+ CatmullRomSpline splineRed;
+ CatmullRomSpline splineGreen;
+ CatmullRomSpline splineBlue;
+ CatmullRomSpline splineAlpha;
+
+ int i = index - 1;
+ for (; i >= 0; --i)
+ {
+ if (eCatmullRomSpline != _controlPoints[i].mode)
+ {
+ ++i;
+ break;
+ }
+ }
+
+ for (; i < (int)_controlPoints.size(); ++i)
+ {
+ splineRed.appendControlPoint(QPointF(_controlPoints[i].x, _controlPoints[i].color.red()));
+ splineGreen.appendControlPoint(QPointF(_controlPoints[i].x, _controlPoints[i].color.green()));
+ splineBlue.appendControlPoint(QPointF(_controlPoints[i].x, _controlPoints[i].color.blue()));
+ splineAlpha.appendControlPoint(QPointF(_controlPoints[i].x, _controlPoints[i].color.alpha()));
+ if (eCatmullRomSpline != _controlPoints[i].mode)
+ break;
+ }
+
+ QPointF pntOnSpline;
+ ColorControlPoint ctrlPntToAdd;
+ ctrlPntToAdd.mode = eCatmullRomSpline;
+ ctrlPntToAdd.x = (ctrlPntFront.x + ctrlPntBehind.x) / 2;
+ splineRed.getPointByX(ctrlPntToAdd.x, pntOnSpline);
+ rectifyColorValue(pntOnSpline);
+ ctrlPntToAdd.color.setRed(pntOnSpline.y());
+ splineGreen.getPointByX(ctrlPntToAdd.x, pntOnSpline);
+ rectifyColorValue(pntOnSpline);
+ ctrlPntToAdd.color.setGreen(pntOnSpline.y());
+ splineBlue.getPointByX(ctrlPntToAdd.x, pntOnSpline);
+ rectifyColorValue(pntOnSpline);
+ ctrlPntToAdd.color.setBlue(pntOnSpline.y());
+ splineAlpha.getPointByX(ctrlPntToAdd.x, pntOnSpline);
+ rectifyColorValue(pntOnSpline);
+ ctrlPntToAdd.color.setAlpha(pntOnSpline.y());
+ _controlPoints.insert(itrToAdd, ctrlPntToAdd);
+ break;
+ }
+ default:
+ break;
+ }
+
+ _needSample = true;
+}
+
+void ColorCurve::removeControlPoint(int index)
+{
+ std::vector<ColorControlPoint>::iterator itrToRemove = _controlPoints.begin();
+ std::advance(itrToRemove, index);
+ _controlPoints.erase(itrToRemove);
+
+ _needSample = true;
+}
+
+QColor ColorCurve::getColorByX(float x)
+{
+ if (!_initValueRange)
+ {
+ QMessageBox::critical(nullptr, QObject::tr("critical"), QObject::tr("Color curve must be initilized its value range before being used!"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
+ return QColor();
+ }
+
+ if (x < _controlPoints[0].x)
+ return _controlPoints[0].color;
+
+ if (x >= _controlPoints[_controlPoints.size() - 1].x)
+ return _controlPoints[_controlPoints.size() - 1].color;
+
+ if (_needSample)
+ _doSamplePoints();
+
+ size_t pntCount = _colorSamplePnts.size();
+
+ if(pntCount < 2)
+ {
+ return QColor();
+ }
+
+ for (size_t i = 0; i < pntCount - 1; i++)
+ {
+ if(_colorSamplePnts[i].x <= x && _colorSamplePnts[i + 1].x > x)
+ {
+ ColorPoint& colorPnt0 = _colorSamplePnts[i];
+ ColorPoint& colorPnt1 = _colorSamplePnts[i + 1];
+ float fRate = (x - colorPnt0.x) / (colorPnt1.x - colorPnt0.x);
+
+ float red = colorPnt0.color.red() + fRate * (colorPnt1.color.red() - colorPnt0.color.red());
+ rectifyColorValue(red);
+ float green = colorPnt0.color.green() + fRate * (colorPnt1.color.green() - colorPnt0.color.green());
+ rectifyColorValue(green);
+ float blue = colorPnt0.color.blue() + fRate * (colorPnt1.color.blue() - colorPnt0.color.blue());
+ rectifyColorValue(blue);
+ float alpha = colorPnt0.color.alpha() + fRate * (colorPnt1.color.alpha() - colorPnt0.color.alpha());
+ rectifyColorValue(alpha);
+
+ return QColor(red, green, blue, alpha);
+ }
+ }
+
+ return QColor();
+}
+
+ColorCurve& ColorCurve::operator = (const ColorCurve& right)
+{
+ if (this == &right)
+ return *this;
+
+ _needSample = right._needSample;
+ _initValueRange = right._initValueRange;
+ _minValue = right._minValue;
+ _maxValue = right._maxValue;
+ _segmentCount = right._segmentCount;
+ _colorSamplePnts = right._colorSamplePnts;
+ _controlPoints = right._controlPoints;
+
+ return *this;
+}
+
+BezierSpline _generateColorBezierSpline(ColorControlPoint& ctrlPntFront, ColorControlPoint& ctrlPntBehind, int component, int numSegments = 100)
+{
+ BezierSplinePoint bezierPnt0;
+ BezierSplinePoint bezierPnt1;
+
+ QColor colorFront = ctrlPntFront.color;
+ if (!ctrlPntFront.texturePath.empty())
+ colorFront = ctrlPntFront.textureAverageColor;
+ QColor colorBehind = ctrlPntBehind.color;
+ if (!ctrlPntBehind.texturePath.empty())
+ colorBehind = ctrlPntBehind.textureAverageColor;
+
+ float y0 = 0.0f, y1 = 0.0f;
+ switch (component) {
+ case 0:
+ y0 = colorFront.red();
+ y1 = colorBehind.red();
+ break;
+ case 1:
+ y0 = colorFront.green();
+ y1 = colorBehind.green();
+ break;
+ case 2:
+ y0 = colorFront.blue();
+ y1 = colorBehind.blue();
+ break;
+ case 3:
+ y0 = colorFront.alpha();
+ y1 = colorBehind.alpha();
+ break;
+ default:
+ break;
+ }
+
+ QPointF pntFront = QPointF(ctrlPntFront.x, y0);
+ QPointF pntBehind = QPointF(ctrlPntBehind.x, y1);
+ float xStride = ctrlPntBehind.x - ctrlPntFront.x;
+ bezierPnt0.ctrlPoint0 = pntFront;
+ bezierPnt0.valuePoint = pntFront;
+ float xDelta = ctrlPntFront.splineData.outLen * xStride;
+ bezierPnt0.ctrlPoint1 = pntFront + QPointF(xDelta, xDelta * ctrlPntFront.splineData.outTan);
+
+ if (eBezierSpline == ctrlPntBehind.mode)
+ {
+ xDelta = ctrlPntBehind.splineData.inLen * xStride;
+ bezierPnt1.ctrlPoint0 = pntBehind - QPointF(xDelta, xDelta * ctrlPntBehind.splineData.inTan);
+ bezierPnt1.valuePoint = pntBehind;
+ bezierPnt1.ctrlPoint1 = pntBehind;
+ }
+ else
+ {
+ bezierPnt1.ctrlPoint0 = pntBehind;
+ bezierPnt1.valuePoint = pntBehind;
+ bezierPnt1.ctrlPoint1 = pntBehind;
+ }
+
+ BezierSpline spline(numSegments);
+ spline.appendControlPoint(bezierPnt0);
+ spline.appendControlPoint(bezierPnt1);
+ return spline;
+}
+
+void _doBezierColorSample(ColorControlPoint& ctrlPntFront, ColorControlPoint& ctrlPntBehind, std::vector<ColorPoint>& colorSamplePnts, int numSegments = 100)
+{
+ BezierSpline redSpline = _generateColorBezierSpline(ctrlPntFront, ctrlPntBehind, 0, numSegments);
+ BezierSpline greenSpline = _generateColorBezierSpline(ctrlPntFront, ctrlPntBehind, 1, numSegments);
+ BezierSpline blueSpline = _generateColorBezierSpline(ctrlPntFront, ctrlPntBehind, 2, numSegments);
+ BezierSpline alphaSpline = _generateColorBezierSpline(ctrlPntFront, ctrlPntBehind, 3, numSegments);
+
+ std::vector<QPointF> redSamplePnts = redSpline.getSamplePoints();
+ std::vector<QPointF> greenSamplePnts = greenSpline.getSamplePoints();
+ std::vector<QPointF> blueSamplePnts = blueSpline.getSamplePoints();
+ std::vector<QPointF> alphaSamplePnts = alphaSpline.getSamplePoints();
+
+ size_t samplePntsCount = redSamplePnts.size();
+ for (size_t i = 0; i < samplePntsCount; ++i)
+ {
+ colorSamplePnts.push_back(ColorPoint(redSamplePnts[i].x() , QColor(redSamplePnts[i].y()
+ , greenSamplePnts[i].y()
+ , blueSamplePnts[i].y()
+ , alphaSamplePnts[i].y())));
+ }
+}
+
+float saturate( float v)
+{
+ if( v < 0.0 ) v = 0.0;
+ if( v > 1.0 ) v = 1.0;
+ return v;
+}
+
+float _GetRatio(float s, float weight, float fallOff)
+{
+ float ratio = s;
+
+ // add bias for first/second color variation
+ if (weight < 0.5f)
+ {
+ float slope = 2.0f * weight;
+ ratio = slope * ratio;
+ }
+ else
+ {
+ float slope = 2.0f * (1.0f - weight) ;
+ ratio = slope * (ratio - 1.0f) + 1.0f;
+ }
+
+ // modify ratio for falloff
+ float slope = 1.0f / (fallOff + 0.001f);
+ ratio = saturate(0.5f + slope * (ratio - 0.5f));
+
+ return ratio;
+}
+
+void ColorCurve::_doSamplePoints()
+{
+ _colorSamplePnts.clear();
+
+ for (long i = 1; i < _controlPoints.size(); ++i)
+ {
+ ColorControlPoint& ctrlPntFront = _controlPoints[i-1];
+ ColorControlPoint& ctrlPntBehind = _controlPoints[i];
+ QColor colorFront = ctrlPntFront.color;
+ if (!ctrlPntFront.texturePath.empty())
+ colorFront = ctrlPntFront.textureAverageColor;
+ QColor colorBehind = ctrlPntBehind.color;
+ if (!ctrlPntBehind.texturePath.empty())
+ colorBehind = ctrlPntBehind.textureAverageColor;
+
+ switch (ctrlPntFront.mode)
+ {
+ case eDiscret:
+ {
+ _colorSamplePnts.push_back(ColorPoint(ctrlPntFront.x, colorFront));
+ _colorSamplePnts.push_back(ColorPoint(ctrlPntBehind.x, colorFront));
+ break;
+ }
+ case eLinear:
+ {
+ for (long j = 0; j <= _segmentCount; ++j)
+ {
+ float s = (float)j / _segmentCount;
+ float ratio = s;
+ if (ctrlPntBehind.weight >= 0)
+ ratio = _GetRatio(s, ctrlPntFront.weight, ctrlPntFront.fallOff);
+
+ float xPos = ctrlPntFront.x + s * (ctrlPntBehind.x - ctrlPntFront.x);
+
+ int red = colorBehind.red() - colorFront.red();
+ int green = colorBehind.green() - colorFront.green();
+ int blue = colorBehind.blue() - colorFront.blue();
+ int alpha = colorBehind.alpha() - colorFront.alpha();
+
+ QColor color(colorFront.red() + ratio * red
+ , colorFront.green() + ratio * green
+ , colorFront.blue() + ratio * blue
+ , colorFront.alpha() + ratio * alpha);
+ _colorSamplePnts.push_back(ColorPoint(xPos, color));
+ }
+ break;
+ }
+ case eBezierSpline:
+ {
+ _doBezierColorSample(ctrlPntFront, ctrlPntBehind, _colorSamplePnts, _segmentCount);
+ break;
+ }
+ case eCatmullRomSpline:
+ {
+ CatmullRomSpline redSpline(_segmentCount);
+ CatmullRomSpline greenSpline(_segmentCount);
+ CatmullRomSpline blueSpline(_segmentCount);
+ CatmullRomSpline alphaSpline(_segmentCount);
+
+ for (i = i - 1; i < _controlPoints.size(); ++i)
+ {
+ QColor color = _controlPoints[i].color;
+ if (!_controlPoints[i].texturePath.empty())
+ color = _controlPoints[i].textureAverageColor;
+ redSpline.appendControlPoint(QPointF(_controlPoints[i].x, color.red()));
+ greenSpline.appendControlPoint(QPointF(_controlPoints[i].x, color.green()));
+ blueSpline.appendControlPoint(QPointF(_controlPoints[i].x, color.blue()));
+ alphaSpline.appendControlPoint(QPointF(_controlPoints[i].x, color.alpha()));
+ if (eCatmullRomSpline != _controlPoints[i].mode)
+ {
+ break;
+ }
+ }
+
+ std::vector<QPointF> redSamplePnts = redSpline.getSamplePoints();
+ std::vector<QPointF> greenSamplePnts = greenSpline.getSamplePoints();
+ std::vector<QPointF> blueSamplePnts = blueSpline.getSamplePoints();
+ std::vector<QPointF> alphaSamplePnts = alphaSpline.getSamplePoints();
+
+ size_t samplePntsCount = redSamplePnts.size();
+ for (size_t i = 0; i < samplePntsCount; ++i)
+ {
+ rectifyColorValue(redSamplePnts[i]);
+ rectifyColorValue(greenSamplePnts[i]);
+ rectifyColorValue(blueSamplePnts[i]);
+ rectifyColorValue(alphaSamplePnts[i]);
+ _colorSamplePnts.push_back(ColorPoint(redSamplePnts[i].x() , QColor(redSamplePnts[i].y()
+ , greenSamplePnts[i].y()
+ , blueSamplePnts[i].y()
+ , alphaSamplePnts[i].y())));
+ }
+
+ break;
+ }
+ }
+ }
+
+ _needSample = false;
+}
+
+void ColorCurve::_reOrderControlPoints(int& pickedPoint)
+{
+ for (size_t i = 0; i < _controlPoints.size() - 1; ++i)
+ {
+ if (_controlPoints[i].x > _controlPoints[i+1].x)
+ {
+ ColorControlPoint temp = _controlPoints[i];
+ _controlPoints[i] = _controlPoints[i + 1];
+ _controlPoints[i + 1] = temp;
+
+ if (pickedPoint == i)
+ pickedPoint = (int)(i + 1);
+ else if (pickedPoint == (i + 1) )
+ pickedPoint = (int)i;
+
+ break;
+ }
+ }
+}
+
+QColor ColorCurve::_calcTextureAverageColor(const char* texturePath)
+{
+ QImage img(texturePath);
+ double red = 0, green = 0, blue = 0, alpha = 0;
+
+ // Try stb_image for .TGA
+ int width = 0;
+ int height = 0;
+ int numComponents = 0;
+ unsigned char *pSTBIRes = stbi_load(texturePath, &width, &height, &numComponents, 4);
+
+ if (!pSTBIRes)
+ return QColor(0, 0, 0);
+
+ int colorCount = width * height;
+ unsigned int* pixels = (unsigned int*)pSTBIRes;
+
+ for (int i = 0; i < height; ++i)
+ {
+ for (int j = 0; j < width; ++j)
+ {
+ QRgb pixel = *pixels++;
+ QColor color(pixel);
+ red += color.redF();
+ green += color.greenF();
+ blue += color.blueF();
+ alpha += color.alphaF();
+ }
+ }
+
+ if (colorCount != 0)
+ {
+ red /= colorCount;
+ green /= colorCount;
+ blue /= colorCount;
+ alpha /= colorCount;
+ }
+
+ QColor color;
+ color.setRgbF(red, green, blue, alpha);
+ return color;
+}
+
+} // namespace CurveEditor
+} // namespace nvidia \ No newline at end of file
diff --git a/tools/CurveEditor/source/Internal/Curve/Spline.cpp b/tools/CurveEditor/source/Internal/Curve/Spline.cpp
new file mode 100644
index 0000000..c40a4f9
--- /dev/null
+++ b/tools/CurveEditor/source/Internal/Curve/Spline.cpp
@@ -0,0 +1,151 @@
+#include "Spline.h"
+
+namespace nvidia {
+namespace CurveEditor {
+
+Spline::Spline(long segmentCount)
+ : _needSample(true)
+ , _samplePoints()
+ , _edgeLengths()
+ , _totolLength(0.0f)
+{
+ _segmentCount = segmentCount < 1 ? 1 : segmentCount;
+}
+
+Spline::~Spline(void)
+{
+}
+
+bool Spline::getPercentPoint(float percent, QPointF& point)
+{
+ long lPointCount = (long)_samplePoints.size();
+
+ if (lPointCount == 0)
+ {
+ return false;
+ }
+ else if (lPointCount == 1)
+ {
+ point = _samplePoints[0];
+ return true;
+ }
+
+ if (_needSample)
+ _sample();
+
+ if (percent > 1.0)
+ {
+ percent -= (int)percent;
+ }
+
+ if (percent < 0.0)
+ {
+ percent += (int)(-percent) + 1;
+ }
+
+ if (percent <= 0.0)
+ {
+ // return begin point
+ point = _samplePoints[0];
+ return true;
+ }
+ else if (percent >= 1.0)
+ {
+ // return end point
+ point = _samplePoints[_samplePoints.size() - 1];
+ return true;
+ }
+
+ float fCurPos = _totolLength * percent;
+ int index = 0;
+
+ {// get edge's index the point is on based on the percent value
+ long lEdgeCount = (long)_edgeLengths.size();
+ for ( ; index < lEdgeCount; index++)
+ {
+ if (fCurPos < _edgeLengths[index])
+ {
+ break;
+ }
+
+ fCurPos -= _edgeLengths[index];
+ }
+
+ if (index == lEdgeCount)
+ {
+ point = _samplePoints[_samplePoints.size() - 1];
+ return true;
+ }
+ }
+
+ QPointF v1 = _samplePoints[index];
+ QPointF v2 = _samplePoints[index + 1];
+
+ point = v1 + (v2 - v1) * (fCurPos / _edgeLengths[index]);
+
+ return true;
+}
+
+bool Spline::getPointByX(float x, QPointF& point)
+{
+ if (_needSample)
+ _sample();
+
+ long lPointCount = (long)_samplePoints.size();
+
+ if(lPointCount < 2)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < lPointCount - 1; i++)
+ {
+ if(_samplePoints[i].x() <= x && _samplePoints[i + 1].x() > x)
+ {
+ point.setX( x );
+ float fRate = (x - _samplePoints[i].x())/ (_samplePoints[i + 1].x() - _samplePoints[i].x());
+ point.setY(_samplePoints[i].y() + (_samplePoints[i+1].y() - _samplePoints[i].y()) * fRate);
+ return true;
+ }
+ }
+ return false;
+}
+
+void Spline::setSegmentCount(long segmentCount)
+{
+ _segmentCount = segmentCount < 1 ? 1 : segmentCount;
+ _needSample = true;
+}
+
+std::vector<QPointF> Spline::getSamplePoints()
+{
+ if (_needSample)
+ _sample();
+ return _samplePoints;
+}
+
+void Spline::_sample()
+{
+ _samplePoints.clear();
+
+ _doSample();
+
+ // get all edges length and total length
+ _totolLength = 0;
+ _edgeLengths.clear();
+ float fEdgeLength;
+
+ long lPointCount = (long)_samplePoints.size();
+ for (int i = 1; i < lPointCount; i++)
+ {
+ QPointF dist = _samplePoints[i] - _samplePoints[i - 1];
+ fEdgeLength = (sqrt(dist.x()*dist.x() + dist.y() + dist.y()));
+ _edgeLengths.push_back(fEdgeLength);
+ _totolLength += fEdgeLength;
+ }
+
+ _needSample = false;
+}
+
+} // namespace CurveEditor
+} // namespace nvidia \ No newline at end of file
diff --git a/tools/CurveEditor/source/Internal/Curve/Spline.h b/tools/CurveEditor/source/Internal/Curve/Spline.h
new file mode 100644
index 0000000..fc111a7
--- /dev/null
+++ b/tools/CurveEditor/source/Internal/Curve/Spline.h
@@ -0,0 +1,43 @@
+#ifndef Spline_h__
+#define Spline_h__
+#include <vector>
+#include <QtCore/QPointF>
+
+namespace nvidia {
+namespace CurveEditor {
+
+class Spline
+{
+public:
+ Spline(long segmentCount = 100);
+ virtual ~Spline(void) = 0;
+
+ // return the point which lies on the polyline after sample.
+ // percent: the length between the returned point and the begin point along the polyline after sample
+ // ������������������������������������������������������������������������������������������
+ // the length between the end point and the begin point along the polyline after sample
+ // [0.0, 1.0]
+ bool getPercentPoint(float percent, QPointF& point);
+
+ bool getPointByX(float x, QPointF& point);
+
+ void setSegmentCount(long segmentCount);
+
+ std::vector<QPointF> getSamplePoints();
+
+protected:
+ void _sample();
+ virtual void _doSample() = 0;
+
+protected:
+ bool _needSample;
+ long _segmentCount;
+ std::vector<QPointF> _samplePoints;
+ std::vector<float> _edgeLengths; // the number of edges + 1 == the number of sample points
+ float _totolLength;// the total length of all edges
+};
+
+} // namespace CurveEditor
+} // namespace nvidia
+
+#endif // Spline_h__