using System.Drawing; using System.Numerics; namespace WebmrAPI.Utils.Sequences { public abstract class BezierGenerator : BaseGenerator where TControl : struct { public bool IsCubic { get => Control2 != null; } protected TControl Control1 { get; set; } protected TControl? Control2 { get; set; } public override double Step { get; protected set; } protected double CalcBezier(double start, double end, double ctrl1, double? ctrl2) { double t = Progress; double rt = ProgressRemainder; if (IsCubic && ctrl2 != null) { double rt3 = rt * rt * rt; double t3 = t * t * t; double rt2_t = rt * rt * t; double rt_t2 = rt * t * t; return rt3 * start + 3 * rt2_t * ctrl1 + 3 * rt_t2 * ctrl2.Value + t3 * end; } double rt2 = rt * rt; double t2 = t * t; double rt_t = rt * t; return rt2 * start + 2 * rt_t * ctrl1 + t2 * end; } public BezierGenerator(T min, T max, TControl ctrl1, TControl? ctrl2) : base(min, max) { Control1 = ctrl1; Control2 = ctrl2; } } public class BezierNumberGenerator : BezierGenerator, INumberGenerator where T : struct, INumber { public INumberGenerator Self { get => this; } private double Bezier { get => CalcBezier(Self.DMin, Self.DMax, Control1, Control2); } public BezierNumberGenerator SetShapeFactor(double shape) { FShape = shape; return this; } public BezierNumberGenerator SetFadeFactor(double fade) { FFade = fade; return this; } protected override T Calc() { return T.CreateChecked(Math.Clamp(Self.Mean + (Bezier - Self.Mean) * Scale, Self.DMin, Self.DMax)); } public BezierNumberGenerator(T start, T end, double ctrl1, double? ctrl2 = null, int count = 10) : base(start, end, ctrl1, ctrl2) { Step = CalcStepByCount(count); } } public class ArcNumberGenerator : BezierNumberGenerator where T : struct, INumber { public ArcNumberGenerator(T start, T end, int count = 10, bool bidirectional = true, bool reverse = false) : base(start, end, Double.NaN, null, count) { var hMean = (Self.DMax - Self.Mean); if (bidirectional) { Control1 = (reverse) ? Self.DMin : Self.DMax; Control2 = (reverse) ? Self.DMax : Self.DMin; } else { Control1 = (reverse) ? Self.DMin : Self.DMax; } } } public class BezierPointGenerator : BezierGenerator, IPointGenerator { protected readonly double _pAngle; public IPointGenerator Self { get => this; } public BezierPointGenerator SetShapeFactor(double shape) { FShape = shape; return this; } public BezierPointGenerator SetFadeFactor(double fade) { FFade = fade; return this; } private double BezierX { get => CalcBezier(Min.X, Max.X, Control1.X, Control2?.X); } private double BezierY { get => CalcBezier(Min.Y, Max.Y, Control1.Y, Control2?.Y); } protected override Point Calc() { double dx = BezierX - Self.LinearX; double dy = BezierY - Self.LinearY; return IPointGenerator.CalcPoint(Self.LinearX + dx * Scale, Self.LinearY + dy * Scale, 0, 0); } public static BezierPointGenerator CreateByCount(Point start, Point end, Point ctrl1, Point? ctrl2 = null, int count = 10) { return new BezierPointGenerator(start, end, ctrl1, ctrl2) { Step = CalcStepByCount(count) }; } public static BezierPointGenerator CreateByStepSize(Point start, Point end, Point ctrl1, Point? ctrl2 = null, double pxPerStep = 5) { return new BezierPointGenerator(start, end, ctrl1, ctrl2, pxPerStep); } public BezierPointGenerator(Point start, Point end, Point ctrl1, Point? ctrl2 = null, double pxPerStep = 5) : this(start, end, ctrl1, ctrl2) { Step = pxPerStep / IPointGenerator.CalcDistance(start, end); } public BezierPointGenerator(Point start, Point end, Point ctrl1, Point? ctrl2 = null) : base(start, end, ctrl1, ctrl2) { _pAngle = IPointGenerator.CalcAngle(start, end) + Math.PI / 2.0; } } public sealed class ArcPointGenerator : BezierPointGenerator { public new ArcPointGenerator SetShapeFactor(double shape) { base.SetShapeFactor(shape); return this; } public new ArcPointGenerator SetFadeFactor(double fade) { base.SetFadeFactor(fade); return this; } public static ArcPointGenerator CreateByCount(Point start, Point end, bool bidirectional = false, bool reverse = false, double arcHeight = 15.0, int count = 10) { return new ArcPointGenerator(start, end, bidirectional, reverse, arcHeight) { Step = CalcStepByCount(count) }; } public static ArcPointGenerator CreateByStepSize(Point start, Point end, bool bidirectional = false, bool reverse = false, double arcHeight = 15.0, double pxPerStep = 5) { return new ArcPointGenerator(start, end, bidirectional, reverse, arcHeight, pxPerStep); } public ArcPointGenerator(Point start, Point end, bool bidirectional = false, bool reverse = false, double arcHeight = 15.0, double pxPerStep = 5) : this(start, end, bidirectional, reverse, arcHeight) { Step = pxPerStep / IPointGenerator.CalcDistance(start, end); } private ArcPointGenerator(Point start, Point end, bool bidirectional, bool reverse, double arcHeight) : base(start, end, Point.Empty, null) { var height = (reverse) ? -arcHeight : arcHeight; if (bidirectional) { Control1 = IPointGenerator.NewPointAngleDistance(IPointGenerator.NewPointDistanceFactor(start, end, 1.0 / 3.0), height, _pAngle); Control2 = IPointGenerator.NewPointAngleDistance(IPointGenerator.NewPointDistanceFactor(start, end, 2.0 / 3.0), -height, _pAngle); } else { Control1 = IPointGenerator.NewPointAngleDistance(IPointGenerator.CalcMiddle(start, end), height, _pAngle); } } } }