2 // System.Drawing.Drawing2D.ExtendedGeneralPath.cs
5 // Bors Kirzner <boris@mainsoft.com>
7 // Copyright (C) 2005 Mainsoft Corporation, (http://www.mainsoft.com)
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using java.awt.geom;
\r
35 namespace System.Drawing.Drawing2D
\r
37 internal class ExtendedGeneralPath : Shape, ICloneable
\r
41 public const int WIND_EVEN_ODD = 0; //PathIterator__Finals.WIND_EVEN_ODD;
\r
42 public const int WIND_NON_ZERO = 1; //PathIterator__Finals.WIND_NON_ZERO;
\r
44 public const sbyte SEG_MOVETO = 0; //(byte) PathIterator__Finals.SEG_MOVETO;
\r
45 public const sbyte SEG_LINETO = 1; //(byte) PathIterator__Finals.SEG_LINETO;
\r
46 public const sbyte SEG_QUADTO = 2; //(byte) PathIterator__Finals.SEG_QUADTO;
\r
47 public const sbyte SEG_CUBICTO = 3; //(byte) PathIterator__Finals.SEG_CUBICTO;
\r
48 public const sbyte SEG_CLOSE = 4; //(byte) PathIterator__Finals.SEG_CLOSE;
\r
50 public const sbyte SEG_START = 16; // segment start
\r
52 public const sbyte SEG_MASK = SEG_MOVETO | SEG_LINETO | SEG_QUADTO | SEG_CUBICTO | SEG_CLOSE; // mask to eliminate SEG_CLOSE and SEG_MARKER
\r
54 private const sbyte SEG_MARKER = 32; // path marker
\r
57 private sbyte [] _types;
\r
58 private float [] _coords;
\r
59 private int _typesCount;
\r
60 private int _coordsCount;
\r
61 private int _windingRule;
\r
63 private PathData _pathData;
\r
64 private GeneralPath _generalPath;
\r
66 const int INIT_SIZE = 20;
\r
67 const int EXPAND_MAX = 500;
\r
69 #endregion // Fileds
\r
71 #region Constructors
\r
73 public ExtendedGeneralPath() : this (WIND_NON_ZERO, INIT_SIZE, INIT_SIZE)
\r
77 public ExtendedGeneralPath(int rule) : this (rule, INIT_SIZE, INIT_SIZE)
\r
81 public ExtendedGeneralPath(int rule, int initialCapacity) : this (rule, initialCapacity, initialCapacity)
\r
85 public ExtendedGeneralPath(Shape s) : this(WIND_NON_ZERO, INIT_SIZE, INIT_SIZE)
\r
87 PathIterator pi = s.getPathIterator (null);
\r
88 setWindingRule (pi.getWindingRule ());
\r
92 private ExtendedGeneralPath(int rule, int initialTypes, int initialCoords)
\r
94 setWindingRule(rule);
\r
95 Reset (initialTypes, initialCoords);
\r
98 #endregion // Constructors
\r
102 private GeneralPath GeneralPath
\r
105 if (_generalPath == null) {
\r
106 _generalPath = GetGeneralPath ();
\r
108 return _generalPath;
\r
112 public sbyte [] Types
\r
114 get { return _types; }
\r
117 public float [] Coords
\r
119 get { return _coords; }
\r
122 public int TypesCount
\r
124 get { return _typesCount; }
\r
127 public int CoordsCount
\r
129 get { return _coordsCount; }
\r
132 public bool LastFigureClosed
\r
135 return ((TypesCount == 0) ||
\r
136 ((Types [TypesCount - 1] & ExtendedGeneralPath.SEG_CLOSE) != 0) ||
\r
137 ((Types [TypesCount - 1] & ExtendedGeneralPath.SEG_START) != 0));
\r
141 public int PointCount
\r
144 return CoordsCount / 2;
\r
148 public PathData PathData
\r
152 if (_pathData == null)
\r
153 _pathData = GetPathData ();
\r
159 #endregion // Properties
\r
165 private void ClearCache ()
\r
168 _generalPath = null;
\r
171 private GeneralPath GetGeneralPath ()
\r
173 PathIterator iter = getPathIterator (null);
\r
174 GeneralPath path = new GeneralPath ();
\r
175 path.append (iter, false);
\r
179 private PathData GetPathData ()
\r
181 PathData pathData = new PathData();
\r
182 pathData.Types = new byte [PointCount];
\r
183 pathData.Points = new PointF [PointCount];
\r
189 for (int i = 0; i < TypesCount; i++) {
\r
190 sbyte segmentType = (sbyte)(Types [i] & SEG_MASK);
\r
192 // set the masks and the markers
\r
193 marker = ((Types [i] & SEG_MARKER) != 0) ? (byte)PathPointType.PathMarker : (byte)0;
\r
194 start = ((Types [i] & SEG_START) != 0);
\r
196 switch (segmentType) {
\r
198 pathData.InternalTypes [tpos - 1] = (byte) (pathData.InternalTypes [tpos - 1] | (byte) PathPointType.CloseSubpath | marker);
\r
201 pathData.InternalTypes [tpos++] = (byte)((byte) PathPointType.Start | marker);
\r
202 pathData.InternalPoints [ppos++] = new PointF (Coords [cpos++], Coords [cpos++]);
\r
205 pathData.InternalTypes [tpos++] = (byte) ((byte) PathPointType.Line | marker);
\r
206 pathData.InternalPoints [ppos++] = new PointF (Coords [cpos++], Coords [cpos++]);
\r
209 // FIXME : use 4 cp , two of which
\r
210 pathData.InternalTypes [tpos++] = (byte)(byte) PathPointType.Bezier;
\r
211 pathData.InternalPoints [ppos++] = new PointF (Coords [cpos++], Coords [cpos++]);
\r
212 pathData.InternalTypes [tpos++] = (byte) ((byte)PathPointType.Bezier | marker);
\r
213 pathData.InternalPoints [ppos++] = new PointF (Coords [cpos++], Coords [cpos++]);
\r
216 pathData.InternalTypes [tpos++] = (byte)(byte) PathPointType.Bezier3;
\r
217 pathData.InternalPoints [ppos++] = new PointF (Coords [cpos++], Coords [cpos++]);
\r
218 pathData.InternalTypes [tpos++] = (byte) PathPointType.Bezier3;
\r
219 pathData.InternalPoints [ppos++] = new PointF (Coords [cpos++], Coords [cpos++]);
\r
220 pathData.InternalTypes [tpos++] = (byte) ((byte)PathPointType.Bezier3 | marker);
\r
221 pathData.InternalPoints [ppos++] = new PointF (Coords [cpos++], Coords [cpos++]);
\r
228 #endregion // CachedData
\r
230 public void append(Shape s)
\r
232 append (s, !LastFigureClosed);
\r
235 #region GeneralPath
\r
237 public void append(PathIterator pi, bool connect)
\r
240 float [] coords = new float [6];
\r
241 while (!pi.isDone ()) {
\r
242 switch (pi.currentSegment (coords)) {
\r
244 if (!connect || _typesCount < 1 || _coordsCount < 2) {
\r
245 moveTo (coords [0], coords [1]);
\r
248 if (_types [_typesCount - 1] != SEG_CLOSE &&
\r
249 _coords [_coordsCount - 2] == coords [0] &&
\r
250 _coords [_coordsCount - 1] == coords [1])
\r
252 goto case SEG_LINETO;
\r
254 lineTo (coords [0], coords [1]);
\r
257 quadTo (coords [0], coords [1], coords [2], coords [3]);
\r
260 curveTo (coords [0], coords [1], coords [2], coords [3], coords [4], coords [5]);
\r
271 public void append(Shape s, bool connect)
\r
273 PathIterator pi = s.getPathIterator (null);
\r
274 append (pi,connect);
\r
277 public object Clone()
\r
279 ExtendedGeneralPath copy = (ExtendedGeneralPath)MemberwiseClone ();
\r
280 copy._types = (sbyte []) _types.Clone ();
\r
281 copy._coords = (float []) _coords.Clone ();
\r
285 public void closePath()
\r
288 if (_typesCount == 0 || _types[_typesCount - 1] != SEG_CLOSE) {
\r
289 needRoom (1, 0, true);
\r
290 _types [_typesCount++] = SEG_CLOSE;
\r
294 public bool contains(double x, double y)
\r
296 return GeneralPath.contains (x, y);
\r
299 public bool contains(double x, double y, double w, double h)
\r
301 return GeneralPath.contains (x, y, w, h);
\r
304 public bool contains(Point2D p)
\r
306 return contains (p.getX (), p.getY ());
\r
309 public bool contains(Rectangle2D r)
\r
311 return contains (r.getX (), r.getY (), r.getWidth (), r.getHeight ());
\r
314 public Shape createTransformedShape(AffineTransform at)
\r
316 ExtendedGeneralPath gp = (ExtendedGeneralPath) Clone ();
\r
323 public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3)
\r
326 needRoom (1, 6, true);
\r
327 _types [_typesCount++] = SEG_CUBICTO;
\r
328 _coords [_coordsCount++] = x1;
\r
329 _coords [_coordsCount++] = y1;
\r
330 _coords [_coordsCount++] = x2;
\r
331 _coords [_coordsCount++] = y2;
\r
332 _coords [_coordsCount++] = x3;
\r
333 _coords [_coordsCount++] = y3;
\r
336 public java.awt.Rectangle getBounds()
\r
338 return getBounds2D ().getBounds ();
\r
341 public Rectangle2D getBounds2D()
\r
343 float x1, y1, x2, y2;
\r
344 int i = _coordsCount;
\r
346 y1 = y2 = _coords [--i];
\r
347 x1 = x2 = _coords [--i];
\r
349 float y = _coords [--i];
\r
350 float x = _coords [--i];
\r
351 if (x < x1) x1 = x;
\r
352 if (y < y1) y1 = y;
\r
353 if (x > x2) x2 = x;
\r
354 if (y > y2) y2 = y;
\r
358 x1 = y1 = x2 = y2 = 0f;
\r
360 return new Rectangle2D.Float (x1, y1, x2 - x1, y2 - y1);
\r
363 public Point2D getCurrentPoint()
\r
365 if (_typesCount < 1 || _coordsCount < 2)
\r
368 int index = _coordsCount;
\r
369 if (_types [_typesCount - 1] == SEG_CLOSE)
\r
370 for (int i = _typesCount - 2; i > 0; i--) {
\r
371 switch (_types [i]) {
\r
390 return new Point2D.Float (_coords [index - 2], _coords [index - 1]);
\r
393 public PathIterator getPathIterator(AffineTransform at) {
\r
394 return new GeneralPathIterator (this, at);
\r
397 public PathIterator getPathIterator(AffineTransform at, double flatness) {
\r
398 return new FlatteningPathIterator (getPathIterator (at), flatness);
\r
401 public int getWindingRule()
\r
403 return _windingRule;
\r
406 public bool intersects(double x, double y, double w, double h)
\r
408 return GeneralPath.intersects (x, y, w, h);
\r
411 public bool intersects(Rectangle2D r)
\r
413 return intersects (r.getX (), r.getY (), r.getWidth (), r.getHeight ());
\r
416 public void lineTo(float x, float y)
\r
419 needRoom (1, 2, true);
\r
420 _types [_typesCount++] = SEG_LINETO;
\r
421 _coords [_coordsCount++] = x;
\r
422 _coords [_coordsCount++] = y;
\r
425 public void moveTo(float x, float y)
\r
428 if (_typesCount > 0 && _types [_typesCount - 1] == SEG_MOVETO) {
\r
429 _coords [_coordsCount - 2] = x;
\r
430 _coords [_coordsCount - 1] = y;
\r
433 needRoom (1, 2, false);
\r
434 _types [_typesCount++] = SEG_MOVETO;
\r
435 _coords [_coordsCount++] = x;
\r
436 _coords [_coordsCount++] = y;
\r
440 public void quadTo(float x1, float y1, float x2, float y2)
\r
443 needRoom (1, 4, true);
\r
444 _types [_typesCount++] = SEG_QUADTO;
\r
445 _coords [_coordsCount++] = x1;
\r
446 _coords [_coordsCount++] = y1;
\r
447 _coords [_coordsCount++] = x2;
\r
448 _coords [_coordsCount++] = y2;
\r
451 public void reset()
\r
458 public void setWindingRule(int rule)
\r
460 if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
\r
461 throw new IllegalArgumentException ("winding rule must be WIND_EVEN_ODD or WIND_NON_ZERO");
\r
463 _windingRule = rule;
\r
466 public void transform(AffineTransform at)
\r
469 at.transform (_coords, 0, _coords, 0, _coordsCount/2);
\r
472 private void needRoom(int newTypes, int newCoords, bool needMove)
\r
474 if (needMove && _typesCount == 0)
\r
475 throw new IllegalPathStateException ("missing initial moveto in path definition");
\r
477 int size = _coords.Length;
\r
478 if (_coordsCount + newCoords > size) {
\r
480 if (grow > EXPAND_MAX * 2)
\r
481 grow = EXPAND_MAX * 2;
\r
483 if (grow < newCoords)
\r
486 float [] arr = new float [size + grow];
\r
487 Array.Copy (_coords, 0, arr, 0, _coordsCount);
\r
490 size = _types.Length;
\r
491 if (_typesCount + newTypes > size) {
\r
493 if (grow > EXPAND_MAX)
\r
496 if (grow < newTypes)
\r
499 sbyte [] arr = new sbyte [size + grow];
\r
500 Array.Copy (_types, 0, arr, 0, _typesCount);
\r
505 #endregion // GeneralPath
\r
507 public void SetMarkers()
\r
510 if (TypesCount > 0)
\r
511 Types [ TypesCount - 1] |= SEG_MARKER;
\r
514 public void ClearMarkers()
\r
517 for (int i = 0; i < TypesCount; i++)
\r
518 Types [i] &= ~SEG_MARKER;
\r
521 public void StartFigure ()
\r
524 if (TypesCount > 0)
\r
525 Types [TypesCount - 1] |= ExtendedGeneralPath.SEG_START;
\r
528 private void Reset (int initialTypes, int initialCoords)
\r
531 _types = new sbyte [initialTypes];
\r
532 _coords = new float [initialCoords * 2];
\r
537 internal void Clear ()
\r
539 Reset (INIT_SIZE, INIT_SIZE);
\r
542 internal void Reverse ()
\r
545 // revert coordinates
\r
546 for (int i=0, max = CoordsCount / 2; i < max;) {
\r
549 int rix = CoordsCount - i;
\r
551 float tmpx = Coords [ix];
\r
552 float tmpy = Coords [iy];
\r
553 Coords [ix] = Coords [rix];
\r
554 Coords [iy] = Coords [riy];
\r
555 Coords [rix] = tmpx;
\r
556 Coords [riy] = tmpy;
\r
560 sbyte [] newTypes = new sbyte [TypesCount];
\r
562 int newIdx = TypesCount - 1;
\r
567 sbyte closeMask = 0;
\r
568 bool closedFigure = false;
\r
570 while (oldIdx < TypesCount) {
\r
571 // start copying after moveto
\r
572 copyStart = ++oldIdx;
\r
573 // continue to the next figure start
\r
574 while ((Types [oldIdx] != SEG_MOVETO) && (oldIdx < TypesCount))
\r
577 copyEnd = oldIdx - 1;
\r
578 // check whenever current figure is closed
\r
579 if ((Types [oldIdx - 1] & SEG_CLOSE) != 0) {
\r
580 closedFigure = true;
\r
582 newTypes [newIdx--] = (sbyte)(SEG_CLOSE | mask1);
\r
585 // end copy one cell earlier
\r
587 closeMask = (sbyte)(Types [oldIdx - 1] & (sbyte)SEG_MARKER);
\r
594 // copy reverted "inner" types
\r
595 for(int i = copyStart; i <= copyEnd; i++) {
\r
596 newTypes [newIdx--] = (sbyte)((Types [i] & SEG_MASK) | mask2);
\r
598 mask1 = (sbyte)(Types [i] & (sbyte)SEG_MARKER);
\r
602 newTypes [newIdx--] = SEG_MOVETO;
\r
604 // pass close mask to the nex figure
\r
605 if (closedFigure) {
\r
607 closedFigure = false;
\r
614 public PointF GetLastPoint ()
\r
616 if (CoordsCount == 0)
\r
617 throw new System.ArgumentException ("Invalid parameter used.");
\r
619 return new PointF (Coords [CoordsCount - 2], Coords [CoordsCount - 1]);
\r
622 #endregion //Methods
\r
624 #region Private helpers
\r
627 private void Print()
\r
629 Console.WriteLine ("\n\n");
\r
630 float [] fpoints = _coords;
\r
632 for (int i=0; i < _typesCount; i++) {
\r
633 sbyte type = _types [i];
\r
634 string marker = String.Empty;
\r
635 if ((type & SEG_MARKER) != 0)
\r
636 marker = " | MARKER";
\r
638 switch (type & SEG_MASK) {
\r
640 Console.WriteLine ("CLOSE {0}",marker);
\r
643 Console.WriteLine("{0}{3} ({1},{2})","MOVETO", fpoints[cpos++], fpoints[cpos++], marker);
\r
646 Console.WriteLine("{0}{3} ({1},{2})","LINETO", fpoints[cpos++], fpoints[cpos++], marker);
\r
649 Console.WriteLine("{0}{3} ({1},{2})","QUADTO", fpoints[cpos++], fpoints[cpos++], marker);
\r
650 Console.WriteLine(" ({1},{2})","QUADTO", fpoints[cpos++], fpoints[cpos++]);
\r
653 Console.WriteLine("{0}{3} ({1},{2})","CUBICTO", fpoints[cpos++], fpoints[cpos++], marker);
\r
654 Console.WriteLine(" ({1},{2})","CUBICTO", fpoints[cpos++], fpoints[cpos++]);
\r
655 Console.WriteLine(" ({1},{2})","CUBICTO", fpoints[cpos++], fpoints[cpos++]);
\r
661 #endregion // Private helpers
\r