|
|
|
|
|
|
using System; |
|
|
|
using System.Collections; |
|
|
|
using Vector2 = UnityEngine.Vector2; |
|
|
|
using Vector3 = UnityEngine.Vector3; |
|
|
|
|
|
|
|
namespace Unity.UIWidgets.ui { |
|
|
|
public enum PathOperation { |
|
|
|
|
|
|
xor, |
|
|
|
reverseDifference, |
|
|
|
} |
|
|
|
|
|
|
|
public class Path { |
|
|
|
const float _KAPPA90 = 0.5522847493f; |
|
|
|
|
|
|
|
|
|
|
float _minX, _minY; |
|
|
|
float _maxX, _maxY; |
|
|
|
|
|
|
|
|
|
|
|
PathCache _cache; |
|
|
|
|
|
|
|
static uint pathGlobalKey = 0; |
|
|
|
|
|
|
public uint pathKey { |
|
|
|
get { |
|
|
|
return this._pathKey; |
|
|
|
} |
|
|
|
get { return this._pathKey; } |
|
|
|
} |
|
|
|
|
|
|
|
public Path(int capacity = 128) { |
|
|
|
|
|
|
|
|
|
|
public List<float> commands => this._commands; |
|
|
|
public List<float> commands { |
|
|
|
get { return this._commands; } |
|
|
|
} |
|
|
|
|
|
|
|
public override string ToString() { |
|
|
|
var sb = new StringBuilder("Path: count = " + this._commands.Count); |
|
|
|
|
|
|
|
|
|
|
internal PathCache flatten(float scale) { |
|
|
|
scale = Mathf.Round(scale * 2.0f) / 2.0f; // round to 0.5f
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this._cache = new PathCache(scale); |
|
|
|
|
|
|
|
var i = 0; |
|
|
|
|
|
|
if (x < this._minX) { |
|
|
|
this._minX = x; |
|
|
|
} |
|
|
|
|
|
|
|
if (y < this._minY) { |
|
|
|
this._minY = y; |
|
|
|
} |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (y > this._maxY) { |
|
|
|
this._maxY = y; |
|
|
|
} |
|
|
|
|
|
|
public static Path combine(PathOperation operation, Path path1, Path path2) { |
|
|
|
D.assert(path1 != null); |
|
|
|
D.assert(path2 != null); |
|
|
|
Path path = new Path(); |
|
|
|
throw new UIWidgetsError("Path._op() not implemented yet!"); |
|
|
|
Path path = null; |
|
|
|
D.assert(() => { |
|
|
|
Debug.LogWarning("Path._op() not implemented yet!"); |
|
|
|
return true; |
|
|
|
}); |
|
|
|
return path; |
|
|
|
// if (path._op(path1, path2, (int) operation)) {
|
|
|
|
// return path;
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
public PathMetrics computeMetrics(bool forceClosed = false) { |
|
|
|
return PathMetrics._(this, forceClosed); |
|
|
|
} |
|
|
|
|
|
|
|
void _appendMoveTo(float x, float y) { |
|
|
|
this._commands.Add((float) PathCommand.moveTo); |
|
|
|
this._commands.Add(x); |
|
|
|
|
|
|
this._commandy = y; |
|
|
|
|
|
|
|
|
|
|
|
this._pathKey = pathGlobalKey++; |
|
|
|
this._cache = null; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
this._commandx = x; |
|
|
|
this._commandy = y; |
|
|
|
|
|
|
|
|
|
|
|
this._pathKey = pathGlobalKey++; |
|
|
|
this._cache = null; |
|
|
|
} |
|
|
|
|
|
|
this._expandBounds(x1, y1); |
|
|
|
this._expandBounds(x2, y2); |
|
|
|
this._expandBounds(x3, y3); |
|
|
|
|
|
|
|
|
|
|
|
this._commands.Add((float) PathCommand.bezierTo); |
|
|
|
this._commands.Add(x1); |
|
|
|
this._commands.Add(y1); |
|
|
|
|
|
|
this._commands.Add(y3); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this._pathKey = pathGlobalKey++; |
|
|
|
this._cache = null; |
|
|
|
} |
|
|
|
|
|
|
this._commands.Add(winding); |
|
|
|
|
|
|
|
|
|
|
|
this._pathKey = pathGlobalKey++; |
|
|
|
this._cache = null; |
|
|
|
} |
|
|
|
|
|
|
var y0 = this._commandy; |
|
|
|
|
|
|
|
|
|
|
|
this._appendMoveTo(x + x0, y + y0); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
public void relativeLineTo(float x, float y) { |
|
|
|
var x0 = this._commandx; |
|
|
|
var y0 = this._commandy; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void lineTo(float x, float y) { |
|
|
|
this._appendLineTo(x, y); |
|
|
|
} |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.cubicTo(x0 + c1x, y0 + c1y, x0 + c2x, y0 + c2y, x0 + x, y0 + y); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
if (!(w > 0)) { |
|
|
|
this.lineTo(x2, y2); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (w == 1) { |
|
|
|
this.quadraticBezierTo(x1, y1, x2, y2); |
|
|
|
return; |
|
|
|
|
|
|
var quadX = new float[5]; |
|
|
|
var quadY = new float[5]; |
|
|
|
conic.chopIntoQuadsPOW2(quadX, quadY, 1); |
|
|
|
|
|
|
|
|
|
|
|
this.quadraticBezierTo(quadX[1], quadY[1], quadX[2], quadY[2]); |
|
|
|
this.quadraticBezierTo(quadX[3], quadY[3], quadX[4], quadY[4]); |
|
|
|
} |
|
|
|
|
|
|
var y0 = this._commandy; |
|
|
|
|
|
|
|
|
|
|
|
this.conicTo(x0 + x1, y0 + y1, x0 + x2, y0 + y2, w); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
var squareRy = ry * ry; |
|
|
|
var squareX = transformedMidPoint.dx * transformedMidPoint.dx; |
|
|
|
var squareY = transformedMidPoint.dy * transformedMidPoint.dy; |
|
|
|
|
|
|
|
|
|
|
|
// Check if the radii are big enough to draw the arc, scale radii if not.
|
|
|
|
// http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii
|
|
|
|
var radiiScale = squareX / squareRx + squareY / squareRy; |
|
|
|
|
|
|
ry *= radiiScale; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var unitPts = new [] { |
|
|
|
var unitPts = new[] { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!clockwise != largeArc) { // flipped from the original implementation
|
|
|
|
if (!clockwise != largeArc) { |
|
|
|
// flipped from the original implementation
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (thetaArc < 0 && clockwise) { // arcSweep flipped from the original implementation
|
|
|
|
if (thetaArc < 0 && clockwise) { |
|
|
|
// arcSweep flipped from the original implementation
|
|
|
|
} else if (thetaArc > 0 && !clockwise) { // arcSweep flipped from the original implementation
|
|
|
|
} |
|
|
|
else if (thetaArc > 0 && !clockwise) { |
|
|
|
// arcSweep flipped from the original implementation
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// the arc may be slightly bigger than 1/4 circle, so allow up to 1/3rd
|
|
|
|
int segments = Mathf.CeilToInt(Mathf.Abs(thetaArc / (2 * Mathf.PI / 3))); |
|
|
|
var thetaWidth = thetaArc / segments; |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool expectIntegers = ScalarUtils.ScalarNearlyZero(Mathf.PI/2 - Mathf.Abs(thetaWidth)) && |
|
|
|
|
|
|
|
bool expectIntegers = ScalarUtils.ScalarNearlyZero(Mathf.PI / 2 - Mathf.Abs(thetaWidth)) && |
|
|
|
ScalarUtils.ScalarIsInteger(rx) && ScalarUtils.ScalarIsInteger(ry) && |
|
|
|
ScalarUtils.ScalarIsInteger(x1) && ScalarUtils.ScalarIsInteger(y1); |
|
|
|
|
|
|
|
|
|
|
unitPts[1] += centerPoint; |
|
|
|
unitPts[0] = unitPts[1]; |
|
|
|
unitPts[0] = unitPts[0].translate(t * sinEndTheta, -t * cosEndTheta); |
|
|
|
var mapped = new [] { |
|
|
|
var mapped = new[] { |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
Computing the arc width introduces rounding errors that cause arcs to start |
|
|
|
outside their marks. A round rect may lose convexity as a result. If the input |
|
|
|
|
|
|
a0 = Mathf.Atan2(dx0, -dy0); |
|
|
|
a1 = Mathf.Atan2(-dx1, dy1); |
|
|
|
dir = PathWinding.clockwise; |
|
|
|
} else { |
|
|
|
} |
|
|
|
else { |
|
|
|
cx = x1 + dx0 * d + -dy0 * radius; |
|
|
|
cy = y1 + dy0 * d + dx0 * radius; |
|
|
|
a0 = Mathf.Atan2(-dx0, dy0); |
|
|
|
|
|
|
if (dir == PathWinding.clockwise) { |
|
|
|
if (Mathf.Abs(da) >= Mathf.PI * 2) { |
|
|
|
da = Mathf.PI * 2; |
|
|
|
} else { |
|
|
|
} |
|
|
|
else { |
|
|
|
|
|
|
|
} else { |
|
|
|
} |
|
|
|
else { |
|
|
|
} else { |
|
|
|
} |
|
|
|
else { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Split arc into max 90 degree segments.
|
|
|
|
int ndivs = Mathf.Max(1, Mathf.Min((int) (Mathf.Abs(da) / (Mathf.PI * 0.5f) + 0.5f), 5)); |
|
|
|
float hda = (da / ndivs) / 2.0f; |
|
|
|
|
|
|
|
|
|
|
if (move == PathCommand.moveTo) { |
|
|
|
this._appendMoveTo(x1, y1); |
|
|
|
} else { |
|
|
|
} |
|
|
|
else { |
|
|
|
} else { |
|
|
|
} |
|
|
|
else { |
|
|
|
float c1x = px + ptanx; |
|
|
|
float c1y = py + ptany; |
|
|
|
float c2x = x - tanx; |
|
|
|
|
|
|
|
|
|
|
this._appendBezierTo(c1x, c1y, c2x, c2y, x1, y1); |
|
|
|
} |
|
|
|
|
|
|
|
px = x; |
|
|
|
py = y; |
|
|
|
ptanx = tanx; |
|
|
|
|
|
|
if (points.Count == 0) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this._appendMoveTo(points[0].dx, points[0].dy); |
|
|
|
|
|
|
|
for (int i = 1; i < points.Count; i++) { |
|
|
|
|
|
|
this.addPath(path); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var transform = Matrix3.makeTrans(offset.dx, offset.dy); |
|
|
|
this.addPath(path, transform); |
|
|
|
} |
|
|
|
|
|
|
if (transform != null) { |
|
|
|
transform.mapXY(x, y, out x, out y); |
|
|
|
} |
|
|
|
|
|
|
|
this._appendMoveTo(x, y); |
|
|
|
} |
|
|
|
i += 3; |
|
|
|
|
|
|
if (transform != null) { |
|
|
|
transform.mapXY(x, y, out x, out y); |
|
|
|
} |
|
|
|
|
|
|
|
this._appendLineTo(x, y); |
|
|
|
} |
|
|
|
i += 3; |
|
|
|
|
|
|
transform.mapXY(c2x, c2y, out c2x, out c2y); |
|
|
|
transform.mapXY(x1, y1, out x1, out y1); |
|
|
|
} |
|
|
|
|
|
|
|
this._appendBezierTo(c1x, c1y, c2x, c2y, x1, y1); |
|
|
|
} |
|
|
|
i += 7; |
|
|
|
|
|
|
|
|
|
|
return totalW != 0; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static int windingLine(float x0, float y0, float x1, float y1, float x, float y) { |
|
|
|
if (y0 == y1) { |
|
|
|
return 0; |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public class PathMetrics : IEnumerable<PathMetric> { |
|
|
|
public PathMetrics(IEnumerator<PathMetric> enumerator) { |
|
|
|
this._enumerator = enumerator; |
|
|
|
} |
|
|
|
|
|
|
|
public static PathMetrics _(Path path, bool forceClosed) { |
|
|
|
return new PathMetrics(PathMetricIterator._(new _PathMeasure())); // TODO: complete the implementation
|
|
|
|
} |
|
|
|
|
|
|
|
public readonly IEnumerator<PathMetric> _enumerator; |
|
|
|
|
|
|
|
public IEnumerator<PathMetric> GetEnumerator() { |
|
|
|
return this._enumerator; |
|
|
|
} |
|
|
|
|
|
|
|
IEnumerator IEnumerable.GetEnumerator() { |
|
|
|
return this.GetEnumerator(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public class PathMetric { |
|
|
|
// TODO
|
|
|
|
public readonly float length; |
|
|
|
} |
|
|
|
|
|
|
|
public class PathMetricIterator : IEnumerator<PathMetric> { |
|
|
|
PathMetricIterator(_PathMeasure measure) { |
|
|
|
this._pathMeasure = measure; |
|
|
|
} |
|
|
|
|
|
|
|
internal static PathMetricIterator _(_PathMeasure _pathMeasure) { |
|
|
|
D.assert(_pathMeasure != null); |
|
|
|
return new PathMetricIterator(_pathMeasure); |
|
|
|
} |
|
|
|
|
|
|
|
PathMetric _pathMetric; |
|
|
|
_PathMeasure _pathMeasure; |
|
|
|
|
|
|
|
public void Reset() { |
|
|
|
throw new NotImplementedException(); |
|
|
|
} |
|
|
|
|
|
|
|
public PathMetric Current { |
|
|
|
get { return this._pathMetric; } |
|
|
|
} |
|
|
|
|
|
|
|
object IEnumerator.Current { |
|
|
|
get { return this._pathMetric; } |
|
|
|
} |
|
|
|
|
|
|
|
public bool MoveNext() { |
|
|
|
// if (_pathMeasure._nextContour()) {
|
|
|
|
// _pathMetric = PathMetric._(_pathMeasure);
|
|
|
|
// return true;
|
|
|
|
// }
|
|
|
|
// _pathMetric = null;
|
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
public void Dispose() { |
|
|
|
throw new NotImplementedException(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
class _PathMeasure { |
|
|
|
} |
|
|
|
|
|
|
|
public enum PathWinding { |
|
|
|
counterClockwise = 1, // which just means the order as the input is.
|
|
|
|
clockwise = 2, // which just means the reversed order.
|
|
|
|
|
|
|
var closerY = Mathf.Abs(midY - startY) < Mathf.Abs(midY - endY) ? startY : endY; |
|
|
|
c1.y2 = c2.y0 = closerY; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Verify that all five points are in order.
|
|
|
|
D.assert(_between(startY, c1.y1, c1.y2)); |
|
|
|
D.assert(_between(c1.y1, c1.y2, c2.y1)); |
|
|
|
|
|
|
this.addPath(); |
|
|
|
this.addPoint(0, 0, PointFlags.corner); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
ref var path = ref this._paths.array[this._paths.length - 1]; |
|
|
|
if (path.count > 0) { |
|
|
|
ref var pt = ref this._points.array[this._points.length - 1]; |
|
|
|
|
|
|
y = point.y + y1, |
|
|
|
flags = flags, |
|
|
|
}); |
|
|
|
} else { |
|
|
|
} |
|
|
|
else { |
|
|
|
this._addPoint(new PathPoint { |
|
|
|
x = point.x + x1, |
|
|
|
y = point.y + y1, |
|
|
|
|
|
|
|
|
|
|
public void normalize() { |
|
|
|
var points = this._points; |
|
|
|
var paths = this._paths; |
|
|
|
var paths = this._paths; |
|
|
|
for (var j = 0; j < paths.length; j++) { |
|
|
|
ref var path = ref paths.array[j]; |
|
|
|
if (path.count <= 1) { |
|
|
|
|
|
|
|
|
|
|
cvertices += path.count; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this._vertices = new List<Vector3>(cvertices); |
|
|
|
for (var i = 0; i < paths.length; i++) { |
|
|
|
ref var path = ref paths.array[i]; |
|
|
|
|
|
|
this._expandFill(); |
|
|
|
|
|
|
|
var paths = this._paths; |
|
|
|
|
|
|
|
|
|
|
|
var cindices = 0; |
|
|
|
for (var i = 0; i < paths.length; i++) { |
|
|
|
ref var path = ref paths.array[i]; |
|
|
|
|
|
|
this._expandStroke(strokeWidth, lineCap, lineJoin, miterLimit); |
|
|
|
|
|
|
|
var paths = this._paths; |
|
|
|
|
|
|
|
|
|
|
|
var cindices = 0; |
|
|
|
for (var i = 0; i < paths.length; i++) { |
|
|
|
ref var path = ref paths.array[i]; |
|
|
|