Beispiel #1
0
        protected override Path ParsePath(Dictionary <string, string> properties)
        {
            // Get the attributes of the circle
            var cx = DoubleUtils.TryParse(properties.GetOrDefault("cx")) ?? 0;
            var cy = DoubleUtils.TryParse(properties.GetOrDefault("cy")) ?? 0;
            var r  = DoubleUtils.TryParse(properties.GetOrDefault("r")) ?? 0;

            // Quit on invalid values
            if (r <= 0)
            {
                return(new Path());
            }

            var rr = new Double2(r, r);

            // Mount the path commands
            return(new Path(new[]
            {
                PathCommand.MoveTo(new Double2(cx + r, cy)),
                PathCommand.ArcTo(rr, 0, false, true, new Double2(cx, cy + r)),
                PathCommand.ArcTo(rr, 0, false, true, new Double2(cx - r, cy)),
                PathCommand.ArcTo(rr, 0, false, true, new Double2(cx, cy - r)),
                PathCommand.ArcToClose(rr, 0, false, true)
            }));
        }
Beispiel #2
0
        public virtual void AddVertex(Point aPoint, PathCommand aCommand, bool closeFigure)
        {
            if (closeFigure)
                aCommand |= PathCommand.CloseFigure;

            fVertexList.Add(aPoint);
            fCommandList.Add((byte)aCommand);
        }
 private void SetPath(PathCommand pathCommand, double x, double y, double cx, double cy, double cx1, double cy1)
 {
     PathCommand = pathCommand;
     X           = x * Scale;
     Y           = y * Scale;
     Cx          = cx * Scale;
     Cy          = cy * Scale;
     Cx1         = cx1 * Scale;
     Cy1         = cy1 * Scale;
 }
Beispiel #4
0
        private Path AddCommand(PathCommand command)
        {
            _commands.Enqueue(command);

            SetX(GetAllPoints.Min(x => x.X));
            SetY(GetAllPoints.Min(x => x.Y));
            SetWidth(GetAllPoints.Max(x => x.X) - (Width ?? 0));
            SetHeight(GetAllPoints.Max(x => x.Y) - (Height ?? 0));

            return(this);
        }
Beispiel #5
0
        public PathData(string data)
        {
            var cmds = CommandSplit.Split(data);

            // i = 1 to avoid the empty first value
            for (int i = 1; i < cmds.Length; i++)
            {
                this.commands.Add(PathCommand.Create(cmds[i]));
            }

            Calculate();
        }
Beispiel #6
0
            public static PathSegment FromPathCommand(PathCommand command)
            {
                var points = new List <PointF>();
                var i      = 0;

                while (i < command.Values.Count)
                {
                    points.Add(new PointF(command.Values[i], command.Values[i + 1]));
                    i += 2;
                }

                return(new PathSegment(command.Command, points));
            }
