private static void TessellateShapeLibTess(Shape vectorShape, Color color, List <Geometry> geoms, TessellationOptions tessellationOptions)
        {
            UnityEngine.Profiling.Profiler.BeginSample("LibTess");

            var tess = new Tess();

            var angle  = 45.0f * Mathf.Deg2Rad;
            var mat    = Matrix2D.Rotate(angle);
            var invMat = Matrix2D.Rotate(-angle);

            foreach (var c in vectorShape.Contours)
            {
                var contour = new List <Vector2>(100);
                foreach (var v in VectorUtils.TraceShape(c, vectorShape.PathProps.Stroke, tessellationOptions))
                {
                    contour.Add(mat.MultiplyPoint(v));
                }

                tess.AddContour(contour.Select(v => new ContourVertex()
                {
                    Position = new Vec3()
                    {
                        X = v.x, Y = v.y
                    }
                }).ToArray(), ContourOrientation.Original);
            }

            var windingRule = (vectorShape.Fill.Mode == FillMode.OddEven) ? WindingRule.EvenOdd : WindingRule.NonZero;

            try
            {
                tess.Tessellate(windingRule, ElementType.Polygons, 3);
            }
            catch (System.Exception)
            {
                Debug.LogWarning("Shape tessellation failed, skipping...");
                UnityEngine.Profiling.Profiler.EndSample();
                return;
            }

            var indices  = tess.Elements.Select(i => (UInt16)i);
            var vertices = tess.Vertices.Select(v => invMat.MultiplyPoint(new Vector2(v.Position.X, v.Position.Y)));

            if (indices.Count() > 0)
            {
                geoms.Add(new Geometry()
                {
                    Vertices = vertices.ToArray(), Indices = indices.ToArray(), Color = color, Fill = vectorShape.Fill, FillTransform = vectorShape.FillTransform
                });
            }

            UnityEngine.Profiling.Profiler.EndSample();
        }
        static void GenerateTip(BezierSegment segment, bool atStart, float t, PathEnding ending, float halfThickness, TessellationOptions tessellateOptions, List <Vector2> verts, List <UInt16> inds)
        {
            // The tip includes the vertices at the end itself
            Vector2 tan, nrm;
            var     pos        = VectorUtils.EvalFull(segment, t, out tan, out nrm);
            int     indexStart = verts.Count;

            switch (ending)
            {
            case PathEnding.Chop:
                if (atStart)
                {
                    verts.Add(pos + nrm * halfThickness);
                    verts.Add(pos - nrm * halfThickness);
                }
                else
                {
                    // Not much, path segments are always expected to be generated perpendicular to the path
                    // at the segment point location, so we don't have to do anything for the ending
                }
                break;

            case PathEnding.Square:
                if (atStart)
                {
                    verts.Add(pos + nrm * halfThickness - tan * halfThickness);
                    verts.Add(pos - nrm * halfThickness - tan * halfThickness);
                    verts.Add(pos + nrm * halfThickness);
                    verts.Add(pos - nrm * halfThickness);

                    inds.Add((UInt16)(indexStart + 0));
                    inds.Add((UInt16)(indexStart + 3));
                    inds.Add((UInt16)(indexStart + 1));
                    inds.Add((UInt16)(indexStart + 0));
                    inds.Add((UInt16)(indexStart + 2));
                    inds.Add((UInt16)(indexStart + 3));
                }
                else
                {
                    // Relying on the last two vertices, and just adding two of our own here
                    verts.Add(pos + nrm * halfThickness + tan * halfThickness);
                    verts.Add(pos - nrm * halfThickness + tan * halfThickness);

                    inds.Add((UInt16)(indexStart + 0 - 2));
                    inds.Add((UInt16)(indexStart + 3 - 2));
                    inds.Add((UInt16)(indexStart + 1 - 2));
                    inds.Add((UInt16)(indexStart + 0 - 2));
                    inds.Add((UInt16)(indexStart + 2 - 2));
                    inds.Add((UInt16)(indexStart + 3 - 2));
                }
                break;

            case PathEnding.Round:
                float arcSign     = atStart ? -1 : 1;
                int   arcSegments = CalculateArcSteps(halfThickness, 0, Mathf.PI, tessellateOptions);
                for (int i = 1; i < arcSegments; i++)
                {
                    float angle = Mathf.PI * (i / (float)arcSegments);
                    verts.Add(pos + Matrix2D.Rotate(angle) * nrm * halfThickness * arcSign);
                }

                if (atStart)
                {
                    // Note how we maintain the last two vertices being setup for connection by the rest of the path vertices
                    int indexTipStart = verts.Count;
                    verts.Add(pos + nrm * halfThickness);
                    verts.Add(pos - nrm * halfThickness);

                    for (int i = 1; i < arcSegments; i++)
                    {
                        inds.Add((UInt16)(indexTipStart + 1));
                        inds.Add((UInt16)(indexStart + i - 1));
                        inds.Add((UInt16)(indexStart + i));
                    }
                }
                else
                {
                    inds.Add((UInt16)(indexStart - 1));
                    inds.Add((UInt16)(indexStart - 2));
                    inds.Add((UInt16)(indexStart + 0));
                    for (int i = 1; i < arcSegments - 1; i++)
                    {
                        inds.Add((UInt16)(indexStart - 1));
                        inds.Add((UInt16)(indexStart + i - 1));
                        inds.Add((UInt16)(indexStart + i));
                    }
                }
                break;

            default:
                System.Diagnostics.Debug.Assert(false);     // Joining has its own function
                break;
            }
        }
        static void GenerateJoining(JoiningInfo joinInfo, PathCorner corner, float halfThickness, float tippedCornerLimit, TessellationOptions tessellateOptions, List <Vector2> verts, List <UInt16> inds)
        {
            // The joining generates the vertices at both ends as well as the joining itself
            if (verts.Count == 0)
            {
                // Starting a path with a joining (meaning a loop)
                verts.Add(joinInfo.RoundPosThickness ? joinInfo.PosThicknessEnd : joinInfo.InnerCornerVertex);
                verts.Add(joinInfo.RoundPosThickness ? joinInfo.InnerCornerVertex : joinInfo.NegThicknessEnd);
            }

            System.Diagnostics.Debug.Assert(verts.Count >= 2);
            int indexStart = verts.Count - 2; // Using the last two vertices

            // Convert a tipped corner to a beveled one if tippedCornerLimit ratio is reached
            if (corner == PathCorner.Tipped && tippedCornerLimit >= 1.0f)
            {
                var theta = Vector2.Angle(-joinInfo.TanAtEnd, joinInfo.TanAtStart) * Mathf.Deg2Rad;
                var ratio = 1.0f / Mathf.Sin(theta / 2.0f);
                if (ratio > tippedCornerLimit)
                {
                    corner = PathCorner.Beveled;
                }
            }

            if (joinInfo.SimpleJoin)
            {
                // TODO
            }
            else if (corner == PathCorner.Tipped)
            {
                verts.Add(joinInfo.PosThicknessClosingPoint);
                verts.Add(joinInfo.NegThicknessClosingPoint);
                verts.Add(joinInfo.RoundPosThickness ? joinInfo.PosThicknessStart : joinInfo.InnerCornerVertex);
                verts.Add(joinInfo.RoundPosThickness ? joinInfo.InnerCornerVertex : joinInfo.NegThicknessStart);

                // Ending to tip
                inds.Add((UInt16)(indexStart + 0));
                inds.Add((UInt16)(indexStart + 3));
                inds.Add((UInt16)(indexStart + 1));
                inds.Add((UInt16)(indexStart + 0));
                inds.Add((UInt16)(indexStart + 2));
                inds.Add((UInt16)(indexStart + 3));

                // Tip to starting
                inds.Add((UInt16)(indexStart + 4));
                inds.Add((UInt16)(indexStart + 3));
                inds.Add((UInt16)(indexStart + 2));
                inds.Add((UInt16)(indexStart + 4));
                inds.Add((UInt16)(indexStart + 5));
                inds.Add((UInt16)(indexStart + 3));

                return;
            }
            else if (corner == PathCorner.Beveled)
            {
                verts.Add(joinInfo.RoundPosThickness ? joinInfo.PosThicknessEnd : joinInfo.InnerCornerVertex);   // 2
                verts.Add(joinInfo.RoundPosThickness ? joinInfo.InnerCornerVertex : joinInfo.NegThicknessEnd);   // 3
                verts.Add(joinInfo.RoundPosThickness ? joinInfo.PosThicknessStart : joinInfo.InnerCornerVertex); // 4
                verts.Add(joinInfo.RoundPosThickness ? joinInfo.InnerCornerVertex : joinInfo.NegThicknessStart); // 5

                // Ending to tip
                inds.Add((UInt16)(indexStart + 0));
                inds.Add((UInt16)(indexStart + 2));
                inds.Add((UInt16)(indexStart + 1));
                inds.Add((UInt16)(indexStart + 1));
                inds.Add((UInt16)(indexStart + 2));
                inds.Add((UInt16)(indexStart + 3));

                // Bevel
                if (joinInfo.RoundPosThickness)
                {
                    inds.Add((UInt16)(indexStart + 2));
                    inds.Add((UInt16)(indexStart + 4));
                    inds.Add((UInt16)(indexStart + 3));
                }
                else
                {
                    inds.Add((UInt16)(indexStart + 3));
                    inds.Add((UInt16)(indexStart + 2));
                    inds.Add((UInt16)(indexStart + 5));
                }

                return;
            }

            if (corner == PathCorner.Round)
            {
                float sweepAngle = Mathf.Acos(Vector2.Dot(joinInfo.NormAtEnd, joinInfo.NormAtStart));
                bool  flipArc    = false;
                if (!PointOnTheLeftOfLine(Vector2.zero, joinInfo.NormAtEnd, joinInfo.NormAtStart))
                {
                    sweepAngle = -sweepAngle;
                    flipArc    = true;
                }

                UInt16 innerCornerVertexIndex = (UInt16)verts.Count;
                verts.Add(joinInfo.InnerCornerVertex);

                int arcSegments = CalculateArcSteps(halfThickness, 0, sweepAngle, tessellateOptions);
                for (int i = 0; i <= arcSegments; i++)
                {
                    float   angle = sweepAngle * (i / (float)arcSegments);
                    Vector2 nrm   = Matrix2D.Rotate(angle) * joinInfo.NormAtEnd;
                    if (flipArc)
                    {
                        nrm = -nrm;
                    }
                    verts.Add(nrm * halfThickness + joinInfo.JoinPos);

                    if (i == 0)
                    {
                        inds.Add((UInt16)(indexStart + 0));
                        inds.Add((UInt16)(indexStart + 3));
                        inds.Add((UInt16)(indexStart + (joinInfo.RoundPosThickness ? 2 : 1)));

                        inds.Add((UInt16)(indexStart + 0));
                        inds.Add((UInt16)(indexStart + 2));
                        inds.Add((UInt16)(indexStart + (joinInfo.RoundPosThickness ? 1 : 3)));
                    }
                    else
                    {
                        if (joinInfo.RoundPosThickness)
                        {
                            inds.Add((UInt16)(indexStart + i + (flipArc ? 3 : 2)));
                            inds.Add((UInt16)(indexStart + i + (flipArc ? 2 : 3)));
                            inds.Add(innerCornerVertexIndex);
                        }
                        else
                        {
                            inds.Add((UInt16)(indexStart + i + (flipArc ? 3 : 2)));
                            inds.Add((UInt16)(indexStart + i + (flipArc ? 2 : 3)));
                            inds.Add(innerCornerVertexIndex);
                        }
                    }
                }

                // Manually add the last segment, maintain the expected vertex positioning
                int endingVerticesIndex = verts.Count;
                if (joinInfo.RoundPosThickness)
                {
                    verts.Add(joinInfo.PosThicknessStart);
                    verts.Add(joinInfo.InnerCornerVertex);
                }
                else
                {
                    verts.Add(joinInfo.InnerCornerVertex);
                    verts.Add(joinInfo.NegThicknessStart);
                }
                inds.Add((UInt16)(endingVerticesIndex - 1));
                inds.Add((UInt16)(endingVerticesIndex + 0));
                inds.Add(innerCornerVertexIndex);
            }
        }