//
// System.Drawing.Drawing2D.ExtendedGeneralPath.cs
//
// Author:
// Bors Kirzner <boris@mainsoft.com>
//
// Copyright (C) 2005 Mainsoft Corporation, (http://www.mainsoft.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using java.awt;
using java.awt.geom;
using java.lang;
namespace System.Drawing.Drawing2D{
internal class ExtendedGeneralPath : Shape, ICloneable
{
#region Fields
public const int WIND_EVEN_ODD = 0; //PathIterator__Finals.WIND_EVEN_ODD;
public const int WIND_NON_ZERO = 1; //PathIterator__Finals.WIND_NON_ZERO;
public const sbyte SEG_MOVETO = 0; //(byte) PathIterator__Finals.SEG_MOVETO;
public const sbyte SEG_LINETO = 1; //(byte) PathIterator__Finals.SEG_LINETO;
public const sbyte SEG_QUADTO = 2; //(byte) PathIterator__Finals.SEG_QUADTO;
public const sbyte SEG_CUBICTO = 3; //(byte) PathIterator__Finals.SEG_CUBICTO;
public const sbyte SEG_CLOSE = 4; //(byte) PathIterator__Finals.SEG_CLOSE;
public const sbyte SEG_START = 16; // segment start
public const sbyte SEG_MASK = SEG_MOVETO | SEG_LINETO | SEG_QUADTO | SEG_CUBICTO | SEG_CLOSE; // mask to eliminate SEG_CLOSE and SEG_MARKER
private const sbyte SEG_MARKER = 32; // path marker
private sbyte [] _types;
private float [] _coords;
private int _typesCount;
private int _coordsCount;
private int _windingRule;
private PathData _pathData;
private GeneralPath _generalPath;
const int INIT_SIZE = 20;
const int EXPAND_MAX = 500;
#endregion // Fileds
#region Constructors
public ExtendedGeneralPath() : this (WIND_NON_ZERO, INIT_SIZE, INIT_SIZE)
{
}
public ExtendedGeneralPath(int rule) : this (rule, INIT_SIZE, INIT_SIZE)
{
}
public ExtendedGeneralPath(int rule, int initialCapacity) : this (rule, initialCapacity, initialCapacity)
{
}
public ExtendedGeneralPath(Shape s) : this(WIND_NON_ZERO, INIT_SIZE, INIT_SIZE)
{
PathIterator pi = s.getPathIterator (null);
setWindingRule (pi.getWindingRule ());
append (pi, false);
}
private ExtendedGeneralPath(int rule, int initialTypes, int initialCoords)
{
setWindingRule(rule);
Reset (initialTypes, initialCoords);
}
#endregion // Constructors
#region Properties
private GeneralPath GeneralPath
{
get {
if (_generalPath == null) {
_generalPath = GetGeneralPath ();
}
return _generalPath;
}
}
public sbyte [] Types
{
get { return _types; }
}
public float [] Coords
{
get { return _coords; }
}
public int TypesCount
{
get { return _typesCount; }
}
public int CoordsCount
{
get { return _coordsCount; }
}
public bool LastFigureClosed
{
get {
return ((TypesCount == 0) ||
((Types [TypesCount - 1] & ExtendedGeneralPath.SEG_CLOSE) != 0) ||
((Types [TypesCount - 1] & ExtendedGeneralPath.SEG_START) != 0));
}
}
public int PointCount
{
get {
return CoordsCount / 2;
}
}
public PathData PathData
{
get
{
if (_pathData == null)
_pathData = GetPathData ();
return _pathData;
}
}
#endregion // Properties
#region Methods
#region CachedData
private void ClearCache ()
{
_pathData = null;
_generalPath = null;
}
private GeneralPath GetGeneralPath ()
{
PathIterator iter = getPathIterator (null);
GeneralPath path = new GeneralPath ();
path.append (iter, false);
return path;
}
private PathData GetPathData ()
{
PathData pathData = new PathData();
int nPts = PointCount;
for (int i = 0; i < TypesCount; i++)
if ((Types [i] & SEG_MASK) == SEG_QUADTO)
nPts++;
pathData.Types = new byte [nPts];
pathData.Points = new PointF [nPts];
int tpos = 0;
int ppos = 0;
int cpos = 0;
byte marker;
bool start;
for (int i = 0; i < TypesCount; i++) {
sbyte segmentType = (sbyte)(Types [i] & SEG_MASK);
// set the masks and the markers
marker = ((Types [i] & SEG_MARKER) != 0) ? (byte)PathPointType.PathMarker : (byte)0;
start = ((Types [i] & SEG_START) != 0);
switch (segmentType) {
case SEG_CLOSE:
pathData.Types [tpos - 1] = (byte) (pathData.Types [tpos - 1] | (byte) PathPointType.CloseSubpath | marker);
break;
case SEG_MOVETO:
pathData.Types [tpos++] = (byte)((byte) PathPointType.Start | marker);
pathData.Points [ppos++] = new PointF (Coords [cpos++], Coords [cpos++]);
break;
case SEG_LINETO:
pathData.Types [tpos++] = (byte) ((byte) PathPointType.Line | marker);
pathData.Points [ppos++] = new PointF (Coords [cpos++], Coords [cpos++]);
break;
case SEG_QUADTO:
/*
.net does not support Quadratic curves, so convert to Cubic according to http://pfaedit.sourceforge.net/bezier.html
The end points of the cubic will be the same as the quadratic's.
CP0 = QP0
CP3 = QP2
The two control points for the cubic are:
CP1 = QP0 + 2/3 *(QP1-QP0)
CP2 = CP1 + 1/3 *(QP2-QP0)
*/
float x0 = Coords[cpos-2]; //QP0
float y0 = Coords[cpos-1]; //QP0
float x1 = x0 + (2/3 * (Coords [cpos++]-x0));
float y1 = y0 + (2/3 * (Coords [cpos++]-y0));
float x3 = Coords [cpos++]; //QP2
float y3 = Coords [cpos++]; //QP2
float x2 = x1 + (1/3 * (x3-x0));
float y2 = y1 + (1/3 * (y3-y0));
pathData.Types [tpos++] = (byte)(byte) PathPointType.Bezier;
pathData.Points [ppos++] = new PointF (x1, y1);
pathData.Types [tpos++] = (byte)(byte) PathPointType.Bezier;
pathData.Points [ppos++] = new PointF (x2, y2);
pathData.Types [tpos++] = (byte) ((byte)PathPointType.Bezier | marker);
pathData.Points [ppos++] = new PointF (x3, y3);
break;
case SEG_CUBICTO:
pathData.Types [tpos++] = (byte)(byte) PathPointType.Bezier3;
pathData.Points [ppos++] = new PointF (Coords [cpos++], Coords [cpos++]);
pathData.Types [tpos++] = (byte) PathPointType.Bezier3;
pathData.Points [ppos++] = new PointF (Coords [cpos++], Coords [cpos++]);
pathData.Types [tpos++] = (byte) ((byte)PathPointType.Bezier3 | marker);
pathData.Points [ppos++] = new PointF (Coords [cpos++], Coords [cpos++]);
break;
}
}
return pathData;
}
#endregion // CachedData
public void append(Shape s)
{
append (s, !LastFigureClosed);
}
#region GeneralPath
public void append(PathIterator pi, bool connect)
{
ClearCache ();
float [] coords = new float [6];
while (!pi.isDone ()) {
switch (pi.currentSegment (coords)) {
case SEG_MOVETO:
if (!connect || _typesCount < 1 || _coordsCount < 2) {
moveTo (coords [0], coords [1]);
break;
}
if (_types [_typesCount - 1] != SEG_CLOSE &&
_coords [_coordsCount - 2] == coords [0] &&
_coords [_coordsCount - 1] == coords [1])
break;
goto case SEG_LINETO;
case SEG_LINETO:
lineTo (coords [0], coords [1]);
break;
case SEG_QUADTO:
quadTo (coords [0], coords [1], coords [2], coords [3]);
break;
case SEG_CUBICTO:
curveTo (coords [0], coords [1], coords [2], coords [3], coords [4], coords [5]);
break;
case SEG_CLOSE:
closePath ();
break;
}
pi.next ();
connect = false;
}
}
public void append(Shape s, bool connect)
{
PathIterator pi = s.getPathIterator (null);
append (pi,connect);
}
public object Clone()
{
ExtendedGeneralPath copy = (ExtendedGeneralPath)MemberwiseClone ();
copy._types = (sbyte []) _types.Clone ();
copy._coords = (float []) _coords.Clone ();
return copy;
}
public void closePath()
{
ClearCache ();
if (_typesCount == 0 || _types[_typesCount - 1] != SEG_CLOSE) {
needRoom (1, 0, true);
_types [_typesCount++] = SEG_CLOSE;
}
}
public bool contains(double x, double y)
{
return GeneralPath.contains (x, y);
}
public bool contains(double x, double y, double w, double h)
{
return GeneralPath.contains (x, y, w, h);
}
public bool contains(Point2D p)
{
return contains (p.getX (), p.getY ());
}
public bool contains(Rectangle2D r)
{
return contains (r.getX (), r.getY (), r.getWidth (), r.getHeight ());
}
public Shape createTransformedShape(AffineTransform at)
{
ExtendedGeneralPath gp = (ExtendedGeneralPath) Clone ();
if (at != null) {
gp.transform (at);
}
return gp;
}
public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3)
{
ClearCache ();
needRoom (1, 6, true);
_types [_typesCount++] = SEG_CUBICTO;
_coords [_coordsCount++] = x1;
_coords [_coordsCount++] = y1;
_coords [_coordsCount++] = x2;
_coords [_coordsCount++] = y2;
_coords [_coordsCount++] = x3;
_coords [_coordsCount++] = y3;
}
public java.awt.Rectangle getBounds()
{
return getBounds2D ().getBounds ();
}
public Rectangle2D getBounds2D()
{
float x1, y1, x2, y2;
int i = _coordsCount;
if (i > 0) {
y1 = y2 = _coords [--i];
x1 = x2 = _coords [--i];
while (i > 0) {
float y = _coords [--i];
float x = _coords [--i];
if (x < x1) x1 = x;
if (y < y1) y1 = y;
if (x > x2) x2 = x;
if (y > y2) y2 = y;
}
}
else {
x1 = y1 = x2 = y2 = 0f;
}
return new Rectangle2D.Float (x1, y1, x2 - x1, y2 - y1);
}
public Point2D getCurrentPoint()
{
if (_typesCount < 1 || _coordsCount < 2)
return null;
int index = _coordsCount;
if (_types [_typesCount - 1] == SEG_CLOSE)
for (int i = _typesCount - 2; i > 0; i--) {
switch (_types [i]) {
case SEG_MOVETO:
//break loop;
goto loopend;
case SEG_LINETO:
index -= 2;
break;
case SEG_QUADTO:
index -= 4;
break;
case SEG_CUBICTO:
index -= 6;
break;
case SEG_CLOSE:
break;
}
}
loopend:
return new Point2D.Float (_coords [index - 2], _coords [index - 1]);
}
public PathIterator getPathIterator(AffineTransform at) {
return new GeneralPathIterator (this, at);
}
public PathIterator getPathIterator(AffineTransform at, double flatness) {
return new FlatteningPathIterator (getPathIterator (at), flatness);
}
public int getWindingRule()
{
return _windingRule;
}
public bool intersects(double x, double y, double w, double h)
{
return GeneralPath.intersects (x, y, w, h);
}
public bool intersects(Rectangle2D r)
{
return intersects (r.getX (), r.getY (), r.getWidth (), r.getHeight ());
}
public void lineTo(float x, float y)
{
ClearCache ();
needRoom (1, 2, true);
_types [_typesCount++] = SEG_LINETO;
_coords [_coordsCount++] = x;
_coords [_coordsCount++] = y;
}
public void moveTo(float x, float y)
{
ClearCache ();
if (_typesCount > 0 && _types [_typesCount - 1] == SEG_MOVETO) {
_coords [_coordsCount - 2] = x;
_coords [_coordsCount - 1] = y;
}
else {
needRoom (1, 2, false);
_types [_typesCount++] = SEG_MOVETO;
_coords [_coordsCount++] = x;
_coords [_coordsCount++] = y;
}
}
public void quadTo(float x1, float y1, float x2, float y2)
{
// restore quadTo as cubic affects quality
ClearCache ();
needRoom (1, 4, true);
_types [_typesCount++] = SEG_QUADTO;
_coords [_coordsCount++] = x1;
_coords [_coordsCount++] = y1;
_coords [_coordsCount++] = x2;
_coords [_coordsCount++] = y2;
}
public void reset()
{
ClearCache ();
_typesCount = 0;
_coordsCount = 0;
}
public void setWindingRule(int rule)
{
if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
throw new IllegalArgumentException ("winding rule must be WIND_EVEN_ODD or WIND_NON_ZERO");
}
_windingRule = rule;
}
public void transform(AffineTransform at)
{
transform(at, 0, CoordsCount);
}
public void transform(AffineTransform at, int startCoord, int numCoords) {
ClearCache ();
at.transform (_coords, startCoord, _coords, startCoord, numCoords/2);
}
private void needRoom(int newTypes, int newCoords, bool needMove)
{
if (needMove && _typesCount == 0)
throw new IllegalPathStateException ("missing initial moveto in path definition");
int size = _coords.Length;
if (_coordsCount + newCoords > size) {
int grow = size;
if (grow > EXPAND_MAX * 2)
grow = EXPAND_MAX * 2;
if (grow < newCoords)
grow = newCoords;
float [] arr = new float [size + grow];
Array.Copy (_coords, 0, arr, 0, _coordsCount);
_coords = arr;
}
size = _types.Length;
if (_typesCount + newTypes > size) {
int grow = size;
if (grow > EXPAND_MAX)
grow = EXPAND_MAX;
if (grow < newTypes)
grow = newTypes;
sbyte [] arr = new sbyte [size + grow];
Array.Copy (_types, 0, arr, 0, _typesCount);
_types = arr;
}
}
#endregion // GeneralPath
public void SetMarkers()
{
ClearCache ();
if (TypesCount > 0)
Types [ TypesCount - 1] |= SEG_MARKER;
}
public void ClearMarkers()
{
ClearCache ();
for (int i = 0; i < TypesCount; i++)
Types [i] &= ~SEG_MARKER;
}
public void StartFigure ()
{
ClearCache ();
if (TypesCount > 0)
Types [TypesCount - 1] |= ExtendedGeneralPath.SEG_START;
}
private void Reset (int initialTypes, int initialCoords)
{
ClearCache ();
_types = new sbyte [initialTypes];
_coords = new float [initialCoords * 2];
_typesCount = 0;
_coordsCount = 0;
}
internal void Clear ()
{
Reset (INIT_SIZE, INIT_SIZE);
}
internal void Reverse ()
{
ClearCache ();
// revert coordinates
for (int i=0, max = CoordsCount / 2; i < max;) {
int ix = i++;
int iy = i++;
int rix = CoordsCount - i;
int riy = rix + 1;
float tmpx = Coords [ix];
float tmpy = Coords [iy];
Coords [ix] = Coords [rix];
Coords [iy] = Coords [riy];
Coords [rix] = tmpx;
Coords [riy] = tmpy;
}
// revert types
sbyte [] newTypes = new sbyte [TypesCount];
int oldIdx = 0;
int newIdx = TypesCount - 1;
int copyStart;
int copyEnd;
sbyte mask1 = 0;
sbyte mask2 = 0;
sbyte closeMask = 0;
bool closedFigure = false;
while (oldIdx < TypesCount) {
// start copying after moveto
copyStart = ++oldIdx;
// continue to the next figure start
while ((Types [oldIdx] != SEG_MOVETO) && (oldIdx < TypesCount))
oldIdx++;
copyEnd = oldIdx - 1;
// check whenever current figure is closed
if ((Types [oldIdx - 1] & SEG_CLOSE) != 0) {
closedFigure = true;
// close figure
newTypes [newIdx--] = (sbyte)(SEG_CLOSE | mask1);
mask1 = 0;
mask2 = 0;
// end copy one cell earlier
copyEnd--;
closeMask = (sbyte)(Types [oldIdx - 1] & (sbyte)SEG_MARKER);
}
else {
mask2 = mask1;
mask1 = 0;
}
// copy reverted "inner" types
for(int i = copyStart; i <= copyEnd; i++) {
newTypes [newIdx--] = (sbyte)((Types [i] & SEG_MASK) | mask2);
mask2 = mask1;
mask1 = (sbyte)(Types [i] & (sbyte)SEG_MARKER);
}
// copy moveto
newTypes [newIdx--] = SEG_MOVETO;
// pass close mask to the nex figure
if (closedFigure) {
mask1 = closeMask;
closedFigure = false;
}
}
_types = newTypes;
}
public PointF GetLastPoint ()
{
if (CoordsCount == 0)
throw new System.ArgumentException ("Invalid parameter used.");
return new PointF (Coords [CoordsCount - 2], Coords [CoordsCount - 1]);
}
#endregion //Methods
#region Private helpers
#if DEBUG
private void Print()
{
Console.WriteLine ("\n\n");
float [] fpoints = _coords;
int cpos = 0;
for (int i=0; i < _typesCount; i++) {
sbyte type = _types [i];
string marker = String.Empty;
if ((type & SEG_MARKER) != 0)
marker = " | MARKER";
switch (type & SEG_MASK) {
case SEG_CLOSE:
Console.WriteLine ("CLOSE {0}",marker);
break;
case SEG_MOVETO:
Console.WriteLine("{0}{3} ({1},{2})","MOVETO", fpoints[cpos++], fpoints[cpos++], marker);
break;
case SEG_LINETO:
Console.WriteLine("{0}{3} ({1},{2})","LINETO", fpoints[cpos++], fpoints[cpos++], marker);
break;
case SEG_QUADTO:
Console.WriteLine("{0}{3} ({1},{2})","QUADTO", fpoints[cpos++], fpoints[cpos++], marker);
Console.WriteLine(" ({1},{2})","QUADTO", fpoints[cpos++], fpoints[cpos++]);
break;
case SEG_CUBICTO:
Console.WriteLine("{0}{3} ({1},{2})","CUBICTO", fpoints[cpos++], fpoints[cpos++], marker);
Console.WriteLine(" ({1},{2})","CUBICTO", fpoints[cpos++], fpoints[cpos++]);
Console.WriteLine(" ({1},{2})","CUBICTO", fpoints[cpos++], fpoints[cpos++]);
break;
}
}
}
#endif
#endregion // Private helpers
}
}
|