285 lines
10 KiB
C#
285 lines
10 KiB
C#
/*
|
|
The MIT License (MIT)
|
|
Copyright (c) 2014 Matt DesLauriers
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
|
OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
using UnityEngine;
|
|
|
|
namespace DA_Assets.SVGMeshUnity.Internals
|
|
{
|
|
public class BezierToVertex
|
|
{
|
|
// https://github.com/mattdesl/svg-path-contours
|
|
|
|
public float Scale = 1f;
|
|
|
|
public float PathDistanceEpsilon = 1f;
|
|
public int RecursionLimit = 8;
|
|
public float FLTEpsilon = 1.19209290e-7f;
|
|
|
|
public float AngleEpsilon = 0.01f;
|
|
public float AngleTolerance = 0f;
|
|
public float CuspLimit = 0f;
|
|
|
|
public WorkBufferPool WorkBufferPool;
|
|
|
|
private WorkBuffer<Vector2> WorkVertices;
|
|
|
|
public void GetContours(SVGData svg, MeshData data)
|
|
{
|
|
WorkBufferPool.Get(ref WorkVertices);
|
|
|
|
var pen = Vector2.zero;
|
|
|
|
var curves = svg.Curves;
|
|
var l = curves.Count;
|
|
for (var i = 0; i < l; ++i)
|
|
{
|
|
var curve = curves[i];
|
|
if (curve.IsMove)
|
|
{
|
|
EmitWorkVerticesIfNeeded(data);
|
|
}
|
|
else
|
|
{
|
|
FillBezier(pen, curve.InControl, curve.OutControl, curve.Position);
|
|
}
|
|
pen = curve.Position;
|
|
}
|
|
|
|
EmitWorkVerticesIfNeeded(data);
|
|
|
|
WorkBufferPool.Release(ref WorkVertices);
|
|
}
|
|
|
|
private void EmitWorkVerticesIfNeeded(MeshData data)
|
|
{
|
|
if (WorkVertices.UsedSize == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// TODO: Simplify
|
|
|
|
data.AddVertices(WorkVertices);
|
|
WorkVertices.Clear();
|
|
}
|
|
|
|
////// Based on:
|
|
////// https://github.com/pelson/antigrain/blob/master/agg-2.4/src/agg_curves.cpp
|
|
|
|
private void FillBezier(Vector2 start, Vector2 c1, Vector2 c2, Vector2 end)
|
|
{
|
|
var distanceTolerance = PathDistanceEpsilon / Scale;
|
|
distanceTolerance *= distanceTolerance;
|
|
BeginFillBezier(start, c1, c2, end, distanceTolerance);
|
|
}
|
|
|
|
private void BeginFillBezier(Vector2 start, Vector2 c1, Vector2 c2, Vector2 end, float distanceTolerance)
|
|
{
|
|
WorkVertices.Push(ref start);
|
|
RecursiveFillBezier(start, c1, c2, end, distanceTolerance, 0);
|
|
WorkVertices.Push(ref end);
|
|
}
|
|
|
|
private void RecursiveFillBezier(Vector2 v1, Vector2 v2, Vector2 v3, Vector2 v4, float distanceTolerance, int level)
|
|
{
|
|
if (level > RecursionLimit)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var pi = Mathf.PI;
|
|
|
|
// Calculate all the mid-points of the line segments
|
|
//----------------------
|
|
var v12 = (v1 + v2) / 2f;
|
|
var v23 = (v2 + v3) / 2f;
|
|
var v34 = (v3 + v4) / 2f;
|
|
var v123 = (v12 + v23) / 2f;
|
|
var v234 = (v23 + v34) / 2f;
|
|
var v1234 = (v123 + v234) / 2f;
|
|
|
|
// Enforce subdivision first time
|
|
if (level > 0)
|
|
{
|
|
// Try to approximate the full cubic curve by a single straight line
|
|
//------------------
|
|
var d = v4 - v1;
|
|
|
|
var d2 = Mathf.Abs((v2.x - v4.x) * d.y - (v2.y - v4.y) * d.x);
|
|
var d3 = Mathf.Abs((v3.x - v4.x) * d.y - (v3.y - v4.y) * d.x);
|
|
|
|
if (d2 > FLTEpsilon && d3 > FLTEpsilon)
|
|
{
|
|
// Regular care
|
|
//-----------------
|
|
if ((d2 + d3) * (d2 + d3) <= distanceTolerance * (d.x * d.x + d.y * d.y))
|
|
{
|
|
// If the curvature doesn't exceed the distanceTolerance value
|
|
// we tend to finish subdivisions.
|
|
//----------------------
|
|
if (AngleTolerance < AngleEpsilon)
|
|
{
|
|
WorkVertices.Push(ref v1234);
|
|
return;
|
|
}
|
|
|
|
// Angle & Cusp Condition
|
|
//----------------------
|
|
var a23 = Mathf.Atan2(v3.y - v2.y, v3.x - v2.x);
|
|
var da1 = Mathf.Abs(a23 - Mathf.Atan2(v2.y - v1.y, v2.x - v1.x));
|
|
var da2 = Mathf.Abs(Mathf.Atan2(v4.y - v3.y, v4.x - v3.x) - a23);
|
|
|
|
if (da1 >= pi)
|
|
{
|
|
da1 = 2 * pi - da1;
|
|
}
|
|
|
|
if (da2 >= pi)
|
|
{
|
|
da2 = 2 * pi - da2;
|
|
}
|
|
|
|
if (da1 + da2 < AngleTolerance)
|
|
{
|
|
// Finally we can stop the recursion
|
|
//----------------------
|
|
WorkVertices.Push(ref v1234);
|
|
return;
|
|
}
|
|
|
|
if (CuspLimit > 0f)
|
|
{
|
|
if (da1 > CuspLimit)
|
|
{
|
|
WorkVertices.Push(ref v2);
|
|
return;
|
|
}
|
|
|
|
if (da2 > CuspLimit)
|
|
{
|
|
WorkVertices.Push(ref v3);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (d2 > FLTEpsilon)
|
|
{
|
|
// p1,p3,p4 are collinear, p2 is considerable
|
|
//----------------------
|
|
if (d2 * d2 <= distanceTolerance * (d.x * d.x + d.y * d.y))
|
|
{
|
|
if (AngleTolerance < AngleEpsilon)
|
|
{
|
|
WorkVertices.Push(ref v1234);
|
|
return;
|
|
}
|
|
|
|
// Angle Condition
|
|
//----------------------
|
|
var da1 = Mathf.Abs(Mathf.Atan2(v3.y - v2.y, v3.x - v2.x) -
|
|
Mathf.Atan2(v2.y - v1.y, v2.x - v1.x));
|
|
if (da1 >= pi)
|
|
{
|
|
da1 = 2 * pi - da1;
|
|
}
|
|
|
|
if (da1 < AngleTolerance)
|
|
{
|
|
WorkVertices.Push(ref v2);
|
|
WorkVertices.Push(ref v3);
|
|
return;
|
|
}
|
|
|
|
if (CuspLimit > 0f)
|
|
{
|
|
if (da1 > CuspLimit)
|
|
{
|
|
WorkVertices.Push(ref v2);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (d3 > FLTEpsilon)
|
|
{
|
|
// p1,p2,p4 are collinear, p3 is considerable
|
|
//----------------------
|
|
if (d3 * d3 <= distanceTolerance * (d.x * d.x + d.y * d.y))
|
|
{
|
|
if (AngleTolerance < AngleEpsilon)
|
|
{
|
|
WorkVertices.Push(ref v1234);
|
|
return;
|
|
}
|
|
|
|
// Angle Condition
|
|
//----------------------
|
|
var da1 = Mathf.Abs(Mathf.Atan2(v4.y - v3.y, v4.x - v3.x) -
|
|
Mathf.Atan2(v3.y - v2.y, v3.x - v2.x));
|
|
if (da1 >= pi)
|
|
{
|
|
da1 = 2 * pi - da1;
|
|
}
|
|
|
|
if (da1 < AngleTolerance)
|
|
{
|
|
WorkVertices.Push(ref v2);
|
|
WorkVertices.Push(ref v3);
|
|
return;
|
|
}
|
|
|
|
if (CuspLimit > 0f)
|
|
{
|
|
if (da1 > CuspLimit)
|
|
{
|
|
WorkVertices.Push(ref v3);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Collinear case
|
|
//-----------------
|
|
var dx = v1234.x - (v1.x + v4.x) / 2f;
|
|
var dy = v1234.y - (v1.y + v4.y) / 2f;
|
|
if (dx * dx + dy * dy <= distanceTolerance)
|
|
{
|
|
WorkVertices.Push(ref v1234);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Continue subdivision
|
|
//----------------------
|
|
RecursiveFillBezier(v1, v12, v123, v1234, distanceTolerance, level + 1);
|
|
RecursiveFillBezier(v1234, v234, v34, v4, distanceTolerance, level + 1);
|
|
}
|
|
}
|
|
} |