CraftSynth.ImageEditor – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | using System; |
2 | using System.Drawing; |
||
3 | using System.Drawing.Drawing2D; |
||
4 | using System.Globalization; |
||
5 | using System.Runtime.Serialization; |
||
6 | using System.Windows.Forms; |
||
7 | |||
8 | namespace CraftSynth.ImageEditor |
||
9 | { |
||
10 | /// <summary> |
||
11 | /// Line graphic object |
||
12 | /// </summary> |
||
13 | //[Serializable] |
||
14 | public class DrawLine : DrawObject |
||
15 | { |
||
16 | private Point startPoint; |
||
17 | private Point endPoint; |
||
18 | |||
19 | private const string entryStart = "Start"; |
||
20 | private const string entryEnd = "End"; |
||
21 | |||
22 | /// <summary> |
||
23 | /// Graphic objects for hit test |
||
24 | /// </summary> |
||
25 | private GraphicsPath areaPath = null; |
||
26 | |||
27 | private Pen areaPen = null; |
||
28 | private Region areaRegion = null; |
||
29 | |||
30 | private bool _disposed = false; |
||
31 | |||
32 | public DrawLine() |
||
33 | { |
||
34 | startPoint.X = 0; |
||
35 | startPoint.Y = 0; |
||
36 | endPoint.X = 1; |
||
37 | endPoint.Y = 1; |
||
38 | ZOrder = 0; |
||
39 | |||
40 | Initialize(); |
||
41 | } |
||
42 | |||
43 | #region Destruction |
||
44 | protected override void Dispose(bool disposing) |
||
45 | { |
||
46 | if (!this._disposed) |
||
47 | { |
||
48 | if (disposing) |
||
49 | { |
||
50 | // Free any managed objects here. |
||
51 | if (this.areaPath!=null) |
||
52 | { |
||
53 | this.areaPath.Dispose(); |
||
54 | } |
||
55 | if (this.areaPen!=null) |
||
56 | { |
||
57 | this.areaPen.Dispose(); |
||
58 | } |
||
59 | if (this.areaRegion!=null) |
||
60 | { |
||
61 | this.areaRegion.Dispose(); |
||
62 | } |
||
63 | } |
||
64 | |||
65 | // Free any unmanaged objects here. |
||
66 | |||
67 | this._disposed = true; |
||
68 | } |
||
69 | base.Dispose(disposing); |
||
70 | } |
||
71 | |||
72 | ~DrawLine() |
||
73 | { |
||
74 | this.Dispose(false); |
||
75 | } |
||
76 | #endregion |
||
77 | |||
78 | public DrawLine(int x1, int y1, int x2, int y2, Color lineColor, int lineWidth, DrawingPens.PenType penType, LineCap endCap) |
||
79 | { |
||
80 | startPoint.X = x1; |
||
81 | startPoint.Y = y1; |
||
82 | endPoint.X = x2; |
||
83 | endPoint.Y = y2; |
||
84 | Color = lineColor; |
||
85 | PenWidth = lineWidth; |
||
86 | PenType = penType; |
||
87 | EndCap = endCap; |
||
88 | ZOrder = 0; |
||
89 | TipText = String.Format("Line Start @ {0}-{1}, End @ {2}-{3}", x1, y1, x2, y2); |
||
90 | |||
91 | Initialize(); |
||
92 | } |
||
93 | |||
94 | public override void Draw(Graphics g) |
||
95 | { |
||
96 | g.SmoothingMode = SmoothingMode.AntiAlias; |
||
97 | |||
98 | Pen pen; |
||
99 | if (DrawPen == null) |
||
100 | { |
||
101 | pen = new Pen(Color, PenWidth); |
||
102 | DrawingPens.SetCurrentPen(ref pen, PenType, EndCap); |
||
103 | } |
||
104 | else |
||
105 | pen = (Pen) DrawPen.Clone(); |
||
106 | GraphicsPath gp = new GraphicsPath(); |
||
107 | gp.AddLine(startPoint, endPoint); |
||
108 | // Rotate the path about it's center if necessary |
||
109 | if (Rotation != 0) |
||
110 | { |
||
111 | RectangleF pathBounds = gp.GetBounds(); |
||
112 | Matrix m = new Matrix(); |
||
113 | m.RotateAt(Rotation, new PointF(pathBounds.Left + (pathBounds.Width / 2), pathBounds.Top + (pathBounds.Height / 2)), MatrixOrder.Append); |
||
114 | gp.Transform(m); |
||
115 | } |
||
116 | g.DrawPath(pen, gp); |
||
117 | gp.Dispose(); |
||
118 | pen.Dispose(); |
||
119 | } |
||
120 | |||
121 | /// <summary> |
||
122 | /// Clone this instance |
||
123 | /// </summary> |
||
124 | public override DrawObject Clone() |
||
125 | { |
||
126 | DrawLine drawLine = new DrawLine(); |
||
127 | drawLine.startPoint = startPoint; |
||
128 | drawLine.endPoint = endPoint; |
||
129 | |||
130 | FillDrawObjectFields(drawLine); |
||
131 | return drawLine; |
||
132 | } |
||
133 | |||
134 | public override int HandleCount |
||
135 | { |
||
136 | get { return 2; } |
||
137 | } |
||
138 | |||
139 | /// <summary> |
||
140 | /// Get handle point by 1-based number |
||
141 | /// </summary> |
||
142 | /// <param name="handleNumber"></param> |
||
143 | /// <returns></returns> |
||
144 | public override Point GetHandle(int handleNumber) |
||
145 | { |
||
146 | GraphicsPath gp = new GraphicsPath(); |
||
147 | Matrix m = new Matrix(); |
||
148 | gp.AddLine(startPoint, endPoint); |
||
149 | RectangleF pathBounds = gp.GetBounds(); |
||
150 | m.RotateAt(Rotation, new PointF(pathBounds.Left + (pathBounds.Width / 2), pathBounds.Top + (pathBounds.Height / 2)), MatrixOrder.Append); |
||
151 | gp.Transform(m); |
||
152 | Point start, end; |
||
153 | start = Point.Truncate(gp.PathPoints[0]); |
||
154 | end = Point.Truncate(gp.PathPoints[1]); |
||
155 | gp.Dispose(); |
||
156 | m.Dispose(); |
||
157 | if (handleNumber == 1) |
||
158 | return start; |
||
159 | else |
||
160 | return end; |
||
161 | } |
||
162 | |||
163 | /// <summary> |
||
164 | /// Hit test. |
||
165 | /// Return value: -1 - no hit |
||
166 | /// 0 - hit anywhere |
||
167 | /// > 1 - handle number |
||
168 | /// </summary> |
||
169 | /// <param name="point"></param> |
||
170 | /// <returns></returns> |
||
171 | public override int HitTest(Point point) |
||
172 | { |
||
173 | if (Selected) |
||
174 | for (int i = 1; i <= HandleCount; i++) |
||
175 | { |
||
176 | GraphicsPath gp = new GraphicsPath(); |
||
177 | gp.AddRectangle(GetHandleRectangle(i)); |
||
178 | bool vis = gp.IsVisible(point); |
||
179 | gp.Dispose(); |
||
180 | if (vis) |
||
181 | return i; |
||
182 | } |
||
183 | // OK, so the point is not on a selection handle, is it anywhere else on the line? |
||
184 | if (PointInObject(point)) |
||
185 | return 0; |
||
186 | return -1; |
||
187 | } |
||
188 | |||
189 | protected override bool PointInObject(Point point) |
||
190 | { |
||
191 | CreateObjects(); |
||
192 | //return AreaPath.IsVisible(point); |
||
193 | return AreaRegion.IsVisible(point); |
||
194 | } |
||
195 | |||
196 | public override Rectangle GetBounds(Graphics g) |
||
197 | { |
||
198 | var rectF = areaRegion.GetBounds(g); |
||
199 | rectF = GetNormalizedRectangle(rectF); |
||
200 | var rect = new Rectangle((int)Math.Floor(rectF.X), (int)Math.Floor(rectF.Y), (int)Math.Ceiling(rectF.Width), (int)Math.Ceiling(rectF.Height)); |
||
201 | return rect; |
||
202 | } |
||
203 | |||
204 | |||
205 | public override bool IntersectsWith(Rectangle rectangle) |
||
206 | { |
||
207 | CreateObjects(); |
||
208 | |||
209 | return AreaRegion.IsVisible(rectangle); |
||
210 | } |
||
211 | |||
212 | public override Cursor GetHandleCursor(int handleNumber) |
||
213 | { |
||
214 | switch (handleNumber) |
||
215 | { |
||
216 | case 1: |
||
217 | case 2: |
||
218 | return Cursors.SizeAll; |
||
219 | default: |
||
220 | return Cursors.Default; |
||
221 | } |
||
222 | } |
||
223 | |||
224 | public override void MoveHandleTo(Point point, int handleNumber) |
||
225 | { |
||
226 | //GraphicsPath gp = new GraphicsPath(); |
||
227 | //Matrix m = new Matrix(); |
||
228 | //if (handleNumber == 1) |
||
229 | // gp.AddLine(point, endPoint); |
||
230 | //else |
||
231 | // gp.AddLine(startPoint, point); |
||
232 | |||
233 | //RectangleF pathBounds = gp.GetBounds(); |
||
234 | //m.RotateAt(Rotation, new PointF(pathBounds.Left + (pathBounds.Width / 2), pathBounds.Top + (pathBounds.Height / 2)), MatrixOrder.Append); |
||
235 | //gp.Transform(m); |
||
236 | //Point start, end; |
||
237 | //start = Point.Truncate(gp.PathPoints[0]); |
||
238 | //end = Point.Truncate(gp.PathPoints[1]); |
||
239 | //gp.Dispose(); |
||
240 | //m.Dispose(); |
||
241 | //if (handleNumber == 1) |
||
242 | // startPoint = start; |
||
243 | //else |
||
244 | // endPoint = end; |
||
245 | |||
246 | if (handleNumber == 1) |
||
247 | startPoint = point; |
||
248 | else |
||
249 | endPoint = point; |
||
250 | |||
251 | Dirty = true; |
||
252 | Invalidate(); |
||
253 | } |
||
254 | |||
255 | public override void Move(int deltaX, int deltaY) |
||
256 | { |
||
257 | startPoint.X += deltaX; |
||
258 | startPoint.Y += deltaY; |
||
259 | |||
260 | endPoint.X += deltaX; |
||
261 | endPoint.Y += deltaY; |
||
262 | Dirty = true; |
||
263 | Invalidate(); |
||
264 | } |
||
265 | |||
266 | public override void SaveToStream(SerializationInfo info, int orderNumber, int objectIndex) |
||
267 | { |
||
268 | info.AddValue( |
||
269 | String.Format(CultureInfo.InvariantCulture, |
||
270 | "{0}{1}-{2}", |
||
271 | entryStart, orderNumber, objectIndex), |
||
272 | startPoint); |
||
273 | |||
274 | info.AddValue( |
||
275 | String.Format(CultureInfo.InvariantCulture, |
||
276 | "{0}{1}-{2}", |
||
277 | entryEnd, orderNumber, objectIndex), |
||
278 | endPoint); |
||
279 | |||
280 | base.SaveToStream(info, orderNumber, objectIndex); |
||
281 | } |
||
282 | |||
283 | public override void LoadFromStream(SerializationInfo info, int orderNumber, int objectIndex) |
||
284 | { |
||
285 | startPoint = (Point)info.GetValue( |
||
286 | String.Format(CultureInfo.InvariantCulture, |
||
287 | "{0}{1}-{2}", |
||
288 | entryStart, orderNumber, objectIndex), |
||
289 | typeof(Point)); |
||
290 | |||
291 | endPoint = (Point)info.GetValue( |
||
292 | String.Format(CultureInfo.InvariantCulture, |
||
293 | "{0}{1}-{2}", |
||
294 | entryEnd, orderNumber, objectIndex), |
||
295 | typeof(Point)); |
||
296 | |||
297 | base.LoadFromStream(info, orderNumber, objectIndex); |
||
298 | } |
||
299 | |||
300 | /// <summary> |
||
301 | /// Invalidate object. |
||
302 | /// When object is invalidated, path used for hit test |
||
303 | /// is released and should be created again. |
||
304 | /// </summary> |
||
305 | protected void Invalidate() |
||
306 | { |
||
307 | if (AreaPath != null) |
||
308 | { |
||
309 | AreaPath.Dispose(); |
||
310 | AreaPath = null; |
||
311 | } |
||
312 | |||
313 | if (AreaPen != null) |
||
314 | { |
||
315 | AreaPen.Dispose(); |
||
316 | AreaPen = null; |
||
317 | } |
||
318 | |||
319 | if (AreaRegion != null) |
||
320 | { |
||
321 | AreaRegion.Dispose(); |
||
322 | AreaRegion = null; |
||
323 | } |
||
324 | } |
||
325 | |||
326 | /// <summary> |
||
327 | /// Create graphic objects used for hit test. |
||
328 | /// </summary> |
||
329 | protected virtual void CreateObjects() |
||
330 | { |
||
331 | if (AreaPath != null) |
||
332 | return; |
||
333 | |||
334 | // Create path which contains wide line |
||
335 | // for easy mouse selection |
||
336 | AreaPath = new GraphicsPath(); |
||
337 | // Take into account the width of the pen used to draw the actual object |
||
338 | AreaPen = new Pen(Color.Black, PenWidth < 7 ? 7 : PenWidth); |
||
339 | // Prevent Out of Memory crash when startPoint == endPoint |
||
340 | if (startPoint.Equals((Point)endPoint)) |
||
341 | { |
||
342 | endPoint.X++; |
||
343 | endPoint.Y++; |
||
344 | } |
||
345 | AreaPath.AddLine(startPoint.X, startPoint.Y, endPoint.X, endPoint.Y); |
||
346 | AreaPath.Widen(AreaPen); |
||
347 | // Rotate the path about it's center if necessary |
||
348 | if (Rotation != 0) |
||
349 | { |
||
350 | RectangleF pathBounds = AreaPath.GetBounds(); |
||
351 | Matrix m = new Matrix(); |
||
352 | m.RotateAt(Rotation, new PointF(pathBounds.Left + (pathBounds.Width / 2), pathBounds.Top + (pathBounds.Height / 2)), MatrixOrder.Append); |
||
353 | AreaPath.Transform(m); |
||
354 | m.Dispose(); |
||
355 | } |
||
356 | |||
357 | // Create region from the path |
||
358 | AreaRegion = new Region(AreaPath); |
||
359 | } |
||
360 | |||
361 | protected GraphicsPath AreaPath |
||
362 | { |
||
363 | get { return areaPath; } |
||
364 | set { areaPath = value; } |
||
365 | } |
||
366 | |||
367 | protected Pen AreaPen |
||
368 | { |
||
369 | get { return areaPen; } |
||
370 | set { areaPen = value; } |
||
371 | } |
||
372 | |||
373 | protected Region AreaRegion |
||
374 | { |
||
375 | get { return areaRegion; } |
||
376 | set { areaRegion = value; } |
||
377 | } |
||
378 | |||
379 | #region Helper gunctions |
||
380 | public static RectangleF GetNormalizedRectangle(float x1, float y1, float x2, float y2) |
||
381 | { |
||
382 | if (x2 < x1) |
||
383 | { |
||
384 | float tmp = x2; |
||
385 | x2 = x1; |
||
386 | x1 = tmp; |
||
387 | } |
||
388 | |||
389 | if (y2 < y1) |
||
390 | { |
||
391 | float tmp = y2; |
||
392 | y2 = y1; |
||
393 | y1 = tmp; |
||
394 | } |
||
395 | return new RectangleF(x1, y1, x2 - x1, y2 - y1); |
||
396 | } |
||
397 | |||
398 | public static RectangleF GetNormalizedRectangle(PointF p1, PointF p2) |
||
399 | { |
||
400 | return GetNormalizedRectangle(p1.X, p1.Y, p2.X, p2.Y); |
||
401 | } |
||
402 | |||
403 | public static RectangleF GetNormalizedRectangle(RectangleF r) |
||
404 | { |
||
405 | return GetNormalizedRectangle(r.X, r.Y, r.X + r.Width, r.Y + r.Height); |
||
406 | } |
||
407 | #endregion |
||
408 | } |
||
409 | } |