Beispiel #7
0
 public void CommandCreationTest(string commandString, Type expectedException)
 {
     try
     {
         PathCommand.Create(commandString);
     }
     catch (Exception ex)
     {
         if (!expectedException.Equals(ex?.GetType()))
         {
             throw;
         }
     }
 }
        protected override Path ParsePath(Dictionary <string, string> properties)
        {
            // Get the attributes of the line
            var x1 = DoubleUtils.TryParse(properties.GetOrDefault("x1")) ?? 0;
            var y1 = DoubleUtils.TryParse(properties.GetOrDefault("y1")) ?? 0;

            var x2 = DoubleUtils.TryParse(properties.GetOrDefault("x2")) ?? 0;
            var y2 = DoubleUtils.TryParse(properties.GetOrDefault("y2")) ?? 0;

            // And finally return
            return(new Path(new[]
            {
                PathCommand.MoveTo(new Double2(x1, y1)),
                PathCommand.LineTo(new Double2(x2, y2))
            }));
        }
        public PathCommand PathCommandFrom()
        {
            switch (Type)
            {
            case CurveType.Line: return(PathCommand.LineTo(B));

            case CurveType.QuadraticBezier: return(PathCommand.QuadraticCurveTo(B, C));

            case CurveType.CubicBezier: return(PathCommand.CubicCurveTo(B, C, D));

            case CurveType.EllipticArc:
                return(PathCommand.ArcTo(Radii, ComplexRot.Angle, Math.Abs(DeltaAngle) > DoubleUtils.Pi, DeltaAngle > 0, At(1)));

            default: return(PathCommand.None);
            }
        }
        protected override Path ParsePath(Dictionary <string, string> properties)
        {
            // Get the attributes of the ellipse
            var cx = DoubleUtils.TryParse(properties.GetOrDefault("cx")) ?? 0;
            var cy = DoubleUtils.TryParse(properties.GetOrDefault("cy")) ?? 0;

            var radii = new Double2();

            radii.X = DoubleUtils.TryParse(properties.GetOrDefault("rx")) ?? double.NaN;
            radii.Y = DoubleUtils.TryParse(properties.GetOrDefault("ry")) ?? double.NaN;

            // Check both radii
            if (double.IsNaN(radii.X) && double.IsNaN(radii.Y))
            {
                radii.X = radii.Y = 0;
            }
            else if (double.IsNaN(radii.X))
            {
                radii.X = radii.Y;
            }
            else if (double.IsNaN(radii.Y))
            {
                radii.Y = radii.X;
            }

            // Take out the sign and clamp them
            radii.X = Math.Abs(radii.X);
            radii.Y = Math.Abs(radii.Y);

            // Quit on invalid values
            if (radii.X <= 0 || radii.Y <= 0)
            {
                return(new Path());
            }

            // Mount the path commands
            return(new Path(new[]
            {
                PathCommand.MoveTo(new Double2(cx + radii.X, cy)),
                PathCommand.ArcTo(radii, 0, false, true, new Double2(cx, cy + radii.Y)),
                PathCommand.ArcTo(radii, 0, false, true, new Double2(cx - radii.X, cy)),
                PathCommand.ArcTo(radii, 0, false, true, new Double2(cx, cy - radii.Y)),
                PathCommand.ArcToClose(radii, 0, false, true)
            }));
        }
Beispiel #11
0
        public void WriteContent(XmlElement xmlElement, SvgWriter svgWriter)
        {
            _background.ID   = ID + "_background";
            _background.Path = Path;
            _background.Fill = Fill;

            _border.ID          = ID + "_border";
            _border.Path        = Path;
            _border.Stroke      = Stroke;
            _border.StrokeWidth = StrokeWidth;
            _border.Fill        = System.Drawing.Color.Transparent;

            if (Content != null)
            {
                var doc      = xmlElement.OwnerDocument;
                var clipPath = doc.CreateElement("clipPath", XmlNamespace.Svg);
                var id       = "clip" + Guid.NewGuid().ToString("N");
                clipPath.SetAttribute("id", id);
                xmlElement.AppendChild(clipPath);

                clipPath = doc.CreateElement("path", XmlNamespace.Svg);
                xmlElement.LastChild.AppendChild(clipPath);
                clipPath.SetAttribute("d", PathCommand.ToSvgString(Path));

                xmlElement.SetAttribute("clip-path", string.Format("url(#{0})", id));
            }

            var xml = svgWriter.CreateXmlElementFromSvg(_background);

            xmlElement.AppendChild(xml);
            svgWriter.Write(_background, xml);

            if (Content != null)
            {
                xml = svgWriter.CreateXmlElementFromSvg(Content);
                xmlElement.AppendChild(xml);
                svgWriter.Write(Content, xml);
            }

            xml = svgWriter.CreateXmlElementFromSvg(_border);
            xmlElement.AppendChild(xml);
            svgWriter.Write(_border, xml);
        }
