Some color things. More to come.
This commit is contained in:
parent
27c64c4291
commit
866326863b
10
Nerd_STF/Graphics/ColorChannel.cs
Normal file
10
Nerd_STF/Graphics/ColorChannel.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Nerd_STF.Graphics
|
||||
{
|
||||
public enum ColorChannel
|
||||
{
|
||||
Red,
|
||||
Green,
|
||||
Blue,
|
||||
Alpha
|
||||
}
|
||||
}
|
||||
359
Nerd_STF/Graphics/ColorRGB.cs
Normal file
359
Nerd_STF/Graphics/ColorRGB.cs
Normal file
@ -0,0 +1,359 @@
|
||||
using Nerd_STF.Exceptions;
|
||||
using Nerd_STF.Mathematics;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Nerd_STF.Graphics
|
||||
{
|
||||
public struct ColorRGB : IColor<ColorRGB>
|
||||
#if CS11_OR_GREATER
|
||||
,IFromTuple<ColorRGB, (double, double, double)>,
|
||||
IFromTuple<ColorRGB, (double, double, double, double)>,
|
||||
ISplittable<ColorRGB, (double[] Rs, double[] Gs, double[] Bs, double[] As)>
|
||||
#endif
|
||||
{
|
||||
public static int ChannelCount => 4;
|
||||
|
||||
public static ColorRGB Black => new ColorRGB(0, 0, 0, 1);
|
||||
public static ColorRGB Blue => new ColorRGB(0, 0, 1, 1);
|
||||
public static ColorRGB Clear => new ColorRGB(0, 0, 0, 0);
|
||||
public static ColorRGB Cyan => new ColorRGB(0, 1, 1, 1);
|
||||
public static ColorRGB Gray => new ColorRGB(0.5, 0.5, 0.5, 1);
|
||||
public static ColorRGB Green => new ColorRGB(0, 1, 0, 1);
|
||||
public static ColorRGB Magenta => new ColorRGB(1, 0, 1, 1);
|
||||
public static ColorRGB Orange => new ColorRGB(1, 0.5, 0, 1);
|
||||
public static ColorRGB Purple => new ColorRGB(0.5, 0, 1, 1);
|
||||
public static ColorRGB Red => new ColorRGB(1, 0, 0, 1);
|
||||
public static ColorRGB White => new ColorRGB(1, 1, 1, 1);
|
||||
public static ColorRGB Yellow => new ColorRGB(1, 1, 0, 1);
|
||||
|
||||
public double Magnitude => MathE.Sqrt(r * r + g * g + b * b);
|
||||
|
||||
public double r, g, b, a;
|
||||
|
||||
public ColorRGB(double r, double g, double b)
|
||||
{
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
a = 1;
|
||||
}
|
||||
public ColorRGB(double r, double g, double b, double a)
|
||||
{
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
}
|
||||
public ColorRGB(IEnumerable<double> nums)
|
||||
{
|
||||
r = 0;
|
||||
g = 0;
|
||||
b = 0;
|
||||
a = 1;
|
||||
|
||||
int index = 0;
|
||||
foreach (double item in nums)
|
||||
{
|
||||
this[index] = item;
|
||||
index++;
|
||||
if (index == 4) break;
|
||||
}
|
||||
}
|
||||
public ColorRGB(Fill<double> fill)
|
||||
{
|
||||
r = fill(0);
|
||||
g = fill(1);
|
||||
b = fill(2);
|
||||
a = fill(3);
|
||||
}
|
||||
|
||||
public double this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return r;
|
||||
case 1: return g;
|
||||
case 2: return b;
|
||||
case 3: return a;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: r = value; break;
|
||||
case 1: g = value; break;
|
||||
case 2: b = value; break;
|
||||
case 3: a = value; break;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
public ListTuple<double> this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
double[] items = new double[key.Length];
|
||||
for (int i = 0; i < key.Length; i++)
|
||||
{
|
||||
char c = key[i];
|
||||
switch (c)
|
||||
{
|
||||
case 'r': items[i] = r; break;
|
||||
case 'g': items[i] = g; break;
|
||||
case 'b': items[i] = b; break;
|
||||
case 'a': items[i] = a; break;
|
||||
default: throw new ArgumentException("Invalid key.", nameof(key));
|
||||
}
|
||||
}
|
||||
return new ListTuple<double>(items);
|
||||
}
|
||||
set
|
||||
{
|
||||
IEnumerator<double> stepper = value.GetEnumerator();
|
||||
for (int i = 0; i < key.Length; i++)
|
||||
{
|
||||
char c = key[i];
|
||||
stepper.MoveNext();
|
||||
switch (c)
|
||||
{
|
||||
case 'r': r = stepper.Current; break;
|
||||
case 'g': g = stepper.Current; break;
|
||||
case 'b': b = stepper.Current; break;
|
||||
case 'a': a = stepper.Current; break;
|
||||
default: throw new ArgumentException("Invalid key.", nameof(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ColorRGB Average(double gamma, IEnumerable<ColorRGB> colors)
|
||||
{
|
||||
double avgR = 0, avgG = 0, avgB = 0, avgA = 0;
|
||||
int count = 0;
|
||||
foreach (ColorRGB color in colors)
|
||||
{
|
||||
double correctR = MathE.Pow(color.r, gamma),
|
||||
correctG = MathE.Pow(color.g, gamma),
|
||||
correctB = MathE.Pow(color.b, gamma);
|
||||
// Gamma doesn't apply to the alpha channel.
|
||||
|
||||
avgR += correctR;
|
||||
avgG += correctG;
|
||||
avgB += correctB;
|
||||
avgA += color.a;
|
||||
count++;
|
||||
}
|
||||
avgR /= count;
|
||||
avgG /= count;
|
||||
avgB /= count;
|
||||
avgA /= count;
|
||||
double invGamma = 1 / gamma;
|
||||
return new ColorRGB(MathE.Pow(avgR, invGamma),
|
||||
MathE.Pow(avgG, invGamma),
|
||||
MathE.Pow(avgB, invGamma),
|
||||
avgA);
|
||||
}
|
||||
public static ColorRGB Clamp(ColorRGB color, ColorRGB min, ColorRGB max) =>
|
||||
new ColorRGB(MathE.Clamp(color.r, min.r, max.r),
|
||||
MathE.Clamp(color.g, min.g, max.g),
|
||||
MathE.Clamp(color.b, min.b, max.b),
|
||||
MathE.Clamp(color.a, min.a, max.a));
|
||||
public static ColorRGB ClampMagnitude(ColorRGB color, double minMag, double maxMag)
|
||||
{
|
||||
ColorRGB copy = color;
|
||||
ClampMagnitude(ref copy, minMag, maxMag);
|
||||
return copy;
|
||||
}
|
||||
public static void ClampMagnitude(ref ColorRGB color, double minMag, double maxMag)
|
||||
{
|
||||
if (minMag > maxMag) throw new ClampOrderMismatchException(nameof(minMag), nameof(maxMag));
|
||||
double mag = color.Magnitude;
|
||||
|
||||
if (mag < minMag)
|
||||
{
|
||||
double factor = minMag / mag;
|
||||
color.r *= factor;
|
||||
color.g *= factor;
|
||||
color.b *= factor;
|
||||
}
|
||||
else if (mag > maxMag)
|
||||
{
|
||||
double factor = maxMag / mag;
|
||||
color.r *= factor;
|
||||
color.g *= factor;
|
||||
color.b *= factor;
|
||||
}
|
||||
}
|
||||
public static double Dot(ColorRGB a, ColorRGB b) => a.r * b.r + a.g * b.g + a.b * b.b;
|
||||
public static double Dot(IEnumerable<ColorRGB> colors)
|
||||
{
|
||||
bool any = false;
|
||||
double r = 1, g = 1, b = 1;
|
||||
foreach (ColorRGB c in colors)
|
||||
{
|
||||
r *= c.r;
|
||||
g *= c.g;
|
||||
b *= c.b;
|
||||
}
|
||||
return any ? (r + g + b) : 0;
|
||||
}
|
||||
public static ColorRGB Lerp(double gamma, ColorRGB a, ColorRGB b, double t, bool clamp = true)
|
||||
{
|
||||
double aCorrectedR = MathE.Pow(a.r, gamma),
|
||||
aCorrectedG = MathE.Pow(a.g, gamma),
|
||||
aCorrectedB = MathE.Pow(a.b, gamma),
|
||||
bCorrectedR = MathE.Pow(b.r, gamma),
|
||||
bCorrectedG = MathE.Pow(b.g, gamma),
|
||||
bCorrectedB = MathE.Pow(b.b, gamma);
|
||||
// Gamma doesn't apply to the alpha channel.
|
||||
|
||||
double newR = MathE.Lerp(aCorrectedR, bCorrectedR, t, clamp),
|
||||
newG = MathE.Lerp(aCorrectedG, bCorrectedG, t, clamp),
|
||||
newB = MathE.Lerp(aCorrectedB, bCorrectedB, t, clamp),
|
||||
newA = MathE.Lerp(a.a, b.a, t, clamp);
|
||||
|
||||
double invGamma = 1 / gamma;
|
||||
return new ColorRGB(MathE.Pow(newR, invGamma),
|
||||
MathE.Pow(newG, invGamma),
|
||||
MathE.Pow(newB, invGamma),
|
||||
newA);
|
||||
}
|
||||
#if CS11_OR_GREATER
|
||||
static ColorRGB IInterpolable<ColorRGB>.Lerp(ColorRGB a, ColorRGB b, double t, bool clamp) => Lerp(1, a, b, t, clamp);
|
||||
#endif
|
||||
public static ColorRGB Product(IEnumerable<ColorRGB> colors)
|
||||
{
|
||||
bool any = false;
|
||||
ColorRGB result = new ColorRGB(1, 1, 1, 1);
|
||||
foreach (ColorRGB color in colors)
|
||||
{
|
||||
any = true;
|
||||
result *= color;
|
||||
}
|
||||
return any ? result : Black;
|
||||
}
|
||||
public static ColorRGB Sum(IEnumerable<ColorRGB> colors)
|
||||
{
|
||||
bool any = false;
|
||||
ColorRGB result = new ColorRGB(0, 0, 0, 1);
|
||||
foreach (ColorRGB color in colors)
|
||||
{
|
||||
any = true;
|
||||
result += color;
|
||||
}
|
||||
return any ? result : Black;
|
||||
}
|
||||
public static (double[] Rs, double[] Gs, double[] Bs, double[] As) SplitArray(IEnumerable<ColorRGB> colors)
|
||||
{
|
||||
int count = colors.Count();
|
||||
double[] Rs = new double[count], Gs = new double[count], Bs = new double[count], As = new double[count];
|
||||
int index = 0;
|
||||
foreach (ColorRGB c in colors)
|
||||
{
|
||||
Rs[index] = c.r;
|
||||
Gs[index] = c.g;
|
||||
Bs[index] = c.b;
|
||||
As[index] = c.a;
|
||||
index++;
|
||||
}
|
||||
return (Rs, Gs, Bs, As);
|
||||
}
|
||||
|
||||
public Dictionary<ColorChannel, double> GetChannels() => new Dictionary<ColorChannel, double>()
|
||||
{
|
||||
{ ColorChannel.Red, r },
|
||||
{ ColorChannel.Green, g },
|
||||
{ ColorChannel.Blue, b },
|
||||
{ ColorChannel.Alpha, a },
|
||||
};
|
||||
|
||||
public TColor AsColor<TColor>() where TColor : struct, IColor<TColor>
|
||||
{
|
||||
Type type = typeof(TColor);
|
||||
if (type == typeof(ColorRGB)) return (TColor)(object)this;
|
||||
else throw new InvalidCastException();
|
||||
}
|
||||
public ColorRGB AsRgb() => this;
|
||||
|
||||
public IEnumerator<double> GetEnumerator()
|
||||
{
|
||||
yield return r;
|
||||
yield return g;
|
||||
yield return b;
|
||||
yield return a;
|
||||
}
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
public void Deconstruct(out double r, out double g, out double b)
|
||||
{
|
||||
r = this.r;
|
||||
g = this.g;
|
||||
b = this.b;
|
||||
}
|
||||
public void Deconstruct(out double r, out double g, out double b, out double a)
|
||||
{
|
||||
r = this.r;
|
||||
g = this.g;
|
||||
b = this.b;
|
||||
a = this.a;
|
||||
}
|
||||
|
||||
public bool Equals(ColorRGB other)
|
||||
{
|
||||
if (a <= 0 && b <= 0) return true;
|
||||
else return r == other.r && g == other.g && b == other.b;
|
||||
}
|
||||
public bool Equals(IColor other) => Equals(other.AsRgb());
|
||||
#if CS8_OR_GREATER
|
||||
public override bool Equals(object? other)
|
||||
#else
|
||||
public override bool Equals(object other)
|
||||
#endif
|
||||
{
|
||||
if (other is IColor color) return Equals(color.AsRgb());
|
||||
else return false;
|
||||
}
|
||||
public override int GetHashCode() => base.GetHashCode();
|
||||
public override string ToString() => $"{{ r={r:0.00}, g={g:0.00}, b={b:0.00}, a={a:0.00} }}";
|
||||
|
||||
public double[] ToArray() => new double[] { r, g, b, a };
|
||||
public Fill<double> ToFill()
|
||||
{
|
||||
ColorRGB copy = this;
|
||||
return i => copy[i];
|
||||
}
|
||||
public List<double> ToList() => new List<double>() { r, g, b, a };
|
||||
|
||||
public static ColorRGB operator +(ColorRGB a) => a;
|
||||
public static ColorRGB operator +(ColorRGB a, ColorRGB b) => new ColorRGB(a.r + b.r, a.g + b.g, a.b + b.b, 1 - (1 - a.a) * (1 - b.b));
|
||||
public static ColorRGB operator -(ColorRGB a) => new ColorRGB(1 - a.r, 1 - a.g, 1 - a.b);
|
||||
public static ColorRGB operator *(ColorRGB a, ColorRGB b) => new ColorRGB(a.r * b.r, a.g * b.g, a.b * b.b, a.a * b.a);
|
||||
public static ColorRGB operator *(ColorRGB a, double b) => new ColorRGB(a.r * b, a.g * b, a.b * b);
|
||||
public static bool operator ==(ColorRGB a, IColor b) => a.Equals(b.AsRgb());
|
||||
public static bool operator !=(ColorRGB a, IColor b) => !a.Equals(b.AsRgb());
|
||||
public static bool operator ==(ColorRGB a, ColorRGB b) => a.Equals(b);
|
||||
public static bool operator !=(ColorRGB a, ColorRGB b) => !a.Equals(b);
|
||||
|
||||
public static implicit operator ColorRGB(ListTuple<double> tuple)
|
||||
{
|
||||
if (tuple.Length == 3) return new ColorRGB(tuple[0], tuple[1], tuple[2]);
|
||||
else if (tuple.Length == 4) return new ColorRGB(tuple[0], tuple[1], tuple[2], tuple[3]);
|
||||
else throw new InvalidCastException();
|
||||
}
|
||||
public static implicit operator ColorRGB((double, double, double) tuple) => new ColorRGB(tuple.Item1, tuple.Item2, tuple.Item3);
|
||||
public static implicit operator ColorRGB((double, double, double, double) tuple) => new ColorRGB(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4);
|
||||
public static explicit operator ColorRGB(Float3 group) => new ColorRGB(group.x, group.y, group.z);
|
||||
public static explicit operator ColorRGB(Float4 group) => new ColorRGB(group.x, group.y, group.z, group.w);
|
||||
|
||||
public static implicit operator ListTuple<double>(ColorRGB color) => new ListTuple<double>(color.r, color.g, color.b, color.a);
|
||||
public static implicit operator ValueTuple<double, double, double>(ColorRGB color) => (color.r, color.g, color.b);
|
||||
public static implicit operator ValueTuple<double, double, double, double>(ColorRGB color) => (color.r, color.g, color.b, color.a);
|
||||
}
|
||||
}
|
||||
33
Nerd_STF/Graphics/IColor.cs
Normal file
33
Nerd_STF/Graphics/IColor.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using Nerd_STF.Mathematics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Nerd_STF.Graphics
|
||||
{
|
||||
public interface IColor : INumberGroupBase<double>
|
||||
{
|
||||
Dictionary<ColorChannel, double> GetChannels();
|
||||
|
||||
TColor AsColor<TColor>() where TColor : struct, IColor<TColor>;
|
||||
ColorRGB AsRgb();
|
||||
|
||||
bool Equals(IColor other);
|
||||
}
|
||||
|
||||
public interface IColor<TSelf> : IColor,
|
||||
IEquatable<TSelf>,
|
||||
INumberGroup<TSelf, double>
|
||||
#if CS11_OR_GREATER
|
||||
,IColorPresets<TSelf>
|
||||
#endif
|
||||
where TSelf : struct, IColor<TSelf>
|
||||
{
|
||||
#if CS11_OR_GREATER
|
||||
static abstract int ChannelCount { get; }
|
||||
|
||||
// TODO: Do all color formats have a gamma value?
|
||||
static abstract TSelf Average(double gamma, IEnumerable<TSelf> colors);
|
||||
static abstract TSelf Lerp(double gamma, TSelf a, TSelf b, double t, bool clamp = true);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
24
Nerd_STF/Graphics/IColorPresets.cs
Normal file
24
Nerd_STF/Graphics/IColorPresets.cs
Normal file
@ -0,0 +1,24 @@
|
||||
#if CS11_OR_GREATER
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Nerd_STF.Graphics
|
||||
{
|
||||
public interface IColorPresets<TSelf> where TSelf : struct, IColor<TSelf>, IColorPresets<TSelf>
|
||||
{
|
||||
static abstract TSelf Black { get; }
|
||||
static abstract TSelf Blue { get; }
|
||||
static abstract TSelf Clear { get; }
|
||||
static abstract TSelf Cyan { get; }
|
||||
static abstract TSelf Gray { get; }
|
||||
static abstract TSelf Green { get; }
|
||||
static abstract TSelf Magenta { get; }
|
||||
static abstract TSelf Orange { get; }
|
||||
static abstract TSelf Purple { get; }
|
||||
static abstract TSelf Red { get; }
|
||||
static abstract TSelf White { get; }
|
||||
static abstract TSelf Yellow { get; }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -46,7 +46,7 @@ namespace Nerd_STF.Mathematics
|
||||
{
|
||||
this[index] = item;
|
||||
index++;
|
||||
if (index >= 2) break;
|
||||
if (index == 2) break;
|
||||
}
|
||||
}
|
||||
public Float2(Fill<double> fill)
|
||||
|
||||
@ -49,7 +49,7 @@ namespace Nerd_STF.Mathematics
|
||||
{
|
||||
this[index] = item;
|
||||
index++;
|
||||
if (index >= 2) break;
|
||||
if (index == 3) break;
|
||||
}
|
||||
}
|
||||
public Float3(Fill<double> fill)
|
||||
|
||||
@ -53,7 +53,7 @@ namespace Nerd_STF.Mathematics
|
||||
{
|
||||
this[index] = item;
|
||||
index++;
|
||||
if (index >= 2) break;
|
||||
if (index == 4) break;
|
||||
}
|
||||
}
|
||||
public Float4(Fill<double> fill)
|
||||
|
||||
@ -7,7 +7,8 @@ namespace Nerd_STF.Mathematics
|
||||
{
|
||||
public interface INumberGroup<TSelf, TItem> : ICombinationIndexer<TItem>,
|
||||
IEnumerable<TItem>,
|
||||
IEquatable<TSelf>
|
||||
IEquatable<TSelf>,
|
||||
INumberGroupBase<TItem>
|
||||
#if CS11_OR_GREATER
|
||||
, IInterpolable<TSelf>,
|
||||
ISimpleMathOperations<TSelf>,
|
||||
@ -17,11 +18,5 @@ namespace Nerd_STF.Mathematics
|
||||
#if CS11_OR_GREATER
|
||||
where TItem : INumber<TItem>
|
||||
#endif
|
||||
{
|
||||
TItem this[int index] { get; set; }
|
||||
|
||||
TItem[] ToArray();
|
||||
Fill<TItem> ToFill();
|
||||
List<TItem> ToList();
|
||||
}
|
||||
{ }
|
||||
}
|
||||
|
||||
17
Nerd_STF/Mathematics/INumberGroupBase.cs
Normal file
17
Nerd_STF/Mathematics/INumberGroupBase.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Nerd_STF.Mathematics
|
||||
{
|
||||
public interface INumberGroupBase<TItem>
|
||||
#if CS11_OR_GREATER
|
||||
where TItem : INumber<TItem>
|
||||
#endif
|
||||
{
|
||||
TItem this[int index] { get; set; }
|
||||
|
||||
TItem[] ToArray();
|
||||
Fill<TItem> ToFill();
|
||||
List<TItem> ToList();
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,6 @@ using System.Numerics;
|
||||
namespace Nerd_STF.Mathematics
|
||||
{
|
||||
public interface ISimpleMathOperations<TSelf> : IAdditionOperators<TSelf, TSelf, TSelf>,
|
||||
ISubtractionOperators<TSelf, TSelf, TSelf>,
|
||||
IMultiplyOperators<TSelf, TSelf, TSelf>
|
||||
where TSelf : ISimpleMathOperations<TSelf>
|
||||
{
|
||||
|
||||
@ -44,7 +44,7 @@ namespace Nerd_STF.Mathematics
|
||||
{
|
||||
this[index] = item;
|
||||
index++;
|
||||
if (index >= 2) break;
|
||||
if (index == 2) break;
|
||||
}
|
||||
}
|
||||
public Int2(Fill<int> fill)
|
||||
|
||||
@ -47,7 +47,7 @@ namespace Nerd_STF.Mathematics
|
||||
{
|
||||
this[index] = item;
|
||||
index++;
|
||||
if (index >= 2) break;
|
||||
if (index == 3) break;
|
||||
}
|
||||
}
|
||||
public Int3(Fill<int> fill)
|
||||
|
||||
@ -51,7 +51,7 @@ namespace Nerd_STF.Mathematics
|
||||
{
|
||||
this[index] = item;
|
||||
index++;
|
||||
if (index >= 2) break;
|
||||
if (index == 4) break;
|
||||
}
|
||||
}
|
||||
public Int4(Fill<int> fill)
|
||||
|
||||
@ -703,7 +703,11 @@ namespace Nerd_STF.Mathematics
|
||||
public static IEquation Cot(IEquation inputRad, int terms = 8) =>
|
||||
new Equation((double x) => Cot(inputRad[x], terms));
|
||||
|
||||
public static double Sqrt(double num) => 1 / InverseSqrtFast((float)num); // !!TODO!!: Bring back Newton's
|
||||
// YOU CANNOT USE POW HERE!!!
|
||||
// The CordicHelper uses the Sqrt function for the Pow method.
|
||||
// It'll cause a stack overflow.
|
||||
// !!TODO!! - Bring back Newton's
|
||||
public static double Sqrt(double num) => 1 / InverseSqrtFast((float)num);
|
||||
public static IEquation Sqrt(IEquation equ) =>
|
||||
new Equation((double x) => Sqrt(equ.Get(x)));
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user