SketchGeometryEffect.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics.CodeAnalysis;
  4. using System.Linq;
  5. using System.Windows;
  6. using System.Windows.Media;
  7. using HandyControl.Expression.Drawing;
  8. namespace HandyControl.Expression.Media;
  9. public sealed class SketchGeometryEffect : GeometryEffect
  10. {
  11. private readonly long _randomSeed = DateTime.Now.Ticks;
  12. protected override GeometryEffect DeepCopy()
  13. {
  14. return new SketchGeometryEffect();
  15. }
  16. private static void DisturbPoints(RandomEngine random, double scale, IList<Point> points,
  17. IList<Vector> normals)
  18. {
  19. var count = points.Count;
  20. for (var i = 1; i < count; i++)
  21. {
  22. var num3 = random.NextGaussian(0.0, 1.0 * scale);
  23. var num4 = random.NextUniform(-0.5, 0.5) * scale;
  24. var point = points[i];
  25. var vector = normals[i];
  26. var vector2 = normals[i];
  27. var point2 = points[i];
  28. var vector3 = normals[i];
  29. var vector4 = normals[i];
  30. points[i] = new Point(point.X + vector.X * num4 - vector2.Y * num3,
  31. point2.Y + vector3.X * num3 + vector4.Y * num4);
  32. }
  33. }
  34. public override bool Equals(GeometryEffect geometryEffect)
  35. {
  36. return geometryEffect is SketchGeometryEffect;
  37. }
  38. private IEnumerable<SimpleSegment> GetEffectiveSegments(PathFigure pathFigure)
  39. {
  40. var startPoint = pathFigure.StartPoint;
  41. foreach (var iteratorVariable1 in pathFigure.AllSegments())
  42. foreach (var iteratorVariable2 in iteratorVariable1.PathSegment.GetSimpleSegments(
  43. iteratorVariable1.StartPoint))
  44. {
  45. yield return iteratorVariable2;
  46. startPoint = iteratorVariable2.Points.Last();
  47. }
  48. if (pathFigure.IsClosed) yield return SimpleSegment.Create(startPoint, pathFigure.StartPoint);
  49. }
  50. [SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse")]
  51. protected override bool UpdateCachedGeometry(Geometry input)
  52. {
  53. var flag = false;
  54. var inputPath = input.AsPathGeometry();
  55. if (inputPath != null) return flag | UpdateSketchGeometry(inputPath);
  56. CachedGeometry = input;
  57. return flag;
  58. }
  59. private bool UpdateSketchGeometry(PathGeometry inputPath)
  60. {
  61. var flag = false;
  62. flag |= GeometryHelper.EnsureGeometryType(out var geometry, ref CachedGeometry,
  63. () => new PathGeometry());
  64. flag |= geometry.Figures.EnsureListCount(inputPath.Figures.Count, () => new PathFigure());
  65. var random = new RandomEngine(_randomSeed);
  66. for (var i = 0; i < inputPath.Figures.Count; i++)
  67. {
  68. var pathFigure = inputPath.Figures[i];
  69. var isClosed = pathFigure.IsClosed;
  70. var isFilled = pathFigure.IsFilled;
  71. if (pathFigure.Segments.Count == 0)
  72. {
  73. flag |= geometry.Figures[i]
  74. .SetIfDifferent(PathFigure.StartPointProperty, pathFigure.StartPoint);
  75. flag |= geometry.Figures[i].Segments.EnsureListCount(0);
  76. }
  77. else
  78. {
  79. var list = new List<Point>(pathFigure.Segments.Count * 3);
  80. foreach (var segment in GetEffectiveSegments(pathFigure))
  81. {
  82. var resultPolyline = new List<Point>
  83. {
  84. segment.Points[0]
  85. };
  86. segment.Flatten(resultPolyline, 0.0, null);
  87. var polyline = new PolylineData(resultPolyline);
  88. if (resultPolyline.Count > 1 && polyline.TotalLength > 4.0)
  89. {
  90. var a = polyline.TotalLength / 8.0;
  91. var sampleCount = (int) Math.Max(2.0, Math.Ceiling(a));
  92. var interval = polyline.TotalLength / sampleCount;
  93. var scale = interval / 8.0;
  94. var samplePoints = new List<Point>(sampleCount);
  95. var sampleNormals = new List<Vector>(sampleCount);
  96. var sampleIndex = 0;
  97. PolylineHelper.PathMarch(polyline, 0.0, 0.0, delegate (MarchLocation location)
  98. {
  99. if (location.Reason == MarchStopReason.CompletePolyline) return double.NaN;
  100. if (location.Reason != MarchStopReason.CompleteStep) return location.Remain;
  101. if (sampleIndex++ == sampleCount) return double.NaN;
  102. samplePoints.Add(location.GetPoint(polyline.Points));
  103. sampleNormals.Add(location.GetNormal(polyline));
  104. return interval;
  105. });
  106. DisturbPoints(random, scale, samplePoints, sampleNormals);
  107. list.AddRange(samplePoints);
  108. }
  109. else
  110. {
  111. list.AddRange(resultPolyline);
  112. list.RemoveLast();
  113. }
  114. }
  115. if (!isClosed) list.Add(pathFigure.Segments.Last().GetLastPoint());
  116. flag |= PathFigureHelper.SyncPolylineFigure(geometry.Figures[i], list, isClosed,
  117. isFilled);
  118. }
  119. }
  120. if (flag) CachedGeometry = PathGeometryHelper.FixPathGeometryBoundary(CachedGeometry);
  121. return flag;
  122. }
  123. }