123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 |
- using UnityEngine;
- using UnityEngine.Profiling;
- using System;
- using System.Collections.Generic;
- public delegate void SetPixelDelegate(int x, int y);
- public class SVGBasicDraw {
- private struct Vector2Ext {
- private readonly float _delta;
- private readonly Vector2 _point;
- public float t { get { return _delta; } }
- public Vector2 point { get { return _point; } }
- public Vector2Ext(Vector2 point, float t) {
- _point = point;
- _delta = t;
- }
- }
- private Vector2 _currentPoint;
- public SetPixelDelegate SetPixel;
- public Vector2 currentPoint { get { return _currentPoint; } }
- public SetPixelDelegate SetPixelMethod { set { SetPixel = value; } }
- public SVGBasicDraw() {
- _currentPoint = new Vector2(0f, 0f);
- }
- private static void Swap<T>(ref T x1, ref T x2) {
- T temp = x1;
- x1 = x2;
- x2 = temp;
- }
- public void MoveTo(float x, float y) {
- _currentPoint.x = x;
- _currentPoint.y = y;
- }
- public void MoveTo(Vector2 p) {
- _currentPoint = p;
- }
- public void Line(int x0, int y0, int x1, int y1) {
- bool steep = (Math.Abs(y1 - y0) > Math.Abs(x1 - x0));
- if(steep) {
- Swap(ref x0, ref y0);
- Swap(ref x1, ref y1);
- }
- if(x0 > x1) {
- Swap(ref x0, ref x1);
- Swap(ref y0, ref y1);
- }
- int deltax = x1 - x0,
- deltay = Math.Abs(y1 - y0),
- error = -(deltax + 1) / 2,
- y = y0,
- ystep = (y0 < y1) ? 1 : -1;
- for(int x = x0; x <= x1; x++) {
- if(steep)
- SetPixel(y, x);
- else
- SetPixel(x, y);
- error += deltay;
- if(error >= 0) {
- y += ystep;
- error -= deltax;
- }
- }
- }
- public void Line(float x0, float y0, float x1, float y1) {
- Line((int)x0, (int)y0, (int)x1, (int)y1);
- }
- public void Line(Vector2 p1, Vector2 p2) {
- Line(p1.x, p1.y, p2.x, p2.y);
- }
- public void LineTo(float x, float y) {
- Vector2 temp = new Vector2(x, y);
- Line(_currentPoint, temp);
- _currentPoint = temp;
- }
- public void LineTo(Vector2 p) {
- Line(_currentPoint, p);
- _currentPoint = p;
- }
- public void Rect(float x0, float y0, float x1, float y1) {
- MoveTo(x0, y0);
- LineTo(x1, y0);
- MoveTo(x1, y0);
- LineTo(x1, y1);
- MoveTo(x1, y1);
- LineTo(x0, y1);
- MoveTo(x0, y1);
- LineTo(x0, y0);
- }
- public void Rect(Vector2 p1, Vector2 p2) {
- Rect(p1.x, p1.y, p2.x, p2.y);
- }
- public void Rect(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4) {
- MoveTo(p1);
- LineTo(p2);
- LineTo(p3);
- LineTo(p4);
- LineTo(p1);
- }
- private void Circle(int x0, int y0, float radius) {
- float chuvi = 2f * Mathf.PI * radius;
- int _delta = (int)(chuvi / 2f);
- if(_delta > 50)
- _delta = 50;
- float _angle = (2 * Mathf.PI) / _delta;
- float tx, ty, temp;
- tx = x0;
- ty = radius + y0;
- Vector2 fPoint = new Vector2(tx, ty);
- MoveTo(fPoint);
- for(int i = 1; i <= _delta; i++) {
- temp = i * _angle;
- tx = radius * (float)Math.Sin(temp) + x0;
- ty = radius * (float)Math.Cos(temp) + y0;
- Vector2 tPoint = new Vector2(tx, ty);
- LineTo(tPoint);
- }
- LineTo(fPoint);
- }
- public void Circle(float x0, float y0, float r) {
- Circle((int)x0, (int)y0, r);
- }
- public void Circle(Vector2 p, float r) {
- Circle((int)p.x, (int)p.y, r);
- }
- private void Ellipse(int cx, int cy, int rx, int ry, float angle) {
- float chuvi = 2f * Mathf.PI * (float)Math.Sqrt(rx * rx + ry * ry);
- int steps = (int)(chuvi / 3);
- if(steps > 50)
- steps = 50;
- float beta = angle * Mathf.Deg2Rad;
- float sinbeta = Mathf.Sin(beta);
- float cosbeta = Mathf.Cos(beta);
- steps = 360 / steps;
- int i = 0;
- float alpha = i * Mathf.Deg2Rad;
- float sinalpha = Mathf.Sin(alpha);
- float cosalpha = Mathf.Cos(alpha);
- float _x = cx + (rx * cosalpha * cosbeta - ry * sinalpha * sinbeta);
- float _y = cy + (rx * cosalpha * sinbeta + ry * sinalpha * cosbeta);
- float _fPointx = _x;
- float _fPointy = _y;
- MoveTo(_x, _y);
- for(i = 1; i < 360; i += steps) {
- alpha = i * Mathf.Deg2Rad;
- sinalpha = Mathf.Sin(alpha);
- cosalpha = Mathf.Cos(alpha);
- _x = cx + (rx * cosalpha * cosbeta - ry * sinalpha * sinbeta);
- _y = cy + (rx * cosalpha * sinbeta + ry * sinalpha * cosbeta);
- LineTo(_x, _y);
- }
- LineTo(_fPointx, _fPointy);
- }
- public void Ellipse(float x0, float y0, float rx, float ry, float angle) {
- Ellipse((int)x0, (int)y0, (int)rx, (int)ry, angle);
- }
- public void Ellipse(Vector2 p, float rx, float ry, float angle) {
- Ellipse((int)p.x, (int)p.y, (int)rx, (int)ry, angle);
- }
- public void Arc(Vector2 p1, float rx, float ry, float angle, bool largeArcFlag, bool sweepFlag, Vector2 p2) {
- Profiler.BeginSample("SVGBasicDraw.Arc(...)");
- float tx, ty;
- double trx2, try2, tx2, ty2;
- float temp1, temp2;
- float _radian = (angle * Mathf.PI / 180.0f);
- float _CosRadian = (float)Math.Cos(_radian);
- float _SinRadian = (float)Math.Sin(_radian);
- temp1 = (p1.x - p2.x) / 2.0f;
- temp2 = (p1.y - p2.y) / 2.0f;
- tx = (_CosRadian * temp1) + (_SinRadian * temp2);
- ty = (-_SinRadian * temp1) + (_CosRadian * temp2);
- trx2 = rx * rx;
- try2 = ry * ry;
- tx2 = tx * tx;
- ty2 = ty * ty;
- double radiiCheck = tx2 / trx2 + ty2 / try2;
- if(radiiCheck > 1) {
- rx = (float)Math.Sqrt((float)radiiCheck) * rx;
- ry = (float)Math.Sqrt((float)radiiCheck) * ry;
- trx2 = rx * rx;
- try2 = ry * ry;
- }
- double tm1;
- tm1 = (trx2 * try2 - trx2 * ty2 - try2 * tx2) / (trx2 * ty2 + try2 * tx2);
- tm1 = (tm1 < 0) ? 0 : tm1;
- float tm2;
- tm2 = (largeArcFlag == sweepFlag) ? -(float)Math.Sqrt((float)tm1) : (float)Math.Sqrt((float)tm1);
- float tcx, tcy;
- tcx = tm2 * ((rx * ty) / ry);
- tcy = tm2 * (-(ry * tx) / rx);
- float cx, cy;
- cx = _CosRadian * tcx - _SinRadian * tcy + ((p1.x + p2.x) / 2.0f);
- cy = _SinRadian * tcx + _CosRadian * tcy + ((p1.y + p2.y) / 2.0f);
- float ux = (tx - tcx) / rx;
- float uy = (ty - tcy) / ry;
- float vx = (-tx - tcx) / rx;
- float vy = (-ty - tcy) / ry;
- float _angle, _delta;
- float p, n, t;
- n = (float)Math.Sqrt((ux * ux) + (uy * uy));
- p = ux;
- _angle = (uy < 0) ? -(float)Math.Acos(p / n) : (float)Math.Acos(p / n);
- _angle = _angle * 180.0f / Mathf.PI;
- _angle %= 360f;
- n = (float)Math.Sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
- p = ux * vx + uy * vy;
- t = p / n;
- if((Math.Abs(t) >= 0.99999f) && (Math.Abs(t) < 1.000009f)) {
- if(t > 0)
- t = 1f;
- else
- t = -1f;
- }
- _delta = (ux * vy - uy * vx < 0) ? -(float)Math.Acos(t) : (float)Math.Acos(t);
- _delta = _delta * 180.0f / Mathf.PI;
- if(!sweepFlag && _delta > 0)
- _delta -= 360f;
- else if(sweepFlag && _delta < 0)
- _delta += 360f;
- _delta %= 360f;
- int number = 100;
- float deltaT = _delta / number;
- Vector2 _point = new Vector2(0, 0);
- float t_angle;
- for(int i = 0; i <= number; i++) {
- t_angle = (deltaT * i + _angle) * Mathf.PI / 180.0f;
- _point.x = _CosRadian * rx * (float)Math.Cos(t_angle) - _SinRadian * ry * (float)Math.Sin(t_angle) + cx;
- _point.y = _SinRadian * rx * (float)Math.Cos(t_angle) + _CosRadian * ry * (float)Math.Sin(t_angle) + cy;
- LineTo(_point);
- }
- Profiler.EndSample();
- }
- public void ArcTo(float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag, Vector2 p) {
- Vector2 _tempPoint = new Vector2(_currentPoint.x, _currentPoint.y);
- Arc(_tempPoint, r1, r2, angle, largeArcFlag, sweepFlag, p);
- _currentPoint = p;
- }
- private static float BelongPosition(Vector2 a, Vector2 b, Vector2 c) {
- float _up = ((a.y - c.y) * (b.x - a.x)) - ((a.x - c.x) * (b.y - a.y));
- float _under = ((b.x - a.x) * (b.x - a.x)) + ((b.y - a.y) * (b.y - a.y));
- float _r = _up / _under;
- return _r;
- }
- //Caculate Distance from c point to line segment [a,b]
- //return d point is the point on that line segment.
- private static int NumberOfLimitForCubic(Vector2 a, Vector2 b, Vector2 c, Vector2 d) {
- float _r1 = BelongPosition(a, d, b);
- float _r2 = BelongPosition(a, d, c);
- if((_r1 * _r2) > 0)
- return 0;
- return 1;
- }
- private static float Distance(Vector2 a, Vector2 b, Vector2 c) {
- float _up = ((a.y - c.y) * (b.x - a.x)) - ((a.x - c.x) * (b.y - a.y));
- float _under = ((b.x - a.x) * (b.x - a.x)) + ((b.y - a.y) * (b.y - a.y));
- return Math.Abs(_up / _under) * (float)Math.Sqrt(_under);
- }
- private static Vector2 EvaluateForCubic(float t, Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4) {
- Vector2 result = new Vector2(0, 0);
- float b0 = (1.0f - t);
- float b1 = b0 * b0 * b0;
- float b2 = 3 * t * b0 * b0;
- float b3 = 3 * t * t * b0;
- float b4 = t * t * t;
- result.x = b1 * p1.x + b2 * p2.x + b3 * p3.x + b4 * p4.x;
- result.y = b1 * p1.y + b2 * p2.y + b3 * p3.y + b4 * p4.y;
- return result;
- }
- private static Vector2 EvaluateForQuadratic(float t, Vector2 p1, Vector2 p2, Vector2 p3) {
- Vector2 result = Vector2.zero;
- float b0 = (1.0f - t);
- float b1 = b0 * b0;
- float b2 = 2 * t * b0;
- float b3 = t * t;
- result.x = b1 * p1.x + b2 * p2.x + b3 * p3.x;
- result.y = b1 * p1.y + b2 * p2.y + b3 * p3.y;
- return result;
- }
- private static readonly LiteStack<Vector2Ext> _stack = new LiteStack<Vector2Ext>();
- private static readonly List<Vector2Ext> _limitList = new List<Vector2Ext>();
- private void CubicCurve(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, int numberOfLimit, bool cubic) {
- Profiler.BeginSample("SVGBasicDraw.CubicCurve(...)");
- MoveTo(p1);
- //MoveTo the first Point;
- //How many times the curve change form innegative -> negative or vice versa
- int _limit = numberOfLimit;
- float t1, t2, _flatness;
- t1 = 0.0f;
- //t1 is the start point of [0..1].
- t2 = 1.0f;
- //t2 is the end point of [0..1]
- _flatness = 1.0f;
- Vector2Ext _pStart, _pEnd, _pMid;
- _pStart = new Vector2Ext(cubic ? EvaluateForCubic(t1, p1, p2, p3, p4) : EvaluateForQuadratic(t1, p1, p2, p3), t1);
- _pEnd = new Vector2Ext(cubic ? EvaluateForCubic(t2, p1, p2, p3, p4) : EvaluateForQuadratic(t2, p1, p2, p3), t2);
- // The point on Line Segment[_pStart, _pEnd] correlate with _t
- _stack.Clear();
- _stack.Push(_pEnd);
- //Push End Point into Stack
- //Array of Change Point
- _limitList.Clear();
- if(_limitList.Capacity < _limit + 1)
- _limitList.Capacity = _limit + 1;
- int _count = 0;
- while(true) {
- _count++;
- float _tm = (t1 + t2) / 2;
- //tm is a middle of t1 .. t2. [t1 .. tm .. t2]
- //The point on the Curve correlate with tm
- _pMid = new Vector2Ext(cubic ? EvaluateForCubic(_tm, p1, p2, p3, p4) : EvaluateForQuadratic(_tm, p1, p2, p3), _tm);
- //Calculate Distance from Middle Point to the Flatnet
- float dist = Distance(_pStart.point, _stack.Peek().point, _pMid.point);
- //flag = true, Curve Segment must be drawn, else continue calculate other middle point.
- bool flag = false;
- if(dist < _flatness) {
- int i = 0;
- float mm = 0.0f;
- for(i = 0; i < _limit; i++) {
- mm = (t1 + _tm) / 2;
- Vector2Ext _q =
- new Vector2Ext(cubic ? EvaluateForCubic(mm, p1, p2, p3, p4) : EvaluateForQuadratic(mm, p1, p2, p3), mm);
- if(_limitList.Count - 1 < i)
- _limitList.Add(_q);
- else
- _limitList[i] = _q;
- dist = Distance(_pStart.point, _pMid.point, _q.point);
- if(dist >= _flatness)
- break;
- else
- _tm = mm;
- }
- if(i == _limit)
- flag = true;
- else {
- //Continue calculate the first point has Distance > Flatness
- _stack.Push(_pMid);
- for(int j = 0; j <= i; ++j)
- _stack.Push(_limitList[j]);
- t2 = mm;
- }
- }
- if(flag) {
- LineTo(_pStart.point);
- LineTo(_pMid.point);
- _pStart = _stack.Pop();
- if(_stack.Count == 0)
- break;
- _pMid = _stack.Peek();
- t1 = t2;
- t2 = _pMid.t;
- } else if(t2 > _tm) {
- //If Distance > Flatness and t1 < tm < t2 then new t2 is tm.
- _stack.Push(_pMid);
- t2 = _tm;
- }
- }
- LineTo(_pStart.point);
- Profiler.EndSample();
- }
- public void CubicCurve(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4) {
- int _temp = NumberOfLimitForCubic(p1, p2, p3, p4);
- CubicCurve(p1, p2, p3, p4, _temp, true);
- }
- public void CubicCurveTo(Vector2 p1, Vector2 p2, Vector2 p) {
- Vector2 _tempPoint = new Vector2(_currentPoint.x, _currentPoint.y);
- CubicCurve(_tempPoint, p1, p2, p);
- _currentPoint = p;
- }
- public void QuadraticCurve(Vector2 p1, Vector2 p2, Vector2 p3) {
- Vector2 p4 = new Vector2(p2.x, p2.y);
- CubicCurve(p1, p2, p3, p4, 0, false);
- _currentPoint = p3;
- }
- public void QuadraticCurveTo(Vector2 p1, Vector2 p) {
- Vector2 _tempPoint = new Vector2(_currentPoint.x, _currentPoint.y);
- QuadraticCurve(_tempPoint, p1, p);
- _currentPoint = p;
- }
- }
|