Skip to content

Commit

Permalink
Making SplineInterpolator generic
Browse files Browse the repository at this point in the history
  • Loading branch information
Lehonti committed Feb 17, 2025
1 parent 81b2722 commit 9fd4e74
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 37 deletions.
51 changes: 26 additions & 25 deletions Pinta.Core/Classes/SplineInterpolator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Numerics;

namespace Pinta.Core;

public sealed class SplineInterpolator
public sealed class SplineInterpolator<TNumber> where TNumber : IFloatingPoint<TNumber>
{
private readonly SortedList<double, double> points = [];
private ImmutableArray<double> y2;
private readonly SortedList<TNumber, TNumber> points = [];
private ImmutableArray<TNumber> y2;

public int Count => points.Count;

public void Add (double x, double y)
public void Add (TNumber x, TNumber y)
{
points[x] = y;
y2 = default;
Expand All @@ -32,13 +33,13 @@ public void Clear ()
// NUMERICAL RECIPES IN C: THE ART OF SCIENTIFIC COMPUTING
// ISBN 0-521-43108-5, page 113, section 3.3.

public double Interpolate (double x)
public TNumber Interpolate (TNumber x)
{
if (y2.IsDefault)
y2 = PreCompute ();

IList<double> xa = points.Keys;
IList<double> ya = points.Values;
IList<TNumber> xa = points.Keys;
IList<TNumber> ya = points.Values;

int n = ya.Count;
int klo = 0; // We will find the right place in the table by means of
Expand All @@ -55,45 +56,45 @@ public double Interpolate (double x)
}
}

double h = xa[khi] - xa[klo];
double a = (xa[khi] - x) / h;
double b = (x - xa[klo]) / h;
TNumber h = xa[khi] - xa[klo];
TNumber a = (xa[khi] - x) / h;
TNumber b = (x - xa[klo]) / h;

// Cubic spline polynomial is now evaluated.
return a * ya[klo] + b * ya[khi] +
((a * a * a - a) * y2[klo] + (b * b * b - b) * y2[khi]) * (h * h) / 6.0; // NRT - y2 is set above by PreCompute ()
((a * a * a - a) * y2[klo] + (b * b * b - b) * y2[khi]) * (h * h) / TNumber.CreateChecked (6); // NRT - y2 is set above by PreCompute ()
}

private ImmutableArray<double> PreCompute ()
private ImmutableArray<TNumber> PreCompute ()
{
int n = points.Count;
double[] u = new double[n];
IList<double> xa = points.Keys;
IList<double> ya = points.Values;
TNumber[] u = new TNumber[n];
IList<TNumber> xa = points.Keys;
IList<TNumber> ya = points.Values;

var resultingY2 = ImmutableArray.CreateBuilder<double> (n);
var resultingY2 = ImmutableArray.CreateBuilder<TNumber> (n);
resultingY2.Count = n;

u[0] = 0;
resultingY2[0] = 0;
u[0] = TNumber.Zero;
resultingY2[0] = TNumber.Zero;

for (int i = 1; i < n - 1; ++i) {
// This is the decomposition loop of the tridiagonal algorithm.
// y2 and u are used for temporary storage of the decomposed factors.
double wx = xa[i + 1] - xa[i - 1];
double sig = (xa[i] - xa[i - 1]) / wx;
double p = sig * resultingY2[i - 1] + 2.0;
TNumber wx = xa[i + 1] - xa[i - 1];
TNumber sig = (xa[i] - xa[i - 1]) / wx;
TNumber p = sig * resultingY2[i - 1] + TNumber.CreateChecked (2);

resultingY2[i] = (sig - 1.0) / p;
resultingY2[i] = (sig - TNumber.One) / p;

double ddydx =
TNumber ddydx =
(ya[i + 1] - ya[i]) / (xa[i + 1] - xa[i]) -
(ya[i] - ya[i - 1]) / (xa[i] - xa[i - 1]);

u[i] = (6.0 * ddydx / wx - sig * u[i - 1]) / p;
u[i] = (TNumber.CreateChecked (6) * ddydx / wx - sig * u[i - 1]) / p;
}

resultingY2[n - 1] = 0;
resultingY2[n - 1] = TNumber.Zero;

// This is the backsubstitution loop of the tridiagonal algorithm
for (int i = n - 2; i >= 0; --i) {
Expand Down
19 changes: 11 additions & 8 deletions Pinta.Effects/Adjustments/CurvesEffect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,14 @@ private UnaryPixelOp MakeUop ()
int channels = transferCurves.Length;

for (int channel = 0; channel < channels; channel++) {

SortedList<int, int> channelControlPoints = Data.ControlPoints![channel]; // NRT - Code expects this to be not-null

IList<int> xa = channelControlPoints.Keys;
IList<int> ya = channelControlPoints.Values;
SplineInterpolator interpolator = new SplineInterpolator ();

SplineInterpolator<double> interpolator = new ();

int length = channelControlPoints.Count;

for (int i = 0; i < length; i++) {
Expand All @@ -117,20 +121,19 @@ private UnaryPixelOp MakeUop ()
public sealed class CurvesData : EffectData
{
public SortedList<int, int>[]? ControlPoints { get; set; }

public ColorTransferMode Mode { get; set; }

public override CurvesData Clone ()
{
// Not sure if we have to copy contents of ControlPoints
// var controlPoints = new SortedList<int, int> [ControlPoints.Length];
//
// for (int i = 0; i < ControlPoints.Length; i++)
// controlPoints[i] = new SortedList<int, int> (ControlPoints[i]);
// Not sure if we have to copy contents of ControlPoints
// var controlPoints = new SortedList<int, int> [ControlPoints.Length];
//
// for (int i = 0; i < ControlPoints.Length; i++)
// controlPoints[i] = new SortedList<int, int> (ControlPoints[i]);

return new () {
Mode = Mode,
ControlPoints = ControlPoints
ControlPoints = ControlPoints,
};
}
}
8 changes: 4 additions & 4 deletions Pinta.Effects/Dialogs/Effects.CurvesDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -452,15 +452,15 @@ private IEnumerator<ControlPointDrawingInfo> GetDrawingInfos ()
yield break;
}

yield return new ControlPointDrawingInfo (
yield return new (
Color: new Color (0.9, 0, 0),
IsActive: check_red.Active);

yield return new ControlPointDrawingInfo (
yield return new (
Color: new Color (0, 0.9, 0),
IsActive: check_green.Active);

yield return new ControlPointDrawingInfo (
yield return new (
Color: new Color (0, 0, 0.9),
IsActive: check_blue.Active);
}
Expand Down Expand Up @@ -524,7 +524,7 @@ void DrawSpline (Context g)

int points = controlPoints.Count;

SplineInterpolator interpolator = new ();
SplineInterpolator<double> interpolator = new ();

IList<int> xa = controlPoints.Keys;
IList<int> ya = controlPoints.Values;
Expand Down

0 comments on commit 9fd4e74

Please sign in to comment.