private static void AddExtremeVertex(List <Double2> vertices, double halfWidth, Double2 x,
                                             double entryAngle, double exitAngle, bool startVertex)
        {
            var offset = halfWidth * Double2.FromAngle((startVertex ? exitAngle : entryAngle) + Pi_2);

            if (!startVertex)
            {
                offset = -offset;
            }

            // There are two cases to process on the start point:
            // When the angles are equal, there is no need to generate the "gap" to be filled by the joint
            if (RoughlyEquals(exitAngle, entryAngle))
            {
                vertices.Add(x + offset);
                vertices.Add(x - offset);
            }
            // When the angles are different, we generate the perpendicular bisector
            else
            {
                // The sign difference between the entry angle and the previous curve's exit angle will tell
                // where the angle bisector will need to be put
                var diff           = (exitAngle - entryAngle).WrapAngle();
                var bisectorOffset = halfWidth / Math.Cos(Math.Abs(diff) / 2) * Double2.FromAngle(entryAngle + diff / 2 + Pi_2);
                if (!startVertex)
                {
                    bisectorOffset = -bisectorOffset;
                }

                // If the difference is positive, the bisector is to the right (i.e. AFTER the first point)
                if (startVertex == (diff > 0))
                {
                    vertices.Add(x + bisectorOffset);
                    vertices.Add(x);
                    vertices.Add(x - offset);
                }
                // If it is negative, the bisector is to the left (i.e. BEFORE the point)
                else
                {
                    vertices.Add(x + offset);
                    vertices.Add(x);
                    vertices.Add(x - bisectorOffset);
                }
            }
        }
        private static void GenerateLineJoints(List <Triangle> triangles, List <CurveTriangle> curveTriangles,
                                               Curve prevCurve, Curve nextCurve, double halfWidth, StrokeLineJoin lineJoin, double miterLimit)
        {
            // First, calculate the difference between the angles to check where the joint need to be formed
            var exitAngle  = prevCurve.ExitAngle;
            var entryAngle = nextCurve.EntryAngle;
            var diff       = (exitAngle - entryAngle).WrapAngle();
            var sd         = Math.Sign(diff);

            // Skip creating the joint if the diff is small enough
            if (RoughlyZero(diff))
            {
                return;
            }

            // The common point and the offset vectors
            var p           = (prevCurve.At(1) + nextCurve.At(0)) / 2;
            var entryOffset = sd * halfWidth * Double2.FromAngle(entryAngle + Pi_2);
            var exitOffset  = sd * halfWidth * Double2.FromAngle(exitAngle + Pi_2);

            // Calculate the bisector and miter length
            var miter          = halfWidth / Math.Cos(Math.Abs(diff) / 2);
            var bisectorOffset = sd * miter * Double2.FromAngle(entryAngle + diff / 2 + Pi_2);

            // Utility function for miter and round
            Double2[] GenerateClippedTriangle(bool forRound)
            {
                var miterWidth = halfWidth * (forRound ? 1f : miterLimit);

                if (miter < miterWidth)
                {
                    return new[] { Double2.Zero, entryOffset, bisectorOffset, exitOffset }
                }
                ;
                else
                {
                    // Clip the miter
                    var p1 = entryOffset + miterWidth * (bisectorOffset - entryOffset) / miter;

                    var p2 = exitOffset + miterWidth * (bisectorOffset - exitOffset) / miter;
                    return(new[] { Double2.Zero, entryOffset, p1, p2, exitOffset });
                }
            }

            // Now, create the next triangles if necessary
            switch (lineJoin)
            {
            case StrokeLineJoin.Bevel:
                // Create the bevel triangle
                triangles.Add(new Triangle(p, p + entryOffset, p + exitOffset));
                break;

            case StrokeLineJoin.Miter:
            case StrokeLineJoin.MiterClip:
            {
                // Check the conditions for the miter (only clip if miter-clip is explicity selected)
                if (lineJoin == StrokeLineJoin.Miter && miter >= halfWidth * miterLimit)
                {
                    break;
                }

                // Generate the miter
                var polygon = GenerateClippedTriangle(false).Select(v => p + v).ToArray();
                triangles.AddRange(Triangle.MakeTriangleFan(polygon));
                break;
            }

            case StrokeLineJoin.Round:
            {
                // Generate the round triangle
                var curvePolygon = GenerateClippedTriangle(true)
                                   .Select(v => new CurveVertex(p + v, new Double4(v.X, v.Y, -v.Y, 1f))).ToArray();
                curveTriangles.AddRange(CurveVertex.MakeTriangleFan(curvePolygon));
                break;
            }

            case StrokeLineJoin.Arcs:
            {
                // Compute the curvatures of the curves
                var exitKappa  = prevCurve.ExitCurvature;
                var entryKappa = nextCurve.EntryCurvature;

                // If both of them are zero, fall back to miter
                if (RoughlyZero(exitKappa) && RoughlyZero(entryKappa))
                {
                    goto case StrokeLineJoin.MiterClip;
                }

                throw new NotImplementedException("Later i'll end it");
            }
            break;

            default: break;
            }
        }
        public static DoubleMatrix Rotate(double angle)
        {
            var rot = Double2.FromAngle(angle);

            return(new DoubleMatrix(rot.X, rot.Y, -rot.Y, rot.X, 0, 0));
        }