SVGBasicDraw.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. using UnityEngine;
  2. using UnityEngine.Profiling;
  3. using System;
  4. using System.Collections.Generic;
  5. public delegate void SetPixelDelegate(int x, int y);
  6. public class SVGBasicDraw {
  7. private struct Vector2Ext {
  8. private readonly float _delta;
  9. private readonly Vector2 _point;
  10. public float t { get { return _delta; } }
  11. public Vector2 point { get { return _point; } }
  12. public Vector2Ext(Vector2 point, float t) {
  13. _point = point;
  14. _delta = t;
  15. }
  16. }
  17. private Vector2 _currentPoint;
  18. public SetPixelDelegate SetPixel;
  19. public Vector2 currentPoint { get { return _currentPoint; } }
  20. public SetPixelDelegate SetPixelMethod { set { SetPixel = value; } }
  21. public SVGBasicDraw() {
  22. _currentPoint = new Vector2(0f, 0f);
  23. }
  24. private static void Swap<T>(ref T x1, ref T x2) {
  25. T temp = x1;
  26. x1 = x2;
  27. x2 = temp;
  28. }
  29. public void MoveTo(float x, float y) {
  30. _currentPoint.x = x;
  31. _currentPoint.y = y;
  32. }
  33. public void MoveTo(Vector2 p) {
  34. _currentPoint = p;
  35. }
  36. public void Line(int x0, int y0, int x1, int y1) {
  37. bool steep = (Math.Abs(y1 - y0) > Math.Abs(x1 - x0));
  38. if(steep) {
  39. Swap(ref x0, ref y0);
  40. Swap(ref x1, ref y1);
  41. }
  42. if(x0 > x1) {
  43. Swap(ref x0, ref x1);
  44. Swap(ref y0, ref y1);
  45. }
  46. int deltax = x1 - x0,
  47. deltay = Math.Abs(y1 - y0),
  48. error = -(deltax + 1) / 2,
  49. y = y0,
  50. ystep = (y0 < y1) ? 1 : -1;
  51. for(int x = x0; x <= x1; x++) {
  52. if(steep)
  53. SetPixel(y, x);
  54. else
  55. SetPixel(x, y);
  56. error += deltay;
  57. if(error >= 0) {
  58. y += ystep;
  59. error -= deltax;
  60. }
  61. }
  62. }
  63. public void Line(float x0, float y0, float x1, float y1) {
  64. Line((int)x0, (int)y0, (int)x1, (int)y1);
  65. }
  66. public void Line(Vector2 p1, Vector2 p2) {
  67. Line(p1.x, p1.y, p2.x, p2.y);
  68. }
  69. public void LineTo(float x, float y) {
  70. Vector2 temp = new Vector2(x, y);
  71. Line(_currentPoint, temp);
  72. _currentPoint = temp;
  73. }
  74. public void LineTo(Vector2 p) {
  75. Line(_currentPoint, p);
  76. _currentPoint = p;
  77. }
  78. public void Rect(float x0, float y0, float x1, float y1) {
  79. MoveTo(x0, y0);
  80. LineTo(x1, y0);
  81. MoveTo(x1, y0);
  82. LineTo(x1, y1);
  83. MoveTo(x1, y1);
  84. LineTo(x0, y1);
  85. MoveTo(x0, y1);
  86. LineTo(x0, y0);
  87. }
  88. public void Rect(Vector2 p1, Vector2 p2) {
  89. Rect(p1.x, p1.y, p2.x, p2.y);
  90. }
  91. public void Rect(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4) {
  92. MoveTo(p1);
  93. LineTo(p2);
  94. LineTo(p3);
  95. LineTo(p4);
  96. LineTo(p1);
  97. }
  98. private void Circle(int x0, int y0, float radius) {
  99. float chuvi = 2f * Mathf.PI * radius;
  100. int _delta = (int)(chuvi / 2f);
  101. if(_delta > 50)
  102. _delta = 50;
  103. float _angle = (2 * Mathf.PI) / _delta;
  104. float tx, ty, temp;
  105. tx = x0;
  106. ty = radius + y0;
  107. Vector2 fPoint = new Vector2(tx, ty);
  108. MoveTo(fPoint);
  109. for(int i = 1; i <= _delta; i++) {
  110. temp = i * _angle;
  111. tx = radius * (float)Math.Sin(temp) + x0;
  112. ty = radius * (float)Math.Cos(temp) + y0;
  113. Vector2 tPoint = new Vector2(tx, ty);
  114. LineTo(tPoint);
  115. }
  116. LineTo(fPoint);
  117. }
  118. public void Circle(float x0, float y0, float r) {
  119. Circle((int)x0, (int)y0, r);
  120. }
  121. public void Circle(Vector2 p, float r) {
  122. Circle((int)p.x, (int)p.y, r);
  123. }
  124. private void Ellipse(int cx, int cy, int rx, int ry, float angle) {
  125. float chuvi = 2f * Mathf.PI * (float)Math.Sqrt(rx * rx + ry * ry);
  126. int steps = (int)(chuvi / 3);
  127. if(steps > 50)
  128. steps = 50;
  129. float beta = angle * Mathf.Deg2Rad;
  130. float sinbeta = Mathf.Sin(beta);
  131. float cosbeta = Mathf.Cos(beta);
  132. steps = 360 / steps;
  133. int i = 0;
  134. float alpha = i * Mathf.Deg2Rad;
  135. float sinalpha = Mathf.Sin(alpha);
  136. float cosalpha = Mathf.Cos(alpha);
  137. float _x = cx + (rx * cosalpha * cosbeta - ry * sinalpha * sinbeta);
  138. float _y = cy + (rx * cosalpha * sinbeta + ry * sinalpha * cosbeta);
  139. float _fPointx = _x;
  140. float _fPointy = _y;
  141. MoveTo(_x, _y);
  142. for(i = 1; i < 360; i += steps) {
  143. alpha = i * Mathf.Deg2Rad;
  144. sinalpha = Mathf.Sin(alpha);
  145. cosalpha = Mathf.Cos(alpha);
  146. _x = cx + (rx * cosalpha * cosbeta - ry * sinalpha * sinbeta);
  147. _y = cy + (rx * cosalpha * sinbeta + ry * sinalpha * cosbeta);
  148. LineTo(_x, _y);
  149. }
  150. LineTo(_fPointx, _fPointy);
  151. }
  152. public void Ellipse(float x0, float y0, float rx, float ry, float angle) {
  153. Ellipse((int)x0, (int)y0, (int)rx, (int)ry, angle);
  154. }
  155. public void Ellipse(Vector2 p, float rx, float ry, float angle) {
  156. Ellipse((int)p.x, (int)p.y, (int)rx, (int)ry, angle);
  157. }
  158. public void Arc(Vector2 p1, float rx, float ry, float angle, bool largeArcFlag, bool sweepFlag, Vector2 p2) {
  159. Profiler.BeginSample("SVGBasicDraw.Arc(...)");
  160. float tx, ty;
  161. double trx2, try2, tx2, ty2;
  162. float temp1, temp2;
  163. float _radian = (angle * Mathf.PI / 180.0f);
  164. float _CosRadian = (float)Math.Cos(_radian);
  165. float _SinRadian = (float)Math.Sin(_radian);
  166. temp1 = (p1.x - p2.x) / 2.0f;
  167. temp2 = (p1.y - p2.y) / 2.0f;
  168. tx = (_CosRadian * temp1) + (_SinRadian * temp2);
  169. ty = (-_SinRadian * temp1) + (_CosRadian * temp2);
  170. trx2 = rx * rx;
  171. try2 = ry * ry;
  172. tx2 = tx * tx;
  173. ty2 = ty * ty;
  174. double radiiCheck = tx2 / trx2 + ty2 / try2;
  175. if(radiiCheck > 1) {
  176. rx = (float)Math.Sqrt((float)radiiCheck) * rx;
  177. ry = (float)Math.Sqrt((float)radiiCheck) * ry;
  178. trx2 = rx * rx;
  179. try2 = ry * ry;
  180. }
  181. double tm1;
  182. tm1 = (trx2 * try2 - trx2 * ty2 - try2 * tx2) / (trx2 * ty2 + try2 * tx2);
  183. tm1 = (tm1 < 0) ? 0 : tm1;
  184. float tm2;
  185. tm2 = (largeArcFlag == sweepFlag) ? -(float)Math.Sqrt((float)tm1) : (float)Math.Sqrt((float)tm1);
  186. float tcx, tcy;
  187. tcx = tm2 * ((rx * ty) / ry);
  188. tcy = tm2 * (-(ry * tx) / rx);
  189. float cx, cy;
  190. cx = _CosRadian * tcx - _SinRadian * tcy + ((p1.x + p2.x) / 2.0f);
  191. cy = _SinRadian * tcx + _CosRadian * tcy + ((p1.y + p2.y) / 2.0f);
  192. float ux = (tx - tcx) / rx;
  193. float uy = (ty - tcy) / ry;
  194. float vx = (-tx - tcx) / rx;
  195. float vy = (-ty - tcy) / ry;
  196. float _angle, _delta;
  197. float p, n, t;
  198. n = (float)Math.Sqrt((ux * ux) + (uy * uy));
  199. p = ux;
  200. _angle = (uy < 0) ? -(float)Math.Acos(p / n) : (float)Math.Acos(p / n);
  201. _angle = _angle * 180.0f / Mathf.PI;
  202. _angle %= 360f;
  203. n = (float)Math.Sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
  204. p = ux * vx + uy * vy;
  205. t = p / n;
  206. if((Math.Abs(t) >= 0.99999f) && (Math.Abs(t) < 1.000009f)) {
  207. if(t > 0)
  208. t = 1f;
  209. else
  210. t = -1f;
  211. }
  212. _delta = (ux * vy - uy * vx < 0) ? -(float)Math.Acos(t) : (float)Math.Acos(t);
  213. _delta = _delta * 180.0f / Mathf.PI;
  214. if(!sweepFlag && _delta > 0)
  215. _delta -= 360f;
  216. else if(sweepFlag && _delta < 0)
  217. _delta += 360f;
  218. _delta %= 360f;
  219. int number = 100;
  220. float deltaT = _delta / number;
  221. Vector2 _point = new Vector2(0, 0);
  222. float t_angle;
  223. for(int i = 0; i <= number; i++) {
  224. t_angle = (deltaT * i + _angle) * Mathf.PI / 180.0f;
  225. _point.x = _CosRadian * rx * (float)Math.Cos(t_angle) - _SinRadian * ry * (float)Math.Sin(t_angle) + cx;
  226. _point.y = _SinRadian * rx * (float)Math.Cos(t_angle) + _CosRadian * ry * (float)Math.Sin(t_angle) + cy;
  227. LineTo(_point);
  228. }
  229. Profiler.EndSample();
  230. }
  231. public void ArcTo(float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag, Vector2 p) {
  232. Vector2 _tempPoint = new Vector2(_currentPoint.x, _currentPoint.y);
  233. Arc(_tempPoint, r1, r2, angle, largeArcFlag, sweepFlag, p);
  234. _currentPoint = p;
  235. }
  236. private static float BelongPosition(Vector2 a, Vector2 b, Vector2 c) {
  237. float _up = ((a.y - c.y) * (b.x - a.x)) - ((a.x - c.x) * (b.y - a.y));
  238. float _under = ((b.x - a.x) * (b.x - a.x)) + ((b.y - a.y) * (b.y - a.y));
  239. float _r = _up / _under;
  240. return _r;
  241. }
  242. //Caculate Distance from c point to line segment [a,b]
  243. //return d point is the point on that line segment.
  244. private static int NumberOfLimitForCubic(Vector2 a, Vector2 b, Vector2 c, Vector2 d) {
  245. float _r1 = BelongPosition(a, d, b);
  246. float _r2 = BelongPosition(a, d, c);
  247. if((_r1 * _r2) > 0)
  248. return 0;
  249. return 1;
  250. }
  251. private static float Distance(Vector2 a, Vector2 b, Vector2 c) {
  252. float _up = ((a.y - c.y) * (b.x - a.x)) - ((a.x - c.x) * (b.y - a.y));
  253. float _under = ((b.x - a.x) * (b.x - a.x)) + ((b.y - a.y) * (b.y - a.y));
  254. return Math.Abs(_up / _under) * (float)Math.Sqrt(_under);
  255. }
  256. private static Vector2 EvaluateForCubic(float t, Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4) {
  257. Vector2 result = new Vector2(0, 0);
  258. float b0 = (1.0f - t);
  259. float b1 = b0 * b0 * b0;
  260. float b2 = 3 * t * b0 * b0;
  261. float b3 = 3 * t * t * b0;
  262. float b4 = t * t * t;
  263. result.x = b1 * p1.x + b2 * p2.x + b3 * p3.x + b4 * p4.x;
  264. result.y = b1 * p1.y + b2 * p2.y + b3 * p3.y + b4 * p4.y;
  265. return result;
  266. }
  267. private static Vector2 EvaluateForQuadratic(float t, Vector2 p1, Vector2 p2, Vector2 p3) {
  268. Vector2 result = Vector2.zero;
  269. float b0 = (1.0f - t);
  270. float b1 = b0 * b0;
  271. float b2 = 2 * t * b0;
  272. float b3 = t * t;
  273. result.x = b1 * p1.x + b2 * p2.x + b3 * p3.x;
  274. result.y = b1 * p1.y + b2 * p2.y + b3 * p3.y;
  275. return result;
  276. }
  277. private static readonly LiteStack<Vector2Ext> _stack = new LiteStack<Vector2Ext>();
  278. private static readonly List<Vector2Ext> _limitList = new List<Vector2Ext>();
  279. private void CubicCurve(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, int numberOfLimit, bool cubic) {
  280. Profiler.BeginSample("SVGBasicDraw.CubicCurve(...)");
  281. MoveTo(p1);
  282. //MoveTo the first Point;
  283. //How many times the curve change form innegative -> negative or vice versa
  284. int _limit = numberOfLimit;
  285. float t1, t2, _flatness;
  286. t1 = 0.0f;
  287. //t1 is the start point of [0..1].
  288. t2 = 1.0f;
  289. //t2 is the end point of [0..1]
  290. _flatness = 1.0f;
  291. Vector2Ext _pStart, _pEnd, _pMid;
  292. _pStart = new Vector2Ext(cubic ? EvaluateForCubic(t1, p1, p2, p3, p4) : EvaluateForQuadratic(t1, p1, p2, p3), t1);
  293. _pEnd = new Vector2Ext(cubic ? EvaluateForCubic(t2, p1, p2, p3, p4) : EvaluateForQuadratic(t2, p1, p2, p3), t2);
  294. // The point on Line Segment[_pStart, _pEnd] correlate with _t
  295. _stack.Clear();
  296. _stack.Push(_pEnd);
  297. //Push End Point into Stack
  298. //Array of Change Point
  299. _limitList.Clear();
  300. if(_limitList.Capacity < _limit + 1)
  301. _limitList.Capacity = _limit + 1;
  302. int _count = 0;
  303. while(true) {
  304. _count++;
  305. float _tm = (t1 + t2) / 2;
  306. //tm is a middle of t1 .. t2. [t1 .. tm .. t2]
  307. //The point on the Curve correlate with tm
  308. _pMid = new Vector2Ext(cubic ? EvaluateForCubic(_tm, p1, p2, p3, p4) : EvaluateForQuadratic(_tm, p1, p2, p3), _tm);
  309. //Calculate Distance from Middle Point to the Flatnet
  310. float dist = Distance(_pStart.point, _stack.Peek().point, _pMid.point);
  311. //flag = true, Curve Segment must be drawn, else continue calculate other middle point.
  312. bool flag = false;
  313. if(dist < _flatness) {
  314. int i = 0;
  315. float mm = 0.0f;
  316. for(i = 0; i < _limit; i++) {
  317. mm = (t1 + _tm) / 2;
  318. Vector2Ext _q =
  319. new Vector2Ext(cubic ? EvaluateForCubic(mm, p1, p2, p3, p4) : EvaluateForQuadratic(mm, p1, p2, p3), mm);
  320. if(_limitList.Count - 1 < i)
  321. _limitList.Add(_q);
  322. else
  323. _limitList[i] = _q;
  324. dist = Distance(_pStart.point, _pMid.point, _q.point);
  325. if(dist >= _flatness)
  326. break;
  327. else
  328. _tm = mm;
  329. }
  330. if(i == _limit)
  331. flag = true;
  332. else {
  333. //Continue calculate the first point has Distance > Flatness
  334. _stack.Push(_pMid);
  335. for(int j = 0; j <= i; ++j)
  336. _stack.Push(_limitList[j]);
  337. t2 = mm;
  338. }
  339. }
  340. if(flag) {
  341. LineTo(_pStart.point);
  342. LineTo(_pMid.point);
  343. _pStart = _stack.Pop();
  344. if(_stack.Count == 0)
  345. break;
  346. _pMid = _stack.Peek();
  347. t1 = t2;
  348. t2 = _pMid.t;
  349. } else if(t2 > _tm) {
  350. //If Distance > Flatness and t1 < tm < t2 then new t2 is tm.
  351. _stack.Push(_pMid);
  352. t2 = _tm;
  353. }
  354. }
  355. LineTo(_pStart.point);
  356. Profiler.EndSample();
  357. }
  358. public void CubicCurve(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4) {
  359. int _temp = NumberOfLimitForCubic(p1, p2, p3, p4);
  360. CubicCurve(p1, p2, p3, p4, _temp, true);
  361. }
  362. public void CubicCurveTo(Vector2 p1, Vector2 p2, Vector2 p) {
  363. Vector2 _tempPoint = new Vector2(_currentPoint.x, _currentPoint.y);
  364. CubicCurve(_tempPoint, p1, p2, p);
  365. _currentPoint = p;
  366. }
  367. public void QuadraticCurve(Vector2 p1, Vector2 p2, Vector2 p3) {
  368. Vector2 p4 = new Vector2(p2.x, p2.y);
  369. CubicCurve(p1, p2, p3, p4, 0, false);
  370. _currentPoint = p3;
  371. }
  372. public void QuadraticCurveTo(Vector2 p1, Vector2 p) {
  373. Vector2 _tempPoint = new Vector2(_currentPoint.x, _currentPoint.y);
  374. QuadraticCurve(_tempPoint, p1, p);
  375. _currentPoint = p;
  376. }
  377. }