Beispiel #12
0
            public static void Add()
            {
                string fullPath = RuntimeInfo.ExeLocation;

                if (!RuntimeInfo.IsExeInAppFolder)
                {
                    bool v = CliOutput.ReadConfirm("Could not find exe in system path. Add now?");

                    if (v)
                    {
                        PathCommand.Add();
                        return;
                    }

                    if (fullPath == null)
                    {
                        throw new ApplicationException();
                    }
                }


                // Add command
                string[] commandCode =
                {
                    "@echo off",
                    String.Format("reg.exe add {0} /ve /d \"{1} \"\"%%1\"\"\" /f >nul",RuntimeInfo.REG_SHELL_CMD,
                                  fullPath)
                };

                Cli.CreateRunBatchFile("add_to_menu.bat", commandCode);


                // Add icon
                string[] iconCode =
                {
                    "@echo off",
                    String.Format("reg.exe add {0} /v Icon /d \"{1}\" /f >nul",RuntimeInfo.REG_SHELL,  fullPath),
                };

                Cli.CreateRunBatchFile("add_icon_to_menu.bat", iconCode);
            }
Beispiel #13
0
        internal static Path FromSvgString(string svg)
        {
            Path path;

            if (!string.IsNullOrEmpty(svg))
            {
                try
                {
                    var commands = PathCommand.ParseCommands(svg);
                    path = FromPathCommands(commands);
                }
                catch (Exception)
                {
                    path = new Path();
                }
            }
            else
            {
                path = new Path();
            }

            return(path);
        }
