示例#1
0
        //port from https://github.com/mrdoob/three.js/blob/7e0a78beb9317e580d7fa4da9b5b12be051c6feb/examples/js/loaders/SVGLoader.js#L569
        public static IList <Point> Flatten(this ArcSegment segment, Point startPoint)
        {
            var start           = startPoint;
            var end             = segment.Point;
            var rx              = segment.Size.Width;
            var ry              = segment.Size.Height;
            var x_axis_rotation = segment.RotationAngle;
            var large_arc_flag  = segment.IsLargeArc ? 1 : 0;
            var sweep_flag      = (int)segment.SweepDirection;

            // Ensure radii are positive
            rx = Math.Abs(rx);
            ry = Math.Abs(ry);

            // Compute (x1′, y1′)
            var dx2 = (start.x - end.x) / 2.0;
            var dy2 = (start.y - end.y) / 2.0;
            var x1p = Math.Cos(x_axis_rotation) * dx2 + Math.Sin(x_axis_rotation) * dy2;
            var y1p = -Math.Sin(x_axis_rotation) * dx2 + Math.Cos(x_axis_rotation) * dy2;

            // Compute (cx′, cy′)
            var rxs  = rx * rx;
            var rys  = ry * ry;
            var x1ps = x1p * x1p;
            var y1ps = y1p * y1p;

            // Ensure radii are large enough
            var cr = x1ps / rxs + y1ps / rys;

            if (cr > 1)
            {
                // scale up rx,ry equally so cr == 1
                var s = Math.Sqrt(cr);
                rx  = s * rx;
                ry  = s * ry;
                rxs = rx * rx;
                rys = ry * ry;
            }

            var dq = (rxs * y1ps + rys * x1ps);
            var pq = (rxs * rys - dq) / dq;
            var q  = Math.Sqrt(Math.Max(0, pq));

            if (large_arc_flag == sweep_flag)
            {
                q = -q;
            }
            var cxp = q * rx * y1p / ry;
            var cyp = -q * ry * x1p / rx;

            // Step 3: Compute (cx, cy) from (cx′, cy′)
            var cx = Math.Cos(x_axis_rotation) * cxp - Math.Sin(x_axis_rotation) * cyp + (start.x + end.x) / 2;
            var cy = Math.Sin(x_axis_rotation) * cxp + Math.Cos(x_axis_rotation) * cyp + (start.y + end.y) / 2;

            // Step 4: Compute θ1 and Δθ
            var theta = svgAngle(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);
            var delta = svgAngle((x1p - cxp) / rx, (y1p - cyp) / ry, (-x1p - cxp) / rx, (-y1p - cyp) / ry) % (Math.PI * 2);

            var curve = new EllipseCurve(cx, cy, rx, ry, theta, theta + delta, sweep_flag == 0, x_axis_rotation);

            // Calculate number of points for polyline approximation
            var           max    = (int)(Math.Abs(delta) / MathEx.Deg2Rad(3));
            IList <Point> points = new List <Point>(max);

            for (double i = 0; i <= max; i++)
            {
                var p = curve.getPoint(i / max);
                points.Add(p);
            }
            return(points);
        }
示例#2
0
        public void ArcTo(Point point, double radius, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection, bool isStroked)
        {
            ArcSegment segment = new ArcSegment(point, new Size(radius, radius), rotationAngle, isLargeArc, sweepDirection, isStroked);

            Figure.Segments.Add(segment);
        }
示例#3
0
        public void EllipseArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection, bool isStroked)
        {
            ArcSegment segment = new ArcSegment(point, size, rotationAngle, isLargeArc, sweepDirection, isStroked);

            Figure.Segments.Add(segment);
        }
示例#4
0
        /// <summary>
        /// Flatten the ArcSegment into a List of Points
        /// </summary>
        /// <param name="segment">the arc segment</param>
        /// <param name="startPoint">start point</param>
        /// <param name="tolerance">tolerance</param>
        /// <returns>flattened point list</returns>
        /// <remarks>
        /// ref: http://www.charlespetzold.com/blog/2008/01/Mathematics-of-ArcSegment.html
        /// </remarks>
        public static IList <Point> Flatten(this ArcSegment segment, Point startPoint /*start point*/,
                                            double tolerance = 1)
        {
            var pt1                = startPoint;
            var pt2                = segment.Point;
            var radiusX            = segment.Size.Width;
            var radiusY            = segment.Size.Height;
            var angleRotation      = segment.RotationAngle;
            var isLargeArc         = segment.IsLargeArc;
            var isCounterclockwise = segment.SweepDirection == SweepDirection.Counterclockwise;

            // Adjust for different radii and rotation angle
            Matrix matx = new Matrix();

            matx.Rotate(-angleRotation);
            matx.Scale(radiusY / radiusX, 1);
            pt1 = matx.Transform(pt1);
            pt2 = matx.Transform(pt2);

            // Get info about chord that connects both points
            Point  midPoint  = new Point((pt1.X + pt2.X) / 2, (pt1.Y + pt2.Y) / 2);
            Vector vect      = pt2 - pt1;
            double halfChord = vect.Length / 2;

            // Get vector from chord to center
            Vector vectRotated;

            // (comparing two Booleans here!)
            if (isLargeArc == isCounterclockwise)
            {
                vectRotated = new Vector(-vect.Y, vect.X);
            }
            else
            {
                vectRotated = new Vector(vect.Y, -vect.X);
            }

            vectRotated.Normalize();

            // Distance from chord to center
            double centerDistance = Math.Sqrt(radiusY * radiusY - halfChord * halfChord);

            // Calculate center point
            Point center = midPoint + centerDistance * vectRotated;

            // Get angles from center to the two points
            double angle1 = Math.Atan2(pt1.Y - center.Y, pt1.X - center.X);
            double angle2 = Math.Atan2(pt2.Y - center.Y, pt2.X - center.X);

            // (another comparison of two Booleans!)
            if (isLargeArc == (Math.Abs(angle2 - angle1) < Math.PI))
            {
                if (angle1 < angle2)
                {
                    angle1 += 2 * Math.PI;
                }
                else
                {
                    angle2 += 2 * Math.PI;
                }
            }

            // Invert matrix for final point calculation
            matx.Invert();

            // Calculate number of points for polyline approximation
            int max = (int)((4 * (radiusX + radiusY) * Math.Abs(angle2 - angle1) / (2 * Math.PI)) / tolerance);

            IList <Point> points = new List <Point>(max);

            // Loop through the points
            for (int i = 0; i <= max; i++)
            {
                double angle = ((max - i) * angle1 + i * angle2) / max;
                double x     = center.X + radiusY * Math.Cos(angle);
                double y     = center.Y + radiusY * Math.Sin(angle);

                // Transform the point back
                Point pt = matx.Transform(new Point(x, y));
                points.Add(pt);
            }

            return(points);
        }