SvgArcSegment.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Drawing.Drawing2D;
  5. using System.Drawing;
  6. #pragma warning disable
  7. namespace Svg.Pathing
  8. {
  9. public sealed class SvgArcSegment : SvgPathSegment
  10. {
  11. private const double RadiansPerDegree = Math.PI / 180.0;
  12. private const double DoublePI = Math.PI * 2;
  13. public float RadiusX
  14. {
  15. get;
  16. set;
  17. }
  18. public float RadiusY
  19. {
  20. get;
  21. set;
  22. }
  23. public float Angle
  24. {
  25. get;
  26. set;
  27. }
  28. public SvgArcSweep Sweep
  29. {
  30. get;
  31. set;
  32. }
  33. public SvgArcSize Size
  34. {
  35. get;
  36. set;
  37. }
  38. public SvgArcSegment(PointF start, float radiusX, float radiusY, float angle, SvgArcSize size, SvgArcSweep sweep, PointF end)
  39. : base(start, end)
  40. {
  41. this.RadiusX = Math.Abs(radiusX);
  42. this.RadiusY = Math.Abs(radiusY);
  43. this.Angle = angle;
  44. this.Sweep = sweep;
  45. this.Size = size;
  46. }
  47. private static double CalculateVectorAngle(double ux, double uy, double vx, double vy)
  48. {
  49. double ta = Math.Atan2(uy, ux);
  50. double tb = Math.Atan2(vy, vx);
  51. if (tb >= ta)
  52. {
  53. return tb - ta;
  54. }
  55. return SvgArcSegment.DoublePI - (ta - tb);
  56. }
  57. public override void AddToPath(GraphicsPath graphicsPath)
  58. {
  59. if (this.Start == this.End)
  60. {
  61. return;
  62. }
  63. if (this.RadiusX == 0.0f && this.RadiusY == 0.0f)
  64. {
  65. graphicsPath.AddLine(this.Start, this.End);
  66. return;
  67. }
  68. double sinPhi = Math.Sin(this.Angle * SvgArcSegment.RadiansPerDegree);
  69. double cosPhi = Math.Cos(this.Angle * SvgArcSegment.RadiansPerDegree);
  70. double x1dash = cosPhi * (this.Start.X - this.End.X) / 2.0 + sinPhi * (this.Start.Y - this.End.Y) / 2.0;
  71. double y1dash = -sinPhi * (this.Start.X - this.End.X) / 2.0 + cosPhi * (this.Start.Y - this.End.Y) / 2.0;
  72. double root;
  73. double numerator = this.RadiusX * this.RadiusX * this.RadiusY * this.RadiusY - this.RadiusX * this.RadiusX * y1dash * y1dash - this.RadiusY * this.RadiusY * x1dash * x1dash;
  74. float rx = this.RadiusX;
  75. float ry = this.RadiusY;
  76. if (numerator < 0.0)
  77. {
  78. float s = (float)Math.Sqrt(1.0 - numerator / (this.RadiusX * this.RadiusX * this.RadiusY * this.RadiusY));
  79. rx *= s;
  80. ry *= s;
  81. root = 0.0;
  82. }
  83. else
  84. {
  85. root = ((this.Size == SvgArcSize.Large && this.Sweep == SvgArcSweep.Positive) || (this.Size == SvgArcSize.Small && this.Sweep == SvgArcSweep.Negative) ? -1.0 : 1.0) * Math.Sqrt(numerator / (this.RadiusX * this.RadiusX * y1dash * y1dash + this.RadiusY * this.RadiusY * x1dash * x1dash));
  86. }
  87. double cxdash = root * rx * y1dash / ry;
  88. double cydash = -root * ry * x1dash / rx;
  89. double cx = cosPhi * cxdash - sinPhi * cydash + (this.Start.X + this.End.X) / 2.0;
  90. double cy = sinPhi * cxdash + cosPhi * cydash + (this.Start.Y + this.End.Y) / 2.0;
  91. double theta1 = SvgArcSegment.CalculateVectorAngle(1.0, 0.0, (x1dash - cxdash) / rx, (y1dash - cydash) / ry);
  92. double dtheta = SvgArcSegment.CalculateVectorAngle((x1dash - cxdash) / rx, (y1dash - cydash) / ry, (-x1dash - cxdash) / rx, (-y1dash - cydash) / ry);
  93. if (this.Sweep == SvgArcSweep.Negative && dtheta > 0)
  94. {
  95. dtheta -= 2.0 * Math.PI;
  96. }
  97. else if (this.Sweep == SvgArcSweep.Positive && dtheta < 0)
  98. {
  99. dtheta += 2.0 * Math.PI;
  100. }
  101. int segments = (int)Math.Ceiling((double)Math.Abs(dtheta / (Math.PI / 2.0)));
  102. double delta = dtheta / segments;
  103. double t = 8.0 / 3.0 * Math.Sin(delta / 4.0) * Math.Sin(delta / 4.0) / Math.Sin(delta / 2.0);
  104. double startX = this.Start.X;
  105. double startY = this.Start.Y;
  106. for (int i = 0; i < segments; ++i)
  107. {
  108. double cosTheta1 = Math.Cos(theta1);
  109. double sinTheta1 = Math.Sin(theta1);
  110. double theta2 = theta1 + delta;
  111. double cosTheta2 = Math.Cos(theta2);
  112. double sinTheta2 = Math.Sin(theta2);
  113. double endpointX = cosPhi * rx * cosTheta2 - sinPhi * ry * sinTheta2 + cx;
  114. double endpointY = sinPhi * rx * cosTheta2 + cosPhi * ry * sinTheta2 + cy;
  115. double dx1 = t * (-cosPhi * rx * sinTheta1 - sinPhi * ry * cosTheta1);
  116. double dy1 = t * (-sinPhi * rx * sinTheta1 + cosPhi * ry * cosTheta1);
  117. double dxe = t * (cosPhi * rx * sinTheta2 + sinPhi * ry * cosTheta2);
  118. double dye = t * (sinPhi * rx * sinTheta2 - cosPhi * ry * cosTheta2);
  119. graphicsPath.AddBezier((float)startX, (float)startY, (float)(startX + dx1), (float)(startY + dy1),
  120. (float)(endpointX + dxe), (float)(endpointY + dye), (float)endpointX, (float)endpointY);
  121. theta1 = theta2;
  122. startX = (float)endpointX;
  123. startY = (float)endpointY;
  124. }
  125. }
  126. public override string ToString()
  127. {
  128. var arcFlag = this.Size == SvgArcSize.Large ? "1" : "0";
  129. var sweepFlag = this.Sweep == SvgArcSweep.Positive ? "1" : "0";
  130. return "A" + this.RadiusX.ToString() + " " + this.RadiusY.ToString() + " " + this.Angle.ToString() + " " + arcFlag + " " + sweepFlag + " " + this.End.ToSvgString();
  131. }
  132. }
  133. [Flags]
  134. public enum SvgArcSweep
  135. {
  136. Negative = 0,
  137. Positive = 1
  138. }
  139. [Flags]
  140. public enum SvgArcSize
  141. {
  142. Small = 0,
  143. Large = 1
  144. }
  145. }
  146. #pragma warning restore