Beispiel #14
0
        // Main parse loop
        void Parse()
        {
            // Use an exception to break out the loop
            try
            {
                bool isfirst = true;

                while (true)
                {
                    // Pick the command list
                    var cmd = ParseCommand(out var relative);

                    // First command MUST be a moveto
                    if (isfirst && cmd != 'M')
                    {
                        throw new ParserException($"First path command MUST be a moveto! Error at position {index}.");
                    }

                    // Moveto command
                    if (cmd == 'M')
                    {
                        bool first   = true;
                        bool onlyone = true;

                        foreach (var vs in DelimitedPositionTuple(1))
                        {
                            onlyone = first;

                            var lpos = lastValue;
                            var pos  = ProcessRelative(vs[0], relative);
                            commands.Add(first ? PathCommand.MoveTo(pos) : PathCommand.LineTo(pos));
                            lastControl = pos;
                            lastTangent = first ? 0 : lpos.AngleFacing(pos);

                            first = false;
                        }

                        lastCommand = onlyone ? PathCommandType.MoveTo : PathCommandType.LineTo;
                    }
                    // Lineto command
                    else if (cmd == 'L')
                    {
                        foreach (var vs in DelimitedPositionTuple(1))
                        {
                            var lpos = lastValue;
                            var pos  = ProcessRelative(vs[0], relative);
                            commands.Add(PathCommand.LineTo(pos));
                            lastTangent = lpos.AngleFacing(pos);
                            lastControl = pos;
                        }

                        lastCommand = PathCommandType.LineTo;
                    }
                    // Horizontal lineto command
                    else if (cmd == 'H')
                    {
                        foreach (var h in DelimitedSequence())
                        {
                            var lpos = lastValue;
                            var v    = relative ? 0 : lpos.Y;
                            var pos  = ProcessRelative(new Double2(h, v), relative);
                            commands.Add(PathCommand.LineTo(pos));
                            lastTangent = lpos.AngleFacing(pos);
                            lastControl = pos;
                        }

                        lastCommand = PathCommandType.LineTo;
                    }
                    // Vertical lineto command
                    else if (cmd == 'V')
                    {
                        foreach (var v in DelimitedSequence())
                        {
                            var lpos = lastValue;
                            var h    = relative ? 0 : lpos.X;
                            var pos  = ProcessRelative(new Double2(h, v), relative);
                            commands.Add(PathCommand.LineTo(pos));
                            lastTangent = lpos.AngleFacing(pos);
                            lastControl = pos;
                        }

                        lastCommand = PathCommandType.LineTo;
                    }
                    // Quadratic bezier command
                    else if (cmd == 'Q')
                    {
                        foreach (var vs in DelimitedPositionTuple(2))
                        {
                            var ctl = ProcessRelative(vs[0], relative, false);
                            var pos = ProcessRelative(vs[1], relative);
                            commands.Add(PathCommand.QuadraticCurveTo(ctl, pos));
                            lastTangent = ctl.AngleFacing(pos);
                            lastControl = ctl;
                        }

                        lastCommand = PathCommandType.QuadraticCurveTo;
                    }
                    // Smooth quadratic bezier command
                    else if (cmd == 'T')
                    {
                        foreach (var vs in DelimitedPositionTuple(1))
                        {
                            var lpos = lastValue;
                            var lctl = lastCommand == PathCommandType.QuadraticCurveTo ? lastControl : lastValue;
                            var ctl  = 2 * lpos - lctl;
                            var pos  = ProcessRelative(vs[0], relative);
                            commands.Add(PathCommand.QuadraticCurveTo(ctl, pos));
                            lastTangent = ctl.AngleFacing(pos);
                        }

                        lastCommand = PathCommandType.QuadraticCurveTo;
                    }
                    // Cubic bezier command
                    else if (cmd == 'C')
                    {
                        foreach (var vs in DelimitedPositionTuple(3))
                        {
                            var ctl  = ProcessRelative(vs[0], relative, false);
                            var ctl2 = ProcessRelative(vs[1], relative, false);
                            var pos  = ProcessRelative(vs[2], relative);
                            commands.Add(PathCommand.CubicCurveTo(ctl, ctl2, pos));
                            lastTangent = ctl2.AngleFacing(pos);
                            lastControl = ctl2;
                        }

                        lastCommand = PathCommandType.CubicCurveTo;
                    }
                    // Smooth cubic bezier command
                    else if (cmd == 'S')
                    {
                        foreach (var vs in DelimitedPositionTuple(2))
                        {
                            var lpos = lastValue;
                            var lctl = lastCommand == PathCommandType.CubicCurveTo ? lastControl : lastValue;
                            var ctl  = 2 * lpos - lctl;
                            var ctl2 = ProcessRelative(vs[0], relative, false);
                            var pos  = ProcessRelative(vs[1], relative);
                            commands.Add(PathCommand.CubicCurveTo(ctl, ctl2, pos));
                            lastTangent = ctl.AngleFacing(pos);
                            lastControl = ctl2;
                        }

                        lastCommand = PathCommandType.CubicCurveTo;
                    }
                    // Arc command
                    else if (cmd == 'A')
                    {
                        double ThrowF() => throw new ParserException($"Invalid parameter passed to arc command at position {index}.");
                        bool ThrowB() => ThrowF() == 0;

                        // Pick parameters
                        while (true)
                        {
                            // If there isn't a next double, end the command
                            var next = ParseDouble();
                            if (!next.HasValue)
                            {
                                break;
                            }

                            // Pick the doubles and flags in order
                            var rx = next.Value;
                            SkipWhitespaceAndDelimiters();
                            var ry = ParseDouble() ?? ThrowF();
                            SkipWhitespaceAndDelimiters();
                            var rAngle = ParseDouble() ?? ThrowF();
                            SkipWhitespaceAndDelimiters();
                            var largeArc = ParseFlag() ?? ThrowB();
                            SkipWhitespaceAndDelimiters();
                            var sweep = ParseFlag() ?? ThrowB();

                            Double2 target;
                            // Check if the target isn't a premature closepath
                            next = ParseDouble();
                            SkipWhitespaceAndDelimiters();
                            if (!next.HasValue)
                            {
                                if (parseString[index] == 'z' || parseString[index] == 'Z')
                                {
                                    target = new Double2(double.NaN, double.NaN);
                                }
                                else
                                {
                                    ThrowF();
                                }
                            }

                            // Assign the target
                            target.X = next.Value;
                            target.Y = ParseDouble() ?? ThrowF();

                            // Adjust for relative
                            rAngle = (rAngle.ToRadians() + (relative ? currentBearing : 0)).WrapAngle();
                            target = ProcessRelative(target, relative);

                            // Add the command
                            commands.Add(PathCommand.ArcTo(new Double2(rx, ry), rAngle, largeArc, sweep, target));
                        }
                    }
                    // Bearing command
                    else if (cmd == 'B')
                    {
                        if (!relative)
                        {
                            currentBearing = DelimitedSequence().Last().ToRadians();
                        }
                        else
                        {
                            currentBearing += DelimitedSequence().Sum().ToRadians();
                        }

                        lastCommand = PathCommandType.None;
                    }
                    // Complete closepath command
                    else if (cmd == 'Z')
                    {
                        commands.Add(PathCommand.ClosePath());
                        lastCommand = PathCommandType.ClosePath;
                    }
                    // Unknown command
                    else
                    {
                        throw new ParserException($"Unrecognized command at position {index-1}.");
                    }

                    isfirst = false;
                }
            }
            catch (EndParseException)
            {
                // The expression says it
            }
        }
