Fixed clearing curves

Resolves #1295
This commit is contained in:
Raphael Coeffic
2022-01-04 14:21:07 +10:00
committed by Peter Feerick
parent a077f2e325
commit 44cdb4497f
5 changed files with 146 additions and 120 deletions
+136 -96
View File
@@ -25,8 +25,26 @@
#include "libopenui.h"
#endif
constexpr int DEFAULT_POINTS = 5;
constexpr int STD_CURVE_POINTS(int p) { return p + DEFAULT_POINTS; }
constexpr int CUSTOM_CURVE_POINTS(int p) { return 2 * p + (2 * DEFAULT_POINTS - 2); }
int8_t * curveEnd[MAX_CURVES];
uint8_t getCurvePoints(uint8_t index)
{
if (index >= MAX_CURVES)
return 0;
const auto& curve = g_model.curves[index];
if (curve.type == CURVE_TYPE_STANDARD)
return STD_CURVE_POINTS(curve.points);
else if (curve.type == CURVE_TYPE_CUSTOM)
return CUSTOM_CURVE_POINTS(curve.points);
return 0;
}
void loadCurves()
{
bool showWarning= false;
@@ -34,15 +52,15 @@ void loadCurves()
for (int i=0; i<MAX_CURVES; i++) {
switch (g_model.curves[i].type) {
case CURVE_TYPE_STANDARD:
tmp += 5+g_model.curves[i].points;
tmp += STD_CURVE_POINTS(g_model.curves[i].points);
break;
case CURVE_TYPE_CUSTOM:
tmp += 8+2*g_model.curves[i].points;
tmp += CUSTOM_CURVE_POINTS(g_model.curves[i].points);
break;
default:
TRACE("Wrong curve type! Fixing...");
g_model.curves[i].type = CURVE_TYPE_STANDARD;
tmp += 5+g_model.curves[i].points;
tmp += STD_CURVE_POINTS(g_model.curves[i].points);
break;
}
// Older version did not check if we exceeded the array
@@ -66,6 +84,24 @@ int8_t * curveAddress(uint8_t idx)
return idx==0 ? g_model.points : curveEnd[idx-1];
}
static void curveMove_unsafe(uint8_t index, int8_t shift)
{
int8_t* src = curveAddress(index+1);
int8_t* dst = src + shift;
size_t points = curveEnd[MAX_CURVES-1] - curveEnd[index]
+ getCurvePoints(MAX_CURVES-1); // points in the last curve
memmove(dst, src, points);
if (shift < 0) {
// clear points at the tail
memclear(dst + points, src - dst);
}
while (index < MAX_CURVES) {
curveEnd[index++] += shift;
}
}
bool moveCurve(uint8_t index, int8_t shift)
{
if (curveEnd[MAX_CURVES-1] + shift > g_model.points + sizeof(g_model.points)) {
@@ -73,13 +109,8 @@ bool moveCurve(uint8_t index, int8_t shift)
return false;
}
int8_t * nextCrv = curveAddress(index+1);
memmove(nextCrv+shift, nextCrv, 5*(MAX_CURVES-index-1)+curveEnd[MAX_CURVES-1]-curveEnd[index]);
if (shift < 0) memclear(&g_model.points[MAX_CURVE_POINTS-1] + shift, -shift);
while (index<MAX_CURVES) {
curveEnd[index++] += shift;
}
curveMove_unsafe(index, shift);
storageDirty(EE_MODEL);
return true;
}
@@ -96,96 +127,105 @@ void resetCustomCurveX(int8_t * points, int noPoints)
}
}
void curveReset(uint8_t index)
void curveClear(uint8_t index)
{
if (index >= MAX_CURVES)
return;
CurveHeader * curve = &g_model.curves[index];
int8_t * points = curveAddress(index);
int8_t * curvePoints = curveAddress(index);
memclear(points, 5 + curve->points);
if (curve->type == CURVE_TYPE_CUSTOM)
resetCustomCurveX(points, 5 + curve->points);
uint8_t nPoints = getCurvePoints(index);
memclear(curvePoints, nPoints);
memclear(curve, sizeof(CurveHeader));
uint8_t newPoints = getCurvePoints(index);
int8_t shift = newPoints - nPoints;
if (shift != 0) {
curveMove_unsafe(index, shift);
}
}
void curveMirror(uint8_t index)
{
if (index >= MAX_CURVES)
return;
CurveHeader & curve = g_model.curves[index];
int8_t * points = curveAddress(index);
for (int i = 0; i < 5 + curve.points; i++)
// we only mirror Y axis: X axis does not change
for (int i = 0; i < STD_CURVE_POINTS(curve.points); i++)
points[i] = -points[i];
}
bool isCurveUsed(uint8_t index)
{
return !is_memclear(&g_model.curves[index], sizeof(CurveHeader)) || !is_memclear(curveAddress(index), 5);
return !is_memclear(&g_model.curves[index], sizeof(CurveHeader)) ||
!is_memclear(curveAddress(index), DEFAULT_POINTS);
}
#define CUSTOM_POINT_X(points, count, idx) ((idx)==0 ? -100 : (((idx)==(count)-1) ? 100 : points[(count)+(idx)-1]))
int32_t compute_tangent(CurveHeader * crv, const int8_t * points, int i)
#define CUSTOM_POINT_X(points, count, idx) \
((idx) == 0 ? -100 : (((idx) == (count)-1) ? 100 : points[(count) + (idx)-1]))
int32_t compute_tangent(CurveHeader* crv, const int8_t* points, int i)
{
int32_t m=0;
uint8_t num_points = crv->points + 5;
#define MMULT 1024
if (i == 0) {
//linear interpolation between 1st 2 points
//keep 3 decimal-places for m
if (crv->type == CURVE_TYPE_CUSTOM) {
int8_t x0 = CUSTOM_POINT_X(points, num_points, 0);
int8_t x1 = CUSTOM_POINT_X(points, num_points, 1);
if (x1 > x0) m = (MMULT * (points[1] - points[0])) / (x1 - x0);
}
else {
int32_t delta = (2 * 100) / (num_points - 1);
m = (MMULT * (points[1] - points[0])) / delta;
}
int32_t m = 0;
uint8_t num_points = STD_CURVE_POINTS(crv->points);
#define MMULT 1024
if (i == 0) {
// linear interpolation between 1st 2 points
// keep 3 decimal-places for m
if (crv->type == CURVE_TYPE_CUSTOM) {
int8_t x0 = CUSTOM_POINT_X(points, num_points, 0);
int8_t x1 = CUSTOM_POINT_X(points, num_points, 1);
if (x1 > x0) m = (MMULT * (points[1] - points[0])) / (x1 - x0);
} else {
int32_t delta = (2 * 100) / (num_points - 1);
m = (MMULT * (points[1] - points[0])) / delta;
}
else if (i == num_points - 1) {
//linear interpolation between last 2 points
//keep 3 decimal-places for m
if (crv->type == CURVE_TYPE_CUSTOM) {
int8_t x0 = CUSTOM_POINT_X(points, num_points, num_points-2);
int8_t x1 = CUSTOM_POINT_X(points, num_points, num_points-1);
if (x1 > x0) m = (MMULT * (points[num_points-1] - points[num_points-2])) / (x1 - x0);
}
else {
int32_t delta = (2 * 100) / (num_points - 1);
m = (MMULT * (points[num_points-1] - points[num_points-2])) / delta;
}
} else if (i == num_points - 1) {
// linear interpolation between last 2 points
// keep 3 decimal-places for m
if (crv->type == CURVE_TYPE_CUSTOM) {
int8_t x0 = CUSTOM_POINT_X(points, num_points, num_points - 2);
int8_t x1 = CUSTOM_POINT_X(points, num_points, num_points - 1);
if (x1 > x0)
m = (MMULT * (points[num_points - 1] - points[num_points - 2])) /
(x1 - x0);
} else {
int32_t delta = (2 * 100) / (num_points - 1);
m = (MMULT * (points[num_points - 1] - points[num_points - 2])) / delta;
}
else {
//apply monotone rules from
//http://en.wikipedia.org/wiki/Monotone_cubic_interpolation
//1) compute slopes of secant lines
int32_t d0=0, d1=0;
if (crv->type == CURVE_TYPE_CUSTOM) {
int8_t x0 = CUSTOM_POINT_X(points, num_points, i-1);
int8_t x1 = CUSTOM_POINT_X(points, num_points, i);
int8_t x2 = CUSTOM_POINT_X(points, num_points, i+1);
if (x1 > x0) d0 = (MMULT * (points[i] - points[i-1])) / (x1 - x0);
if (x2 > x1) d1 = (MMULT * (points[i+1] - points[i])) / (x2 - x1);
}
else {
int32_t delta = (2 * 100) / (num_points - 1);
d0 = (MMULT * (points[i] - points[i-1])) / (delta);
d1 = (MMULT * (points[i+1] - points[i])) / (delta);
}
//2) compute initial average tangent
m = (d0 + d1) / 2;
//3 check for horizontal lines
if (d0 == 0 || d1 == 0 || (d0 > 0 && d1 < 0) || (d0 < 0 && d1 > 0)) {
m = 0;
}
else if (MMULT * m / d0 > 3 * MMULT) {
m = 3 * d0;
}
else if (MMULT * m / d1 > 3 * MMULT) {
m = 3 * d1;
}
} else {
// apply monotone rules from
// http://en.wikipedia.org/wiki/Monotone_cubic_interpolation
// 1) compute slopes of secant lines
int32_t d0 = 0, d1 = 0;
if (crv->type == CURVE_TYPE_CUSTOM) {
int8_t x0 = CUSTOM_POINT_X(points, num_points, i - 1);
int8_t x1 = CUSTOM_POINT_X(points, num_points, i);
int8_t x2 = CUSTOM_POINT_X(points, num_points, i + 1);
if (x1 > x0) d0 = (MMULT * (points[i] - points[i - 1])) / (x1 - x0);
if (x2 > x1) d1 = (MMULT * (points[i + 1] - points[i])) / (x2 - x1);
} else {
int32_t delta = (2 * 100) / (num_points - 1);
d0 = (MMULT * (points[i] - points[i - 1])) / (delta);
d1 = (MMULT * (points[i + 1] - points[i])) / (delta);
}
return m;
// 2) compute initial average tangent
m = (d0 + d1) / 2;
// 3 check for horizontal lines
if (d0 == 0 || d1 == 0 || (d0 > 0 && d1 < 0) || (d0 < 0 && d1 > 0)) {
m = 0;
} else if (MMULT * m / d0 > 3 * MMULT) {
m = 3 * d0;
} else if (MMULT * m / d1 > 3 * MMULT) {
m = 3 * d1;
}
}
return m;
}
/* The following is a hermite cubic spline.
@@ -197,7 +237,7 @@ int16_t hermite_spline(int16_t x, uint8_t idx)
{
CurveHeader &crv = g_model.curves[idx];
int8_t *points = curveAddress(idx);
uint8_t count = crv.points+5;
uint8_t count = STD_CURVE_POINTS(crv.points);
bool custom = (crv.type == CURVE_TYPE_CUSTOM);
if (x < -RESX)
@@ -240,37 +280,37 @@ int16_t hermite_spline(int16_t x, uint8_t idx)
int intpol(int x, uint8_t idx) // -100, -75, -50, -25, 0 ,25 ,50, 75, 100
{
CurveHeader & crv = g_model.curves[idx];
int8_t * points = curveAddress(idx);
uint8_t count = crv.points+5;
CurveHeader& crv = g_model.curves[idx];
int8_t* points = curveAddress(idx);
uint8_t count = STD_CURVE_POINTS(crv.points);
bool custom = (crv.type == CURVE_TYPE_CUSTOM);
int16_t erg;
x += RESXu;
if (x <= 0) {
erg = (int16_t)points[0] * (RESX/4);
}
else if (x >= (RESX*2)) {
erg = (int16_t)points[count-1] * (RESX/4);
}
else {
uint16_t a=0, b=0;
erg = (int16_t)points[0] * (RESX / 4);
} else if (x >= (RESX * 2)) {
erg = (int16_t)points[count - 1] * (RESX / 4);
} else {
uint16_t a = 0, b = 0;
uint8_t i;
if (custom) {
for (i=0; i<count-1; i++) {
for (i = 0; i < count - 1; i++) {
a = b;
b = (i==count-2 ? 2*RESX : RESX + calc100toRESX(points[count+i]));
if ((uint16_t)x<=b) break;
b = (i == count - 2 ? 2 * RESX
: RESX + calc100toRESX(points[count + i]));
if ((uint16_t)x <= b) break;
}
}
else {
uint16_t d = (RESX * 2) / (count-1);
} else {
uint16_t d = (RESX * 2) / (count - 1);
i = (uint16_t)x / d;
a = i * d;
b = a + d;
}
erg = (int16_t)points[i]*(RESX/4) + ((int32_t)(x-a) * (points[i+1]-points[i]) * (RESX/4)) / ((b-a));
erg = (int16_t)points[i] * (RESX / 4) +
((int32_t)(x - a) * (points[i + 1] - points[i]) * (RESX / 4)) /
((b - a));
}
return erg / 25; // 100*D5/RESX;
@@ -349,7 +389,7 @@ point_t getPoint(uint8_t curveIndex, uint8_t index)
CurveHeader & crv = g_model.curves[curveIndex];
int8_t * points = curveAddress(curveIndex);
bool custom = (crv.type == CURVE_TYPE_CUSTOM);
uint8_t count = 5 + crv.points;
uint8_t count = STD_CURVE_POINTS(crv.points);
if (index < count) {
if (custom && index > 0 && index < count - 1) {
result.x = calc100toRESX(points[count + index - 1]);
+1 -1
View File
@@ -33,7 +33,7 @@ enum BaseCurves {
CURVE_BASE
};
void curveReset(uint8_t index);
void curveClear(uint8_t index);
void curveMirror(uint8_t index);
bool isCurveUsed(uint8_t index);
void loadCurves();
+4 -11
View File
@@ -72,19 +72,12 @@ void onCurveOneMenu(const char * result)
POPUP_INPUT(STR_PRESET, runPopupCurvePreset);
}
else if (result == STR_MIRROR) {
CurveHeader & crv = g_model.curves[s_currIdxSubMenu];
int8_t * points = curveAddress(s_currIdxSubMenu);
for (int i=0; i<5+crv.points; i++)
points[i] = -points[i];
curveMirror(s_currIdxSubMenu);
storageDirty(EE_MODEL);
}
else if (result == STR_CLEAR) {
CurveHeader & crv = g_model.curves[s_currIdxSubMenu];
int8_t * points = curveAddress(s_currIdxSubMenu);
for (int i=0; i<5+crv.points; i++)
points[i] = 0;
if (crv.type == CURVE_TYPE_CUSTOM) {
resetCustomCurveX(points, 5+crv.points);
}
curveClear(s_currIdxSubMenu);
storageDirty(EE_MODEL);
}
}
+4 -11
View File
@@ -74,19 +74,12 @@ void onCurveOneMenu(const char * result)
POPUP_INPUT(STR_PRESET, runPopupCurvePreset);
}
else if (result == STR_MIRROR) {
CurveHeader & crv = g_model.curves[s_currIdxSubMenu];
int8_t * points = curveAddress(s_currIdxSubMenu);
for (int i=0; i<5+crv.points; i++)
points[i] = -points[i];
curveMirror(s_currIdxSubMenu);
storageDirty(EE_MODEL);
}
else if (result == STR_CLEAR) {
CurveHeader & crv = g_model.curves[s_currIdxSubMenu];
int8_t * points = curveAddress(s_currIdxSubMenu);
for (int i=0; i<5+crv.points; i++)
points[i] = 0;
if (crv.type == CURVE_TYPE_CUSTOM) {
resetCustomCurveX(points, 5+crv.points);
}
curveClear(s_currIdxSubMenu);
storageDirty(EE_MODEL);
}
}
+1 -1
View File
@@ -361,7 +361,7 @@ void ModelCurvesPage::build(FormWindow * window, int8_t focusIndex)
button->invalidate();
});
menu->addLine(STR_CLEAR, [=]() {
curveReset(index);
curveClear(index);
storageDirty(EE_MODEL);
rebuild(window, index);
});