123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- using System;
- using System.Collections.Generic;
- using System.Windows;
- namespace HandyControl.Expression.Drawing;
- internal static class BezierCurveFlattener
- {
- public const double StandardFlatteningTolerance = 0.25;
- private static void DoCubicForwardDifferencing(Point[] controlPoints, double leftParameter, double rightParameter, double inverseErrorTolerance, ICollection<Point> resultPolyline, ICollection<double> resultParameters)
- {
- double num14;
- var num = controlPoints[1].X - controlPoints[0].X;
- var num2 = controlPoints[1].Y - controlPoints[0].Y;
- var num3 = controlPoints[2].X - controlPoints[1].X;
- var num4 = controlPoints[2].Y - controlPoints[1].Y;
- var num5 = controlPoints[3].X - controlPoints[2].X;
- var num6 = controlPoints[3].Y - controlPoints[2].Y;
- var num7 = num3 - num;
- var num8 = num4 - num2;
- var num9 = num5 - num3;
- var num10 = num6 - num4;
- var num11 = num9 - num7;
- var num12 = num10 - num8;
- var vector = controlPoints[3].Subtract(controlPoints[0]);
- var length = vector.Length;
- if (!MathHelper.IsVerySmall(length))
- num14 = Math.Max(0.0,
- Math.Max(Math.Abs((num7 * vector.Y - num8 * vector.X) / length),
- Math.Abs((num9 * vector.Y - num10 * vector.X) / length)));
- else
- num14 = Math.Max(0.0,
- Math.Max(GeometryHelper.Distance(controlPoints[1], controlPoints[0]),
- GeometryHelper.Distance(controlPoints[2], controlPoints[0])));
- uint num15 = 0;
- if (num14 > 0.0)
- {
- var d = num14 * inverseErrorTolerance;
- num15 = d < 2147483647.0 ? Log4UnsignedInt32((uint) (d + 0.5)) : Log4Double(d);
- }
- var exp = (int) -num15;
- var num18 = exp + exp;
- var num19 = num18 + exp;
- var num20 = MathHelper.DoubleFromMantissaAndExponent(3.0 * num7, num18);
- var num21 = MathHelper.DoubleFromMantissaAndExponent(3.0 * num8, num18);
- var num22 = MathHelper.DoubleFromMantissaAndExponent(6.0 * num11, num19);
- var num23 = MathHelper.DoubleFromMantissaAndExponent(6.0 * num12, num19);
- var num24 = MathHelper.DoubleFromMantissaAndExponent(3.0 * num, exp) + num20 +
- 0.16666666666666666 * num22;
- var num25 = MathHelper.DoubleFromMantissaAndExponent(3.0 * num2, exp) + num21 +
- 0.16666666666666666 * num23;
- var num26 = 2.0 * num20 + num22;
- var num27 = 2.0 * num21 + num23;
- var x = controlPoints[0].X;
- var y = controlPoints[0].Y;
- var item = new Point(0.0, 0.0);
- var num30 = 1 << (int) num15;
- var num31 = num30 > 0 ? (rightParameter - leftParameter) / num30 : 0.0;
- var num32 = leftParameter;
- for (var i = 1; i < num30; i++)
- {
- x += num24;
- y += num25;
- item.X = x;
- item.Y = y;
- resultPolyline.Add(item);
- num32 += num31;
- resultParameters?.Add(num32);
- num24 += num26;
- num25 += num27;
- num26 += num22;
- num27 += num23;
- }
- }
- private static void DoCubicMidpointSubdivision(Point[] controlPoints, uint depth, double leftParameter, double rightParameter, double inverseErrorTolerance, ICollection<Point> resultPolyline, ICollection<double> resultParameters)
- {
- Point[] pointArray = { controlPoints[0], controlPoints[1], controlPoints[2], controlPoints[3] };
- var pointArray2 = new Point[4];
- pointArray2[3] = pointArray[3];
- pointArray[3] = GeometryHelper.Midpoint(pointArray[3], pointArray[2]);
- pointArray[2] = GeometryHelper.Midpoint(pointArray[2], pointArray[1]);
- pointArray[1] = GeometryHelper.Midpoint(pointArray[1], pointArray[0]);
- pointArray2[2] = pointArray[3];
- pointArray[3] = GeometryHelper.Midpoint(pointArray[3], pointArray[2]);
- pointArray[2] = GeometryHelper.Midpoint(pointArray[2], pointArray[1]);
- pointArray2[1] = pointArray[3];
- pointArray[3] = GeometryHelper.Midpoint(pointArray[3], pointArray[2]);
- pointArray2[0] = pointArray[3];
- depth--;
- var num = (leftParameter + rightParameter) * 0.5;
- if (depth > 0)
- {
- DoCubicMidpointSubdivision(pointArray, depth, leftParameter, num, inverseErrorTolerance,
- resultPolyline, resultParameters);
- resultPolyline.Add(pointArray2[0]);
- resultParameters?.Add(num);
- DoCubicMidpointSubdivision(pointArray2, depth, num, rightParameter, inverseErrorTolerance,
- resultPolyline, resultParameters);
- }
- else
- {
- DoCubicForwardDifferencing(pointArray, leftParameter, num, inverseErrorTolerance,
- resultPolyline, resultParameters);
- resultPolyline.Add(pointArray2[0]);
- resultParameters?.Add(num);
- DoCubicForwardDifferencing(pointArray2, num, rightParameter, inverseErrorTolerance,
- resultPolyline, resultParameters);
- }
- }
- private static void EnsureErrorTolerance(ref double errorTolerance)
- {
- if (errorTolerance <= 0.0)
- {
- errorTolerance = 0.25;
- }
- }
- public static void FlattenCubic(Point[] controlPoints, double errorTolerance, ICollection<Point> resultPolyline, bool skipFirstPoint, ICollection<double> resultParameters = null)
- {
- if (resultPolyline == null) throw new ArgumentNullException(nameof(resultPolyline));
- if (controlPoints == null) throw new ArgumentNullException(nameof(controlPoints));
- if (controlPoints.Length != 4) throw new ArgumentOutOfRangeException(nameof(controlPoints));
- EnsureErrorTolerance(ref errorTolerance);
- if (!skipFirstPoint)
- {
- resultPolyline.Add(controlPoints[0]);
- resultParameters?.Add(0.0);
- }
- if (IsCubicChordMonotone(controlPoints, errorTolerance * errorTolerance))
- {
- var flattener = new AdaptiveForwardDifferencingCubicFlattener(controlPoints, errorTolerance,
- errorTolerance, true);
- var p = new Point();
- var u = 0.0;
- while (flattener.Next(ref p, ref u))
- {
- resultPolyline.Add(p);
- resultParameters?.Add(u);
- }
- }
- else
- {
- var x = controlPoints[3].X - controlPoints[2].X + controlPoints[1].X - controlPoints[0].X;
- var y = controlPoints[3].Y - controlPoints[2].Y + controlPoints[1].Y - controlPoints[0].Y;
- var num4 = 1.0 / errorTolerance;
- var depth = Log8UnsignedInt32((uint) (MathHelper.Hypotenuse(x, y) * num4 + 0.5));
- if (depth > 0) depth--;
- if (depth > 0)
- DoCubicMidpointSubdivision(controlPoints, depth, 0.0, 1.0, 0.75 * num4, resultPolyline,
- resultParameters);
- else
- DoCubicForwardDifferencing(controlPoints, 0.0, 1.0, 0.75 * num4, resultPolyline,
- resultParameters);
- }
- resultPolyline.Add(controlPoints[3]);
- resultParameters?.Add(1.0);
- }
- public static void FlattenQuadratic(Point[] controlPoints, double errorTolerance, ICollection<Point> resultPolyline, bool skipFirstPoint, ICollection<double> resultParameters = null)
- {
- if (resultPolyline == null)
- {
- throw new ArgumentNullException(nameof(resultPolyline));
- }
- if (controlPoints == null)
- {
- throw new ArgumentNullException(nameof(controlPoints));
- }
- if (controlPoints.Length != 3)
- {
- throw new ArgumentOutOfRangeException(nameof(controlPoints));
- }
- EnsureErrorTolerance(ref errorTolerance);
- Point[] pointArray = { controlPoints[0], GeometryHelper.Lerp(controlPoints[0], controlPoints[1], 0.66666666666666663), GeometryHelper.Lerp(controlPoints[1], controlPoints[2], 0.33333333333333331), controlPoints[2] };
- FlattenCubic(pointArray, errorTolerance, resultPolyline, skipFirstPoint, resultParameters);
- }
- private static bool IsCubicChordMonotone(Point[] controlPoints, double squaredTolerance)
- {
- double num = GeometryHelper.SquaredDistance(controlPoints[0], controlPoints[3]);
- if (num <= squaredTolerance)
- {
- return false;
- }
- Vector lhs = controlPoints[3].Subtract(controlPoints[0]);
- Vector rhs = controlPoints[1].Subtract(controlPoints[0]);
- double num2 = GeometryHelper.Dot(lhs, rhs);
- if ((num2 < 0.0) || (num2 > num))
- {
- return false;
- }
- Vector vector3 = controlPoints[2].Subtract(controlPoints[0]);
- double num3 = GeometryHelper.Dot(lhs, vector3);
- if ((num3 < 0.0) || (num3 > num))
- {
- return false;
- }
- if (num2 > num3)
- {
- return false;
- }
- return true;
- }
- private static uint Log4Double(double d)
- {
- uint num = 0;
- while (d > 1.0)
- {
- d *= 0.25;
- num++;
- }
- return num;
- }
- private static uint Log4UnsignedInt32(uint i)
- {
- uint num = 0;
- while (i > 0)
- {
- i = i >> 2;
- num++;
- }
- return num;
- }
- private static uint Log8UnsignedInt32(uint i)
- {
- uint num = 0;
- while (i > 0)
- {
- i = i >> 3;
- num++;
- }
- return num;
- }
- private class AdaptiveForwardDifferencingCubicFlattener
- {
- private double _aX;
- private double _aY;
- private double _bX;
- private double _bY;
- private double _cX;
- private double _cY;
- private readonly double _distanceTolerance;
- private readonly bool _doParameters;
- private double _dParameter;
- private double _dX;
- private double _dY;
- private readonly double _flatnessTolerance;
- private int _numSteps;
- private double _parameter;
- internal AdaptiveForwardDifferencingCubicFlattener(Point[] controlPoints,
- double flatnessTolerance, double distanceTolerance, bool doParameters)
- {
- _numSteps = 1;
- _dParameter = 1.0;
- _flatnessTolerance = 3.0 * flatnessTolerance;
- _distanceTolerance = distanceTolerance;
- _doParameters = doParameters;
- _aX = -controlPoints[0].X + 3.0 * (controlPoints[1].X - controlPoints[2].X) +
- controlPoints[3].X;
- _aY = -controlPoints[0].Y + 3.0 * (controlPoints[1].Y - controlPoints[2].Y) +
- controlPoints[3].Y;
- _bX = 3.0 * (controlPoints[0].X - 2.0 * controlPoints[1].X + controlPoints[2].X);
- _bY = 3.0 * (controlPoints[0].Y - 2.0 * controlPoints[1].Y + controlPoints[2].Y);
- _cX = 3.0 * (-controlPoints[0].X + controlPoints[1].X);
- _cY = 3.0 * (-controlPoints[0].Y + controlPoints[1].Y);
- _dX = controlPoints[0].X;
- _dY = controlPoints[0].Y;
- }
- private void DoubleStepSize()
- {
- _aX *= 8.0;
- _aY *= 8.0;
- _bX *= 4.0;
- _bY *= 4.0;
- _cX += _cX;
- _cY += _cY;
- if (_doParameters) _dParameter *= 2.0;
- _numSteps = _numSteps >> 1;
- }
- private void HalveStepSize()
- {
- _aX *= 0.125;
- _aY *= 0.125;
- _bX *= 0.25;
- _bY *= 0.25;
- _cX *= 0.5;
- _cY *= 0.5;
- if (_doParameters) _dParameter *= 0.5;
- _numSteps = _numSteps << 1;
- }
- private void IncrementDifferencesAndParameter()
- {
- _dX = _aX + _bX + _cX + _dX;
- _dY = _aY + _bY + _cY + _dY;
- _cX = _aX + _aX + _aX + _bX + _bX + _cX;
- _cY = _aY + _aY + _aY + _bY + _bY + _cY;
- _bX = _aX + _aX + _aX + _bX;
- _bY = _aY + _aY + _aY + _bY;
- _numSteps--;
- _parameter += _dParameter;
- }
- private bool MustSubdivide(double flatnessTolerance)
- {
- var num = -(_aY + _bY + _cY);
- var num2 = _aX + _bX + _cX;
- var num3 = Math.Abs(num) + Math.Abs(num2);
- if (num3 <= _distanceTolerance) return false;
- num3 *= flatnessTolerance;
- if (Math.Abs(_cX * num + _cY * num2) <= num3 &&
- Math.Abs((_bX + _cX + _cX) * num + (_bY + _cY + _cY) * num2) <= num3) return false;
- return true;
- }
- // ReSharper disable once RedundantAssignment
- internal bool Next(ref Point p, ref double u)
- {
- while (MustSubdivide(_flatnessTolerance)) HalveStepSize();
- if ((_numSteps & 1) == 0)
- while (_numSteps > 1 && !MustSubdivide(_flatnessTolerance * 0.25))
- DoubleStepSize();
- IncrementDifferencesAndParameter();
- p.X = _dX;
- p.Y = _dY;
- u = _parameter;
- return _numSteps != 0;
- }
- }
- }
|