SvgRectangle.cs 10.5 KB
Newer Older
davescriven's avatar
davescriven committed
1
2
3
4
5
6
using System;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace Svg
{
7
    /// <summary>
John Nguyen's avatar
John Nguyen committed
8
    /// Represents an SVG rectangle that could also have rounded edges.
9
    /// </summary>
10
    [SvgElement("rect")]
11
    public class SvgRectangle : SvgPathBasedElement
davescriven's avatar
davescriven committed
12
13
14
    {
        private SvgUnit _cornerRadiusX;
        private SvgUnit _cornerRadiusY;
15
        private SvgUnit _height;
davescriven's avatar
davescriven committed
16
        private GraphicsPath _path;
17
18
19
20
        private SvgUnit _width;
        private SvgUnit _x;
        private SvgUnit _y;

21
22
23
        /// <summary>
        /// Initializes a new instance of the <see cref="SvgRectangle"/> class.
        /// </summary>
24
25
26
27
        public SvgRectangle()
        {
            _width = new SvgUnit(0.0f);
            _height = new SvgUnit(0.0f);
28
29
            _cornerRadiusX = new SvgUnit(0.0f);
            _cornerRadiusY = new SvgUnit(0.0f);
Tebjan Halm's avatar
Tebjan Halm committed
30
31
            _x = new SvgUnit(0.0f);
            _y = new SvgUnit(0.0f);
32
        }
davescriven's avatar
davescriven committed
33

34
35
36
        /// <summary>
        /// Gets an <see cref="SvgPoint"/> representing the top left point of the rectangle.
        /// </summary>
davescriven's avatar
davescriven committed
37
38
        public SvgPoint Location
        {
39
40
41
            get { return new SvgPoint(X, Y); }
        }

42
43
44
        /// <summary>
        /// Gets or sets the position where the left point of the rectangle should start.
        /// </summary>
davescriven's avatar
davescriven committed
45
46
47
        [SvgAttribute("x")]
        public SvgUnit X
        {
48
49
50
51
52
53
54
55
56
57
        	get { return _x; }
        	set
        	{
        		if(_x != value)
        		{
        			_x = value;
        			OnAttributeChanged(new AttributeEventArgs{ Attribute = "x", Value = value });
        			IsPathDirty = true;
        		}
        	}
davescriven's avatar
davescriven committed
58
59
        }

60
61
62
        /// <summary>
        /// Gets or sets the position where the top point of the rectangle should start.
        /// </summary>
davescriven's avatar
davescriven committed
63
64
65
        [SvgAttribute("y")]
        public SvgUnit Y
        {
66
67
68
69
70
71
72
73
74
75
        	get { return _y; }
        	set
        	{
        		if(_y != value)
        		{
        			_y = value;
        			OnAttributeChanged(new AttributeEventArgs{ Attribute = "y", Value = value });
        			IsPathDirty = true;
        		}
        	}
davescriven's avatar
davescriven committed
76
77
        }

78
79
80
        /// <summary>
        /// Gets or sets the width of the rectangle.
        /// </summary>
davescriven's avatar
davescriven committed
81
82
83
        [SvgAttribute("width")]
        public SvgUnit Width
        {
84
85
86
87
88
89
90
91
92
93
        	get { return _width; }
        	set
        	{
        		if(_width != value)
        		{
        			_width = value;
        			OnAttributeChanged(new AttributeEventArgs{ Attribute = "width", Value = value });
        			IsPathDirty = true;
        		}
        	}
davescriven's avatar
davescriven committed
94
95
        }

96
97
98
        /// <summary>
        /// Gets or sets the height of the rectangle.
        /// </summary>
davescriven's avatar
davescriven committed
99
100
101
        [SvgAttribute("height")]
        public SvgUnit Height
        {
102
103
104
105
106
107
108
109
110
111
        	get { return _height; }
        	set
        	{
        		if(_height != value)
        		{
        			_height = value;
        			OnAttributeChanged(new AttributeEventArgs{ Attribute = "height", Value = value });
        			IsPathDirty = true;
        		}
        	}
davescriven's avatar
davescriven committed
112
113
        }

114
115
116
        /// <summary>
        /// Gets or sets the X-radius of the rounded edges of this rectangle.
        /// </summary>
davescriven's avatar
davescriven committed
117
118
119
120
121
122
        [SvgAttribute("rx")]
        public SvgUnit CornerRadiusX
        {
            get
            {
                // If ry has been set and rx hasn't, use it's value
123
124
                if (_cornerRadiusX.Value == 0.0f && _cornerRadiusY.Value > 0.0f)
                    return _cornerRadiusY;
davescriven's avatar
davescriven committed
125

126
127
128
129
130
131
                return _cornerRadiusX;
            }
            set
            {
                _cornerRadiusX = value;
                IsPathDirty = true;
davescriven's avatar
davescriven committed
132
133
134
            }
        }

135
136
137
        /// <summary>
        /// Gets or sets the Y-radius of the rounded edges of this rectangle.
        /// </summary>
davescriven's avatar
davescriven committed
138
139
140
141
142
143
        [SvgAttribute("ry")]
        public SvgUnit CornerRadiusY
        {
            get
            {
                // If rx has been set and ry hasn't, use it's value
144
145
                if (_cornerRadiusY.Value == 0.0f && _cornerRadiusX.Value > 0.0f)
                    return _cornerRadiusX;
davescriven's avatar
davescriven committed
146

147
                return _cornerRadiusY;
davescriven's avatar
davescriven committed
148
149
150
            }
            set
            {
151
152
                _cornerRadiusY = value;
                IsPathDirty = true;
davescriven's avatar
davescriven committed
153
154
155
            }
        }

156
157
158
        /// <summary>
        /// Gets or sets a value to determine if anti-aliasing should occur when the element is being rendered.
        /// </summary>
davescriven's avatar
davescriven committed
159
160
        protected override bool RequiresSmoothRendering
        {
161
162
163
164
165
166
167
            get
            {
                if (base.RequiresSmoothRendering)
                    return (CornerRadiusX.Value > 0 || CornerRadiusY.Value > 0);
                else
                    return false;
            }
davescriven's avatar
davescriven committed
168
169
        }

170
171
172
        /// <summary>
        /// Gets the <see cref="GraphicsPath"/> for this element.
        /// </summary>
Eric Domke's avatar
Eric Domke committed
173
        public override GraphicsPath Path(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
174
        {
175
            if (_path == null || IsPathDirty)
davescriven's avatar
davescriven committed
176
            {
177
                var halfStrokeWidth = new SvgUnit(base.StrokeWidth / 2);
178
179
180
181
182
183
184
185

                // If it is to render, don't need to consider stroke
                if (renderer != null)
                {
                  halfStrokeWidth = 0;
                  this.IsPathDirty = false;
                }

186
187
                // If the corners aren't to be rounded just create a rectangle
                if (CornerRadiusX.Value == 0.0f && CornerRadiusY.Value == 0.0f)
davescriven's avatar
davescriven committed
188
                {
heindlalex's avatar
heindlalex committed
189
190
191
                  var loc_y = Location.Y.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
                  var loc_x = Location.X.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);

192
                  // Starting location which take consideration of stroke width
heindlalex's avatar
heindlalex committed
193
                  SvgPoint strokedLocation = new SvgPoint(loc_x- halfStrokeWidth, loc_y - halfStrokeWidth);
194

drbassett's avatar
drbassett committed
195
196
                  var width = this.Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) + halfStrokeWidth;
                  var height = this.Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this) + halfStrokeWidth;
197
198
                  
                  var rectangle = new RectangleF(strokedLocation.ToDeviceValue(renderer, this), new SizeF(width, height));
davescriven's avatar
davescriven committed
199

200
201
202
203
204
205
206
207
208
209
210
211
212
                    _path = new GraphicsPath();
                    _path.StartFigure();
                    _path.AddRectangle(rectangle);
                    _path.CloseFigure();
                }
                else
                {
                    _path = new GraphicsPath();
                    var arcBounds = new RectangleF();
                    var lineStart = new PointF();
                    var lineEnd = new PointF();
                    var width = Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
                    var height = Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
Eric Domke's avatar
Eric Domke committed
213
214
                    var rx = Math.Min(CornerRadiusX.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) * 2, width);
                    var ry = Math.Min(CornerRadiusY.ToDeviceValue(renderer, UnitRenderingType.Vertical, this) * 2, height);
215
                    var location = Location.ToDeviceValue(renderer, this);
davescriven's avatar
davescriven committed
216

217
218
                    // Start
                    _path.StartFigure();
davescriven's avatar
davescriven committed
219

220
221
222
223
224
                    // Add first arc
                    arcBounds.Location = location;
                    arcBounds.Width = rx;
                    arcBounds.Height = ry;
                    _path.AddArc(arcBounds, 180, 90);
davescriven's avatar
davescriven committed
225

226
227
228
229
230
231
                    // Add first line
                    lineStart.X = Math.Min(location.X + rx, location.X + width * 0.5f);
                    lineStart.Y = location.Y;
                    lineEnd.X = Math.Max(location.X + width - rx, location.X + width * 0.5f);
                    lineEnd.Y = lineStart.Y;
                    _path.AddLine(lineStart, lineEnd);
davescriven's avatar
davescriven committed
232

233
234
235
                    // Add second arc
                    arcBounds.Location = new PointF(location.X + width - rx, location.Y);
                    _path.AddArc(arcBounds, 270, 90);
davescriven's avatar
davescriven committed
236

237
238
239
240
241
242
                    // Add second line
                    lineStart.X = location.X + width;
                    lineStart.Y = Math.Min(location.Y + ry, location.Y + height * 0.5f);
                    lineEnd.X = lineStart.X;
                    lineEnd.Y = Math.Max(location.Y + height - ry, location.Y + height * 0.5f);
                    _path.AddLine(lineStart, lineEnd);
davescriven's avatar
davescriven committed
243

244
245
246
                    // Add third arc
                    arcBounds.Location = new PointF(location.X + width - rx, location.Y + height - ry);
                    _path.AddArc(arcBounds, 0, 90);
davescriven's avatar
davescriven committed
247

248
249
250
251
252
253
                    // Add third line
                    lineStart.X = Math.Max(location.X + width - rx, location.X + width * 0.5f);
                    lineStart.Y = location.Y + height;
                    lineEnd.X = Math.Min(location.X + rx, location.X + width * 0.5f);
                    lineEnd.Y = lineStart.Y;
                    _path.AddLine(lineStart, lineEnd);
davescriven's avatar
davescriven committed
254

255
256
257
                    // Add third arc
                    arcBounds.Location = new PointF(location.X, location.Y + height - ry);
                    _path.AddArc(arcBounds, 90, 90);
davescriven's avatar
davescriven committed
258

259
260
261
262
263
264
                    // Add fourth line
                    lineStart.X = location.X;
                    lineStart.Y = Math.Max(location.Y + height - ry, location.Y + height * 0.5f);
                    lineEnd.X = lineStart.X;
                    lineEnd.Y = Math.Min(location.Y + ry, location.Y + height * 0.5f);
                    _path.AddLine(lineStart, lineEnd);
davescriven's avatar
davescriven committed
265

266
267
                    // Close
                    _path.CloseFigure();
davescriven's avatar
davescriven committed
268
                }
269
            }
270
            return _path;
davescriven's avatar
davescriven committed
271
272
        }

273
274
275
        /// <summary>
        /// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="Graphics"/> object.
        /// </summary>
Eric Domke's avatar
Eric Domke committed
276
        protected override void Render(ISvgRenderer renderer)
davescriven's avatar
davescriven committed
277
        {
278
            if (Width.Value > 0.0f && Height.Value > 0.0f)
279
280
281
            {
                base.Render(renderer);
            }
davescriven's avatar
davescriven committed
282
        }
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300


		public override SvgElement DeepCopy()
		{
			return DeepCopy<SvgRectangle>();
		}

		public override SvgElement DeepCopy<T>()
		{
 			var newObj = base.DeepCopy<T>() as SvgRectangle;
			newObj.CornerRadiusX = this.CornerRadiusX;
			newObj.CornerRadiusY = this.CornerRadiusY;
			newObj.Height = this.Height;
			newObj.Width = this.Width;
			newObj.X = this.X;
			newObj.Y = this.Y;
			return newObj;
		}
davescriven's avatar
davescriven committed
301
    }
John Nguyen's avatar
John Nguyen committed
302
}