Beispiel #15
0
        void _addArcCommands(
            float cx, float cy, float r, float a0, float a1,
            PathWinding dir, bool forceMoveTo, Matrix3 transform = null)
        {
            // Clamp angles
            float da = a1 - a0;

            if (dir == PathWinding.clockwise)
            {
                if (Mathf.Abs(da) >= Mathf.PI * 2)
                {
                    da = Mathf.PI * 2;
                }
                else
                {
                    while (da < 0.0f)
                    {
                        da += Mathf.PI * 2;
                    }

                    if (da <= 1e-5)
                    {
                        return;
                    }
                }
            }
            else
            {
                if (Mathf.Abs(da) >= Mathf.PI * 2)
                {
                    da = -Mathf.PI * 2;
                }
                else
                {
                    while (da > 0.0f)
                    {
                        da -= Mathf.PI * 2;
                    }

                    if (da >= -1e-5)
                    {
                        return;
                    }
                }
            }

            // Split arc into max 90 degree segments.
            int   ndivs = Mathf.Max(1, Mathf.Min((int)(Mathf.Abs(da) / (Mathf.PI * 0.5f) + 0.5f), 5));
            float hda   = (da / ndivs) / 2.0f;
            float kappa = Mathf.Abs(4.0f / 3.0f * (1.0f - Mathf.Cos(hda)) / Mathf.Sin(hda));

            if (dir == PathWinding.counterClockwise)
            {
                kappa = -kappa;
            }

            PathCommand move = (forceMoveTo || this._commands.Count == 0) ? PathCommand.moveTo : PathCommand.lineTo;
            float       px = 0, py = 0, ptanx = 0, ptany = 0;

            for (int i = 0; i <= ndivs; i++)
            {
                float a    = a0 + da * (i / (float)ndivs);
                float dx   = Mathf.Cos(a);
                float dy   = Mathf.Sin(a);
                float x    = cx + dx * r;
                float y    = cy + dy * r;
                float tanx = -dy * r * kappa;
                float tany = dx * r * kappa;

                if (i == 0)
                {
                    float x1 = x, y1 = y;
                    if (transform != null)
                    {
                        transform.mapXY(x1, y1, out x1, out y1);
                    }

                    if (move == PathCommand.moveTo)
                    {
                        this._appendMoveTo(x1, y1);
                    }
                    else
                    {
                        this._appendLineTo(x1, y1);
                    }
                }
                else
                {
                    float c1x = px + ptanx;
                    float c1y = py + ptany;
                    float c2x = x - tanx;
                    float c2y = y - tany;
                    float x1  = x;
                    float y1  = y;
                    if (transform != null)
                    {
                        transform.mapXY(c1x, c1y, out c1x, out c1y);
                        transform.mapXY(c2x, c2y, out c2x, out c2y);
                        transform.mapXY(x1, y1, out x1, out y1);
                    }

                    this._appendBezierTo(c1x, c1y, c2x, c2y, x1, y1);
                }

                px    = x;
                py    = y;
                ptanx = tanx;
                ptany = tany;
            }
        }
        static System.Drawing.Drawing2D.GraphicsPath ResolveGraphicsPath(GraphicsPath path)
        {
            //convert from graphics path to internal presentation
            System.Drawing.Drawing2D.GraphicsPath innerPath = path.InnerPath as System.Drawing.Drawing2D.GraphicsPath;
            if (innerPath != null)
            {
                return(innerPath);
            }
            //--------
            innerPath      = new System.Drawing.Drawing2D.GraphicsPath();
            path.InnerPath = innerPath;
            List <float>       points;
            List <PathCommand> cmds;

            GraphicsPath.GetPathData(path, out points, out cmds);
            int j       = cmds.Count;
            int p_index = 0;

            for (int i = 0; i < j; ++i)
            {
                PathCommand cmd = cmds[i];
                switch (cmd)
                {
                default:
                    throw new NotSupportedException();

                case PathCommand.Arc:
                    innerPath.AddArc(
                        points[p_index],
                        points[p_index + 1],
                        points[p_index + 2],
                        points[p_index + 3],
                        points[p_index + 4],
                        points[p_index + 5]);
                    p_index += 6;
                    break;

                case PathCommand.Bezier:
                    innerPath.AddBezier(
                        points[p_index],
                        points[p_index + 1],
                        points[p_index + 2],
                        points[p_index + 3],
                        points[p_index + 4],
                        points[p_index + 5],
                        points[p_index + 6],
                        points[p_index + 7]);
                    p_index += 8;
                    break;

                case PathCommand.CloseFigure:
                    innerPath.CloseFigure();
                    break;

                case PathCommand.Ellipse:
                    innerPath.AddEllipse(
                        points[p_index],
                        points[p_index + 1],
                        points[p_index + 2],
                        points[p_index + 3]);
                    p_index += 4;
                    break;

                case PathCommand.Line:
                    innerPath.AddLine(
                        points[p_index],
                        points[p_index + 1],
                        points[p_index + 2],
                        points[p_index + 3]);
                    p_index += 4;
                    break;

                case PathCommand.Rect:
                    innerPath.AddRectangle(
                        new System.Drawing.RectangleF(
                            points[p_index],
                            points[p_index + 1],
                            points[p_index + 2],
                            points[p_index + 3]));
                    p_index += 4;
                    break;

                case PathCommand.StartFigure:
                    break;
                }
            }


            return(innerPath);
        }
 public string PathRepresentation() => PathCommand.MoveTo(At(0)).ToString() + " " + PathCommandFrom().ToString();
        static SkiaSharp.SKPath ResolveGraphicsPath(GraphicsPath path)
        {
            //convert from graphics path to internal presentation
            SkiaSharp.SKPath innerPath = path.InnerPath as SkiaSharp.SKPath;
            if (innerPath != null)
            {
                return(innerPath);
            }
            //--------
            innerPath      = new SkiaSharp.SKPath();
            path.InnerPath = innerPath;
            List <float>       points;
            List <PathCommand> cmds;

            GraphicsPath.GetPathData(path, out points, out cmds);
            int j       = cmds.Count;
            int p_index = 0;

            for (int i = 0; i < j; ++i)
            {
                PathCommand cmd = cmds[i];
                switch (cmd)
                {
                default:
                    throw new NotSupportedException();

                case PathCommand.Arc:

                    var oval = SkiaSharp.SKRect.Create(points[p_index],
                                                       points[p_index + 1],
                                                       points[p_index + 2],
                                                       points[p_index + 3]);

                    innerPath.ArcTo(oval,
                                    points[p_index + 4],
                                    points[p_index + 5],
                                    true);
                    p_index += 6;
                    break;

                case PathCommand.Bezier:
                    innerPath.MoveTo(points[p_index]
                                     , points[p_index + 1]);
                    innerPath.CubicTo(
                        points[p_index + 2],
                        points[p_index + 3],
                        points[p_index + 4],
                        points[p_index + 5],
                        points[p_index + 6],
                        points[p_index + 7]);
                    p_index += 8;
                    break;

                case PathCommand.CloseFigure:
                    //?
                    break;

                case PathCommand.Ellipse:

                    innerPath.AddOval(
                        SkiaSharp.SKRect.Create(
                            points[p_index],
                            points[p_index + 1],
                            points[p_index + 2],
                            points[p_index + 3]
                            ));

                    p_index += 4;
                    break;

                case PathCommand.Line:

                    innerPath.MoveTo(points[p_index],
                                     points[p_index + 1]);
                    innerPath.LineTo(
                        points[p_index + 2],
                        points[p_index + 3]);
                    p_index += 4;
                    break;

                case PathCommand.Rect:
                    innerPath.AddRect(
                        SkiaSharp.SKRect.Create(
                            points[p_index],
                            points[p_index + 1],
                            points[p_index + 2],
                            points[p_index + 3]
                            ));
                    p_index += 4;
                    break;

                case PathCommand.StartFigure:
                    break;
                }
            }


            return(innerPath);
        }
