|
|
|
|
|
|
using System.Text; |
|
|
|
using Unity.UIWidgets.foundation; |
|
|
|
using UnityEngine; |
|
|
|
using Vector2 = UnityEngine.Vector2; |
|
|
|
using Vector3 = UnityEngine.Vector3; |
|
|
|
struct VertexUV { |
|
|
|
public List<Vector3> fillVertices; |
|
|
|
public List<Vector2> fillUV; |
|
|
|
public List<Vector3> strokeVertices; |
|
|
|
public List<Vector2> strokeUV; |
|
|
|
} |
|
|
|
|
|
|
|
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; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
this._commandx = 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; |
|
|
|
|
|
|
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)); |
|
|
|
|
|
|
readonly ArrayRef<PathPath> _paths = new ArrayRef<PathPath>(); |
|
|
|
readonly ArrayRef<PathPoint> _points = new ArrayRef<PathPoint>(); |
|
|
|
List<Vector3> _vertices = null; |
|
|
|
List<Vector2> _uv = null; |
|
|
|
List<Vector3> _strokeVertices = null; |
|
|
|
List<Vector2> _strokeUV = null; |
|
|
|
|
|
|
|
bool _fillConvex; |
|
|
|
bool _fillConvex; |
|
|
|
|
|
|
|
public MeshMesh fillMesh { |
|
|
|
get { return this._fillMesh; } |
|
|
|
} |
|
|
|
|
|
|
|
public MeshMesh strokeMesh { |
|
|
|
get { return this._strokeMesh; } |
|
|
|
} |
|
|
|
|
|
|
|
float _fringe; |
|
|
|
|
|
|
|
public PathCache(float scale) { |
|
|
|
this._scale = scale; |
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void _expandFill() { |
|
|
|
var points = this._points; |
|
|
|
var paths = this._paths; |
|
|
|
for (var j = 0; j < paths.length; j++) { |
|
|
|
ref var path = ref paths.array[j]; |
|
|
|
if (path.count <= 2) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
var ip0 = path.first + path.count - 1; |
|
|
|
var ip1 = path.first; |
|
|
|
for (var i = 0; i < path.count; i++) { |
|
|
|
ref var p0 = ref points.array[ip0]; |
|
|
|
ref var p1 = ref points.array[ip1]; |
|
|
|
p0.dx = p1.x - p0.x; // no need to normalize
|
|
|
|
p0.dy = p1.y - p0.y; |
|
|
|
ip0 = ip1++; |
|
|
|
} |
|
|
|
|
|
|
|
path.convex = true; |
|
|
|
|
|
|
|
ip0 = path.first + path.count - 1; |
|
|
|
ip1 = path.first; |
|
|
|
for (var i = 0; i < path.count; i++) { |
|
|
|
ref var p0 = ref points.array[ip0]; |
|
|
|
ref var p1 = ref points.array[ip1]; |
|
|
|
|
|
|
|
float cross = p1.dx * p0.dy - p0.dx * p1.dy; |
|
|
|
if (cross < 0.0f) { |
|
|
|
path.convex = false; |
|
|
|
} |
|
|
|
|
|
|
|
ip0 = ip1++; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var cvertices = 0; |
|
|
|
for (var i = 0; i < paths.length; i++) { |
|
|
|
ref var path = ref paths.array[i]; |
|
|
|
if (path.count <= 2) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
cvertices += path.count; |
|
|
|
} |
|
|
|
|
|
|
|
this._vertices = new List<Vector3>(cvertices); |
|
|
|
for (var i = 0; i < paths.length; i++) { |
|
|
|
ref var path = ref paths.array[i]; |
|
|
|
if (path.count <= 2) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
path.ifill = this._vertices.Count; |
|
|
|
for (var j = 0; j < path.count; j++) { |
|
|
|
ref var p = ref points.array[path.first + j]; |
|
|
|
this._vertices.Add(new Vector2(p.x, p.y)); |
|
|
|
} |
|
|
|
|
|
|
|
path.nfill = this._vertices.Count - path.ifill; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public MeshMesh getFillMesh(out bool convex) { |
|
|
|
if (this._fillMesh != null) { |
|
|
|
convex = this._fillConvex; |
|
|
|
return this._fillMesh; |
|
|
|
} |
|
|
|
|
|
|
|
this._expandFill(); |
|
|
|
|
|
|
|
var paths = this._paths; |
|
|
|
|
|
|
|
var cindices = 0; |
|
|
|
for (var i = 0; i < paths.length; i++) { |
|
|
|
ref var path = ref paths.array[i]; |
|
|
|
if (path.count <= 2) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if (path.nfill > 0) { |
|
|
|
D.assert(path.nfill >= 2); |
|
|
|
cindices += (path.nfill - 2) * 3; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var indices = new List<int>(cindices); |
|
|
|
for (var i = 0; i < paths.length; i++) { |
|
|
|
ref var path = ref paths.array[i]; |
|
|
|
if (path.count <= 2) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if (path.nfill > 0) { |
|
|
|
for (var j = 2; j < path.nfill; j++) { |
|
|
|
indices.Add(path.ifill); |
|
|
|
indices.Add(path.ifill + j); |
|
|
|
indices.Add(path.ifill + j - 1); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
D.assert(indices.Count == cindices); |
|
|
|
|
|
|
|
var mesh = new MeshMesh(null, this._vertices, indices); |
|
|
|
this._fillMesh = mesh; |
|
|
|
|
|
|
|
this._fillConvex = false; |
|
|
|
for (var i = 0; i < paths.length; i++) { |
|
|
|
ref var path = ref paths.array[i]; |
|
|
|
if (path.count <= 2) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if (this._fillConvex) { |
|
|
|
// if more than two paths, convex is false.
|
|
|
|
this._fillConvex = false; |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
if (!path.convex) { |
|
|
|
// if not convex, convex is false.
|
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
this._fillConvex = true; |
|
|
|
} |
|
|
|
|
|
|
|
convex = this._fillConvex; |
|
|
|
return this._fillMesh; |
|
|
|
} |
|
|
|
|
|
|
|
void _calculateJoins(float w, StrokeJoin lineJoin, float miterLimit) { |
|
|
|
float iw = w > 0.0f ? 1.0f / w : 0.0f; |
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void _expandStroke(float w, StrokeCap lineCap, StrokeJoin lineJoin, float miterLimit) { |
|
|
|
this._calculateJoins(w, lineJoin, miterLimit); |
|
|
|
|
|
|
|
VertexUV _expandStroke(float w, float fringe, StrokeCap lineCap, StrokeJoin lineJoin, float miterLimit) { |
|
|
|
float aa = fringe; |
|
|
|
float u0 = 0.0f, u1 = 1.0f; |
|
|
|
ncap = PathUtils.curveDivs(w, Mathf.PI, this._tessTol); |
|
|
|
ncap = uiPathUtils.curveDivs(w, Mathf.PI, this._tessTol); |
|
|
|
} |
|
|
|
|
|
|
|
w += aa * 0.5f; |
|
|
|
|
|
|
|
if (aa == 0.0f) { |
|
|
|
u0 = 0.5f; |
|
|
|
u1 = 0.5f; |
|
|
|
this._calculateJoins(w, lineJoin, miterLimit); |
|
|
|
|
|
|
|
ref var path = ref paths.array[i]; |
|
|
|
var path = paths.array[i]; |
|
|
|
cvertices += 4; |
|
|
|
cvertices += 8; |
|
|
|
this._uv = new List<Vector2>(cvertices); |
|
|
|
ref var path = ref paths.array[i]; |
|
|
|
var path = paths.array[i]; |
|
|
|
if (path.count <= 1) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
e = path.count - 1; |
|
|
|
} |
|
|
|
|
|
|
|
ref var p0 = ref points.array[ip0]; |
|
|
|
ref var p1 = ref points.array[ip1]; |
|
|
|
var p0 = points.array[ip0]; |
|
|
|
var p1 = points.array[ip1]; |
|
|
|
this._vertices.buttCapStart(p0, p0.dx, p0.dy, w, 0.0f); |
|
|
|
this._vertices.buttCapStart(this._uv, p0, p0.dx, p0.dy, w, 0.0f, aa, u0, u1); |
|
|
|
this._vertices.buttCapStart(p0, p0.dx, p0.dy, w, w); |
|
|
|
this._vertices.buttCapStart(this._uv, p0, p0.dx, p0.dy, w, w, aa, u0, u1); |
|
|
|
this._vertices.roundCapStart(p0, p0.dx, p0.dy, w, ncap); |
|
|
|
this._vertices.roundCapStart(this._uv, p0, p0.dx, p0.dy, w, ncap, u0, u1); |
|
|
|
p0 = ref points.array[ip0]; |
|
|
|
p1 = ref points.array[ip1]; |
|
|
|
p0 = points.array[ip0]; |
|
|
|
p1 = points.array[ip1]; |
|
|
|
this._vertices.roundJoin(p0, p1, w, w, ncap); |
|
|
|
this._vertices.roundJoin(this._uv, p0, p1, w, w, ncap, u0, u1, aa); |
|
|
|
this._vertices.bevelJoin(p0, p1, w, w); |
|
|
|
this._vertices.bevelJoin(this._uv, p0, p1, w, w, u0, u1, aa); |
|
|
|
this._uv.Add(new Vector2(u0, 1)); |
|
|
|
this._uv.Add(new Vector2(u1, 1)); |
|
|
|
} |
|
|
|
|
|
|
|
ip0 = ip1++; |
|
|
|
|
|
|
p0 = ref points.array[ip0]; |
|
|
|
p1 = ref points.array[ip1]; |
|
|
|
p0 = points.array[ip0]; |
|
|
|
p1 = points.array[ip1]; |
|
|
|
this._vertices.buttCapEnd(p1, p0.dx, p0.dy, w, 0.0f); |
|
|
|
this._vertices.buttCapEnd(this._uv, p1, p0.dx, p0.dy, w, 0.0f, aa, u0, u1); |
|
|
|
this._vertices.buttCapEnd(p1, p0.dx, p0.dy, w, w); |
|
|
|
this._vertices.buttCapEnd(this._uv, p1, p0.dx, p0.dy, w, w, aa, u0, u1); |
|
|
|
this._vertices.roundCapEnd(p1, p0.dx, p0.dy, w, ncap); |
|
|
|
this._vertices.roundCapEnd(this._uv, p1, p0.dx, p0.dy, w, ncap, u0, u1); |
|
|
|
this._uv.Add(new Vector2(u0, 1)); |
|
|
|
this._uv.Add(new Vector2(u1, 1)); |
|
|
|
paths.array[i] = path; |
|
|
|
|
|
|
|
D.assert(this._uv.Count == this._vertices.Count); |
|
|
|
|
|
|
|
return new VertexUV { |
|
|
|
strokeVertices = this._vertices, |
|
|
|
strokeUV = this._uv, |
|
|
|
}; |
|
|
|
public MeshMesh getStrokeMesh(float strokeWidth, StrokeCap lineCap, StrokeJoin lineJoin, float miterLimit) { |
|
|
|
VertexUV _expandFill(float fringe) { |
|
|
|
float aa = fringe; |
|
|
|
float woff = aa * 0.5f; |
|
|
|
var points = this._points; |
|
|
|
var paths = this._paths; |
|
|
|
this._calculateJoins(fringe, StrokeJoin.miter, 4.0f); |
|
|
|
|
|
|
|
var cvertices = 0; |
|
|
|
for (var i = 0; i < paths.length; i++) { |
|
|
|
var path = paths.array[i]; |
|
|
|
if (path.count <= 2) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
cvertices += path.count; |
|
|
|
} |
|
|
|
|
|
|
|
this._fillConvex = false; |
|
|
|
for (var i = 0; i < paths.length; i++) { |
|
|
|
var path = paths.array[i]; |
|
|
|
if (path.count <= 2) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if (this._fillConvex) { |
|
|
|
// if more than two paths, convex is false.
|
|
|
|
this._fillConvex = false; |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
if (!path.convex) { |
|
|
|
// if not convex, convex is false.
|
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
this._fillConvex = true; |
|
|
|
} |
|
|
|
|
|
|
|
this._vertices = new List<Vector3>(cvertices); |
|
|
|
this._uv = new List<Vector2>(cvertices); |
|
|
|
for (var i = 0; i < paths.length; i++) { |
|
|
|
var path = paths.array[i]; |
|
|
|
if (path.count <= 2) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
path.ifill = this._vertices.Count; |
|
|
|
for (var j = 0; j < path.count; j++) { |
|
|
|
var p = points.array[path.first + j]; |
|
|
|
if (aa > 0.0f) { |
|
|
|
this._vertices.Add(new Vector2(p.x + p.dmx * woff, p.y + p.dmy * woff)); |
|
|
|
} |
|
|
|
else { |
|
|
|
this._vertices.Add(new Vector2(p.x, p.y)); |
|
|
|
} |
|
|
|
|
|
|
|
this._uv.Add(new Vector2(0.5f, 1.0f)); |
|
|
|
} |
|
|
|
|
|
|
|
path.nfill = this._vertices.Count - path.ifill; |
|
|
|
paths.array[i] = path; |
|
|
|
} |
|
|
|
|
|
|
|
if (aa > 0.0f) { |
|
|
|
this._strokeVertices = new List<Vector3>(); |
|
|
|
this._strokeUV = new List<Vector2>(); |
|
|
|
cvertices = 0; |
|
|
|
for (var i = 0; i < paths.length; i++) { |
|
|
|
var path = paths.array[i]; |
|
|
|
if (path.count <= 2) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
cvertices += path.count * 2; |
|
|
|
} |
|
|
|
|
|
|
|
this._strokeVertices.Capacity = cvertices; |
|
|
|
this._strokeUV.Capacity = cvertices; |
|
|
|
|
|
|
|
float lw = this._fillConvex ? woff : aa + woff; |
|
|
|
float rw = aa - woff; |
|
|
|
float lu = this._fillConvex ? 0.5f : 0.0f; |
|
|
|
float ru = 1.0f; |
|
|
|
|
|
|
|
for (var i = 0; i < paths.length; i++) { |
|
|
|
var path = paths.array[i]; |
|
|
|
if (path.count <= 2) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
path.istroke = this._strokeVertices.Count; |
|
|
|
for (var j = 0; j < path.count; j++) { |
|
|
|
var p = points.array[path.first + j]; |
|
|
|
this._strokeVertices.Add(new Vector2(p.x + p.dmx * lw, p.y + p.dmy * lw)); |
|
|
|
this._strokeUV.Add(new Vector2(lu, 1.0f)); |
|
|
|
this._strokeVertices.Add(new Vector2(p.x - p.dmx * rw, p.y - p.dmy * rw)); |
|
|
|
this._strokeUV.Add(new Vector2(ru, 1.0f)); |
|
|
|
} |
|
|
|
|
|
|
|
path.nstroke = this._strokeVertices.Count - path.istroke; |
|
|
|
paths.array[i] = path; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return new VertexUV { |
|
|
|
fillVertices = this._vertices, |
|
|
|
fillUV = this._uv, |
|
|
|
strokeVertices = this._strokeVertices, |
|
|
|
strokeUV = this._strokeUV, |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
public void computeStrokeMesh(float strokeWidth, float fringe, StrokeCap lineCap, StrokeJoin lineJoin, |
|
|
|
float miterLimit) { |
|
|
|
this._fillMesh == null && // Ensure that the cached stroke mesh was not calculated in computeFillMesh
|
|
|
|
this._fringe == fringe && |
|
|
|
return this._strokeMesh; |
|
|
|
return; |
|
|
|
this._expandStroke(strokeWidth, lineCap, lineJoin, miterLimit); |
|
|
|
var verticesUV = this._expandStroke(strokeWidth, fringe, lineCap, lineJoin, miterLimit); |
|
|
|
|
|
|
|
|
|
|
|
ref var path = ref paths.array[i]; |
|
|
|
var path = paths.array[i]; |
|
|
|
if (path.count <= 1) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
var indices = new List<int>(cindices); |
|
|
|
for (var i = 0; i < paths.length; i++) { |
|
|
|
ref var path = ref paths.array[i]; |
|
|
|
var path = paths.array[i]; |
|
|
|
if (path.count <= 1) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
D.assert(indices.Count == cindices); |
|
|
|
|
|
|
|
this._strokeMesh = new MeshMesh(null, this._vertices, indices); |
|
|
|
this._strokeMesh = new MeshMesh(null, verticesUV.strokeVertices, indices, verticesUV.strokeUV); |
|
|
|
this._fillMesh = null; |
|
|
|
this._fringe = fringe; |
|
|
|
return this._strokeMesh; |
|
|
|
} |
|
|
|
|
|
|
|
public void computeFillMesh(float fringe, out bool convex) { |
|
|
|
if (this._fillMesh != null && (fringe != 0.0f || this._strokeMesh != null) && this._fringe == fringe) { |
|
|
|
convex = this._fillConvex; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
var verticesUV = this._expandFill(fringe); |
|
|
|
convex = this._fillConvex; |
|
|
|
|
|
|
|
var paths = this._paths; |
|
|
|
|
|
|
|
var cindices = 0; |
|
|
|
for (var i = 0; i < paths.length; i++) { |
|
|
|
var path = paths.array[i]; |
|
|
|
if (path.count <= 2) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if (path.nfill > 0) { |
|
|
|
D.assert(path.nfill >= 2); |
|
|
|
cindices += (path.nfill - 2) * 3; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var indices = new List<int>(cindices); |
|
|
|
for (var i = 0; i < paths.length; i++) { |
|
|
|
var path = paths.array[i]; |
|
|
|
if (path.count <= 2) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if (path.nfill > 0) { |
|
|
|
for (var j = 2; j < path.nfill; j++) { |
|
|
|
indices.Add(path.ifill); |
|
|
|
indices.Add(path.ifill + j); |
|
|
|
indices.Add(path.ifill + j - 1); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
D.assert(indices.Count == cindices); |
|
|
|
|
|
|
|
if (verticesUV.strokeVertices != null) { |
|
|
|
cindices = 0; |
|
|
|
for (var i = 0; i < paths.length; i++) { |
|
|
|
var path = paths.array[i]; |
|
|
|
if (path.count <= 2) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if (path.nstroke > 0) { |
|
|
|
D.assert(path.nstroke >= 6); |
|
|
|
cindices += path.nstroke * 3; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var strokeIndices = new List<int>(cindices); |
|
|
|
for (var i = 0; i < paths.length; i++) { |
|
|
|
var path = paths.array[i]; |
|
|
|
if (path.count <= 2) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if (path.nstroke > 0) { |
|
|
|
strokeIndices.Add(path.istroke + path.nstroke - 1); |
|
|
|
strokeIndices.Add(path.istroke + path.nstroke - 2); |
|
|
|
strokeIndices.Add(path.istroke); |
|
|
|
strokeIndices.Add(path.istroke + path.nstroke - 1); |
|
|
|
strokeIndices.Add(path.istroke); |
|
|
|
strokeIndices.Add(path.istroke + 1); |
|
|
|
for (var j = 2; j < path.nstroke; j++) { |
|
|
|
if ((j & 1) == 0) { |
|
|
|
strokeIndices.Add(path.istroke + j - 1); |
|
|
|
strokeIndices.Add(path.istroke + j - 2); |
|
|
|
strokeIndices.Add(path.istroke + j); |
|
|
|
} |
|
|
|
else { |
|
|
|
strokeIndices.Add(path.istroke + j - 2); |
|
|
|
strokeIndices.Add(path.istroke + j - 1); |
|
|
|
strokeIndices.Add(path.istroke + j); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
D.assert(strokeIndices.Count == cindices); |
|
|
|
|
|
|
|
this._strokeMesh = new MeshMesh(null, verticesUV.strokeVertices, strokeIndices, verticesUV.strokeUV); |
|
|
|
} |
|
|
|
|
|
|
|
var mesh = new MeshMesh(null, verticesUV.fillVertices, indices, verticesUV.fillUV); |
|
|
|
this._fillMesh = mesh; |
|
|
|
this._fringe = fringe; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
return d; |
|
|
|
} |
|
|
|
|
|
|
|
public static void buttCapStart(this List<Vector3> dst, PathPoint p, |
|
|
|
float dx, float dy, float w, float d) { |
|
|
|
public static void buttCapStart(this List<Vector3> dst, List<Vector2> uv, PathPoint p, |
|
|
|
float dx, float dy, float w, float d, float aa, float u0, float u1) { |
|
|
|
dst.Add(new Vector2(px + dlx * w - dx * aa, py + dly * w - dy * aa)); |
|
|
|
dst.Add(new Vector2(px - dlx * w - dx * aa, py - dly * w - dy * aa)); |
|
|
|
uv.Add(new Vector2(u0, 0)); |
|
|
|
uv.Add(new Vector2(u1, 0)); |
|
|
|
uv.Add(new Vector2(u0, 1)); |
|
|
|
uv.Add(new Vector2(u1, 1)); |
|
|
|
public static void buttCapEnd(this List<Vector3> dst, PathPoint p, |
|
|
|
float dx, float dy, float w, float d) { |
|
|
|
public static void buttCapEnd(this List<Vector3> dst, List<Vector2> uv, PathPoint p, |
|
|
|
float dx, float dy, float w, float d, float aa, float u0, float u1) { |
|
|
|
float px = p.x + dx * d; |
|
|
|
float py = p.y + dy * d; |
|
|
|
float dlx = dy; |
|
|
|
|
|
|
dst.Add(new Vector2(px - dlx * w, py - dly * w)); |
|
|
|
dst.Add(new Vector2(px + dlx * w + dx * aa, py + dly * w + dy * aa)); |
|
|
|
dst.Add(new Vector2(px - dlx * w + dx * aa, py - dly * w + dy * aa)); |
|
|
|
uv.Add(new Vector2(u0, 1)); |
|
|
|
uv.Add(new Vector2(u1, 1)); |
|
|
|
uv.Add(new Vector2(u0, 0)); |
|
|
|
uv.Add(new Vector2(u1, 0)); |
|
|
|
public static void roundCapStart(this List<Vector3> dst, PathPoint p, |
|
|
|
float dx, float dy, float w, int ncap) { |
|
|
|
public static void roundCapStart(this List<Vector3> dst, List<Vector2> uv, PathPoint p, |
|
|
|
float dx, float dy, float w, int ncap, float u0, float u1) { |
|
|
|
float px = p.x; |
|
|
|
float py = p.y; |
|
|
|
float dlx = dy; |
|
|
|
|
|
|
float ax = Mathf.Cos(a) * w, ay = Mathf.Sin(a) * w; |
|
|
|
dst.Add(new Vector2(px - dlx * ax - dx * ay, py - dly * ax - dy * ay)); |
|
|
|
dst.Add(new Vector2(px, py)); |
|
|
|
uv.Add(new Vector2(u0, 1)); |
|
|
|
uv.Add(new Vector2(0.5f, 1)); |
|
|
|
uv.Add(new Vector2(u0, 1)); |
|
|
|
uv.Add(new Vector2(u1, 1)); |
|
|
|
public static void roundCapEnd(this List<Vector3> dst, PathPoint p, |
|
|
|
float dx, float dy, float w, int ncap) { |
|
|
|
public static void roundCapEnd(this List<Vector3> dst, List<Vector2> uv, PathPoint p, |
|
|
|
float dx, float dy, float w, int ncap, float u0, float u1) { |
|
|
|
float px = p.x; |
|
|
|
float py = p.y; |
|
|
|
float dlx = dy; |
|
|
|
|
|
|
dst.Add(new Vector2(px - dlx * w, py - dly * w)); |
|
|
|
uv.Add(new Vector2(u0, 1)); |
|
|
|
uv.Add(new Vector2(u1, 1)); |
|
|
|
|
|
|
|
for (var i = 0; i < ncap; i++) { |
|
|
|
float a = (float) i / (ncap - 1) * Mathf.PI; |
|
|
|
|
|
|
uv.Add(new Vector2(0.5f, 1)); |
|
|
|
uv.Add(new Vector2(u0, 1)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
return Mathf.Max(2, Mathf.CeilToInt(arc / da)); |
|
|
|
} |
|
|
|
|
|
|
|
public static void roundJoin(this List<Vector3> dst, PathPoint p0, PathPoint p1, |
|
|
|
float lw, float rw, int ncap) { |
|
|
|
public static void roundJoin(this List<Vector3> dst, List<Vector2> uv, PathPoint p0, PathPoint p1, |
|
|
|
float lw, float rw, int ncap, float lu, float ru, float fringe) { |
|
|
|
float dlx0 = p0.dy; |
|
|
|
float dly0 = -p0.dx; |
|
|
|
float dlx1 = p1.dy; |
|
|
|
|
|
|
|
|
|
|
dst.Add(new Vector2(lx0, ly0)); |
|
|
|
dst.Add(new Vector2(p1.x - dlx0 * rw, p1.y - dly0 * rw)); |
|
|
|
uv.Add(new Vector2(lu, 1)); |
|
|
|
uv.Add(new Vector2(ru, 1)); |
|
|
|
|
|
|
|
var n = Mathf.CeilToInt((a0 - a1) / Mathf.PI * ncap).clamp(2, ncap); |
|
|
|
for (var i = 0; i < n; i++) { |
|
|
|
|
|
|
|
|
|
|
dst.Add(new Vector2(p1.x, p1.y)); |
|
|
|
dst.Add(new Vector2(rx, ry)); |
|
|
|
uv.Add(new Vector2(0.5f, 1)); |
|
|
|
uv.Add(new Vector2(ru, 1)); |
|
|
|
uv.Add(new Vector2(lu, 1)); |
|
|
|
uv.Add(new Vector2(ru, 1)); |
|
|
|
} |
|
|
|
else { |
|
|
|
float rx0, ry0, rx1, ry1; |
|
|
|
|
|
|
|
|
|
|
dst.Add(new Vector2(p1.x + dlx0 * lw, p1.y + dly0 * lw)); |
|
|
|
dst.Add(new Vector2(rx0, ry0)); |
|
|
|
uv.Add(new Vector2(lu, 1)); |
|
|
|
uv.Add(new Vector2(ru, 1)); |
|
|
|
|
|
|
|
var n = Mathf.CeilToInt((a1 - a0) / Mathf.PI * ncap).clamp(2, ncap); |
|
|
|
for (var i = 0; i < n; i++) { |
|
|
|
|
|
|
|
|
|
|
dst.Add(new Vector2(lx, ly)); |
|
|
|
dst.Add(new Vector2(p1.x, p1.y)); |
|
|
|
uv.Add(new Vector2(lu, 1)); |
|
|
|
uv.Add(new Vector2(0.5f, 1)); |
|
|
|
uv.Add(new Vector2(lu, 1)); |
|
|
|
uv.Add(new Vector2(ru, 1)); |
|
|
|
public static void bevelJoin(this List<Vector3> dst, PathPoint p0, PathPoint p1, |
|
|
|
float lw, float rw) { |
|
|
|
public static void bevelJoin(this List<Vector3> dst, List<Vector2> uv, PathPoint p0, PathPoint p1, |
|
|
|
float lw, float rw, float lu, float ru, float fringe) { |
|
|
|
float rx0, ry0, rx1, ry1; |
|
|
|
float lx0, ly0, lx1, ly1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dst.Add(new Vector2 {x = lx0, y = ly0}); |
|
|
|
dst.Add(new Vector2 {x = p1.x - dlx0 * rw, y = p1.y - dly0 * rw}); |
|
|
|
uv.Add(new Vector2(lu, 1)); |
|
|
|
uv.Add(new Vector2(ru, 1)); |
|
|
|
|
|
|
|
if ((p1.flags & PointFlags.bevel) != 0) { |
|
|
|
dst.Add(new Vector2(lx0, ly0)); |
|
|
|
|
|
|
uv.Add(new Vector2(lu, 1)); |
|
|
|
uv.Add(new Vector2(ru, 1)); |
|
|
|
uv.Add(new Vector2(lu, 1)); |
|
|
|
uv.Add(new Vector2(ru, 1)); |
|
|
|
} |
|
|
|
else { |
|
|
|
rx0 = p1.x - p1.dmx * rw; |
|
|
|
|
|
|
dst.Add(new Vector2(rx0, ry0)); |
|
|
|
dst.Add(new Vector2(p1.x, p1.y)); |
|
|
|
dst.Add(new Vector2(p1.x - dlx1 * rw, p1.y - dly1 * rw)); |
|
|
|
uv.Add(new Vector2(0.5f, 1)); |
|
|
|
uv.Add(new Vector2(ru, 1)); |
|
|
|
uv.Add(new Vector2(ru, 1)); |
|
|
|
uv.Add(new Vector2(ru, 1)); |
|
|
|
uv.Add(new Vector2(0.5f, 1)); |
|
|
|
uv.Add(new Vector2(ru, 1)); |
|
|
|
uv.Add(new Vector2(lu, 1)); |
|
|
|
uv.Add(new Vector2(ru, 1)); |
|
|
|
} |
|
|
|
else { |
|
|
|
chooseBevel((p1.flags & PointFlags.innerBevel) != 0, p0, p1, -rw, |
|
|
|
|
|
|
dst.Add(new Vector2(rx0, ry0)); |
|
|
|
uv.Add(new Vector2(lu, 1)); |
|
|
|
uv.Add(new Vector2(ru, 1)); |
|
|
|
|
|
|
|
if ((p1.flags & PointFlags.bevel) != 0) { |
|
|
|
dst.Add(new Vector2(p1.x + dlx0 * lw, p1.y + dly0 * lw)); |
|
|
|
|
|
|
uv.Add(new Vector2(lu, 1)); |
|
|
|
uv.Add(new Vector2(ru, 1)); |
|
|
|
uv.Add(new Vector2(lu, 1)); |
|
|
|
uv.Add(new Vector2(ru, 1)); |
|
|
|
} |
|
|
|
else { |
|
|
|
lx0 = p1.x + p1.dmx * lw; |
|
|
|
|
|
|
dst.Add(new Vector2(lx0, ly0)); |
|
|
|
dst.Add(new Vector2(p1.x + dlx1 * lw, p1.y + dly1 * lw)); |
|
|
|
dst.Add(new Vector2(p1.x, p1.y)); |
|
|
|
uv.Add(new Vector2(lu, 1)); |
|
|
|
uv.Add(new Vector2(0.5f, 1)); |
|
|
|
uv.Add(new Vector2(lu, 1)); |
|
|
|
uv.Add(new Vector2(lu, 1)); |
|
|
|
uv.Add(new Vector2(lu, 1)); |
|
|
|
uv.Add(new Vector2(0.5f, 1)); |
|
|
|
uv.Add(new Vector2(lu, 1)); |
|
|
|
uv.Add(new Vector2(ru, 1)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|