Exemple #1
0
        /// <summary>
        /// de Casteljau's algorithm for exactly 3 points
        /// </summary>
        private static GlyphPoint InterpolatePoints(GlyphPoint start, GlyphPoint ctrl, GlyphPoint end, double t, double it)
        {
            var aX = it * start.X + t * ctrl.X;
            var aY = it * start.Y + t * ctrl.Y;

            var bX = it * ctrl.X + t * end.X;
            var bY = it * ctrl.Y + t * end.Y;

            return(new GlyphPoint {
                X = it * aX + t * bX,
                Y = it * aY + t * bY
            });
        }
Exemple #2
0
        private static void AddIfNonZeroDistance(List <GlyphPoint> target, GlyphPoint point)
        {
            if (target.Count < 1)
            {
                target.Add(point);
                return;
            }

            var prev    = target[target.Count - 1];
            var dx      = point.X - prev.X;
            var dy      = point.Y - prev.Y;
            var sqrDist = (dx * dx) + (dy * dy);

            if (sqrDist < 0.0001)
            {
                return;                   // too small, ignore
            }
            target.Add(point);
        }
Exemple #3
0
        /// <summary>
        /// A more refined curve breaker for larger sizes
        /// </summary>
        private static IEnumerable <GlyphPoint> InterpolateCurve(double resolution, GlyphPoint start, GlyphPoint ctrl, GlyphPoint end)
        {
            // Estimate a step size
            var dx1  = start.X - ctrl.X;
            var dy1  = start.Y - ctrl.Y;
            var dx2  = ctrl.X - end.X;
            var dy2  = ctrl.Y - end.Y;
            var dist = Math.Sqrt((dx1 * dx1) + (dy1 * dy1) + (dx2 * dx2) + (dy2 * dy2));

            if (dist <= 1)
            {
                yield return(start);

                yield break;
            }

            var minStep = resolution;     // larger = less refined curve, but faster
            var inv     = minStep / dist; // estimated step size. Refined by 'minStep' checks in the main loop

            for (double t = 0; t < 1; t += inv)
            {
                yield return(InterpolatePoints(start, ctrl, end, t, 1.0 - t));
            }
        }
Exemple #4
0
        /// <summary>
        /// Break curves into segments where needed.
        /// Any zero-length segments should be removed by this function.
        /// </summary>
        public static GlyphPoint[] NormaliseContour(IReadOnlyList <GlyphPoint> contour, bool closeForm, double resolution)
        {
            var len = contour.Count;

            if (len < 2)
            {
                return(Array.Empty <GlyphPoint>());
            }
            var final = new List <GlyphPoint>(len * 4);
            var offs  = len - 1;

            // If we get more than one 'off-curve' point in a row,
            // then we add a new virtual point between the two off-curve
            // points, and interpolate a bezier curve with 1 control point
            for (int i = 0; i <= len; i++)
            {
                var current = contour[i % len];
                var prev    = contour[(i + offs) % len];
                var next    = contour[(i + 1) % len];

                if (current == null || prev == null || next == null)
                {
                    throw new Exception($"Invalid contour at point {i}");
                }

                // if current is on-curve, just add it.
                // if current is off-curve, but next is on-curve, do a simple interpolate
                // if current AND next are off-curve, create a virtual point and interpolate

                if (current.OnCurve) // simple corner point
                {
                    AddIfNonZeroDistance(final, current);
                }
                else if (next.OnCurve && prev.OnCurve) // simple curve
                {
                    var set = InterpolateCurve(resolution, prev, current, next);
                    foreach (var point in set)
                    {
                        AddIfNonZeroDistance(final, point);
                    }
                }
                else if (prev.OnCurve) // single virtual curve forward
                {
                    var virt = new GlyphPoint
                    {
                        X = (current.X + next.X) / 2.0,
                        Y = (current.Y + next.Y) / 2.0
                    };
                    var set = InterpolateCurve(resolution, prev, current, virt);
                    foreach (var point in set)
                    {
                        AddIfNonZeroDistance(final, point);
                    }
                }
                else if (next.OnCurve) // single virtual curve behind
                {
                    var virt = new GlyphPoint
                    {
                        X = (current.X + prev.X) / 2.0,
                        Y = (current.Y + prev.Y) / 2.0
                    };
                    var set = InterpolateCurve(resolution, virt, current, next);
                    foreach (var point in set)
                    {
                        AddIfNonZeroDistance(final, point);
                    }
                }
                else // double virtual curve
                {
                    var virtPrev = new GlyphPoint
                    {
                        X = (current.X + prev.X) / 2.0,
                        Y = (current.Y + prev.Y) / 2.0
                    };
                    var virtNext = new GlyphPoint
                    {
                        X = (current.X + next.X) / 2.0,
                        Y = (current.Y + next.Y) / 2.0
                    };
                    var set = InterpolateCurve(resolution, virtPrev, current, virtNext);
                    foreach (var point in set)
                    {
                        AddIfNonZeroDistance(final, point);
                    }
                }
            }

            // Final check: if start and end points are the same, remove the end
            if (final.Count > 1)
            {
                var start   = final[0];
                var end     = final[final.Count - 1];
                var dx      = end.X - start.X;
                var dy      = end.Y - start.Y;
                var sqrDist = (dx * dx) + (dy * dy);

                if (sqrDist < 0.0001)
                {
                    final.RemoveAt(final.Count - 1);
                }
            }

            return(final.ToArray());
        }