Beispiel #19
0
 internal string ToSvgString()
 {
     return(PathCommand.ToSvgString(ToPathCommands()));
 }
Beispiel #20
0
 public PathCommandDebuggerView(PathCommand pathCommand)
 {
     this.pathCommand = pathCommand;
 }
Beispiel #21
0
        protected override Path ParsePath(Dictionary <string, string> properties)
        {
            // Get the attributes of the rectangle
            var x      = DoubleUtils.TryParse(properties.GetOrDefault("x")) ?? 0;
            var y      = DoubleUtils.TryParse(properties.GetOrDefault("y")) ?? 0;
            var width  = DoubleUtils.TryParse(properties.GetOrDefault("width")) ?? 0;
            var height = DoubleUtils.TryParse(properties.GetOrDefault("height")) ?? 0;

            var radii = new Double2();

            radii.X = DoubleUtils.TryParse(properties.GetOrDefault("rx")) ?? double.NaN;
            radii.Y = DoubleUtils.TryParse(properties.GetOrDefault("ry")) ?? double.NaN;

            // Quit on invalid values
            if (width <= 0 || height <= 0)
            {
                return(new Path());
            }

            // Check both radii
            if (double.IsNaN(radii.X) && double.IsNaN(radii.Y))
            {
                radii.X = radii.Y = 0;
            }
            else if (double.IsNaN(radii.X))
            {
                radii.X = radii.Y;
            }
            else if (double.IsNaN(radii.Y))
            {
                radii.Y = radii.X;
            }

            // Take out the sign and clamp them
            radii.X = Math.Abs(radii.X);
            radii.Y = Math.Abs(radii.Y);

            if (radii.X > width / 2)
            {
                radii.X = width / 2;
            }
            if (radii.Y > height / 2)
            {
                radii.Y = height / 2;
            }

            // Now mount the path commands
            PathCommand[] pathCommands;
            if (radii.X == 0 || radii.Y == 0)
            {
                pathCommands = new[]
                {
                    PathCommand.MoveTo(new Double2(x, y)),
                    PathCommand.LineTo(new Double2(x + width, y)),
                    PathCommand.LineTo(new Double2(x + width, y + height)),
                    PathCommand.LineTo(new Double2(x, y + height)),
                    PathCommand.ClosePath()
                }
            }
            ;
            else
            {
                pathCommands = new[]
                {
                    PathCommand.MoveTo(new Double2(x + radii.X, y)),
                    PathCommand.LineTo(new Double2(x + width - radii.X, y)),
                    PathCommand.ArcTo(radii, 0, false, true, new Double2(x + width, y + radii.Y)),
                    PathCommand.LineTo(new Double2(x + width, y + height - radii.Y)),
                    PathCommand.ArcTo(radii, 0, false, true, new Double2(x + width - radii.X, y + height)),
                    PathCommand.LineTo(new Double2(x + radii.X, y + height)),
                    PathCommand.ArcTo(radii, 0, false, true, new Double2(x, y + height - radii.Y)),
                    PathCommand.LineTo(new Double2(x, y + radii.Y)),
                    PathCommand.ArcToClose(radii, 0, false, true)
                }
            };

            // And finally return
            return(new Path(pathCommands));
        }
    }