www.pudn.com > cad-draw.zip > Utils.cs, change:2007-12-17,size:27332b


using System; 
using System.Collections.Generic; 
using System.Text; 
using System.Drawing; 
using System.Drawing.Drawing2D; 
using System.Windows.Forms; 
using System.Xml; 
 
namespace Canvas 
{ 
	public struct UnitPoint 
	{ 
		public static UnitPoint Empty; 
		public static bool operator !=(UnitPoint left, UnitPoint right) 
		{ 
			return !(left == right); 
		} 
		public static bool operator ==(UnitPoint left, UnitPoint right) 
		{ 
			if (left.X == right.X) 
				return left.Y == right.Y; 
			if (left.IsEmpty && right.IsEmpty) // after changing Empty to use NaN this extra check is required 
				return true; 
			return false; 
		} 
		public static UnitPoint operator + (UnitPoint left, UnitPoint right) 
		{ 
			left.X += right.X; 
			left.Y += right.Y; 
			return left; 
		} 
		double m_x; 
		double m_y; 
		static UnitPoint() 
		{ 
			Empty = new UnitPoint(); 
			Empty.m_x = double.NaN; 
			Empty.m_y = double.NaN; 
		} 
		public UnitPoint(double x, double y) 
		{ 
			m_x = x; 
			m_y = y; 
		} 
		public UnitPoint(PointF p) 
		{ 
			m_x = p.X; 
			m_y = p.Y; 
		} 
		public bool IsEmpty 
		{ 
			get  
			{ 
				return double.IsNaN(X) && double.IsNaN(Y); 
			} 
		} 
		public double X 
		{ 
			get { return m_x; } 
			set { m_x = value; } 
		} 
		public double Y 
		{ 
			get { return m_y; } 
			set { m_y = value; } 
		} 
		public PointF Point 
		{ 
			get { return new PointF((float)m_x, (float)m_y); } 
		} 
		public override string ToString() 
		{ 
			return string.Format("{{X={0}, Y={1}}}", XmlConvert.ToString(Math.Round(X,8)), XmlConvert.ToString(Math.Round(Y,8))); 
		} 
		public override bool Equals(object obj) 
		{ 
			if (obj is UnitPoint) 
				return (this == (UnitPoint)obj); 
			return false; 
		} 
		public override int GetHashCode() 
		{ 
			return base.GetHashCode(); 
		} 
		public string PosAsString() 
		{ 
			return string.Format("[{0:f4}, {1:f4}]", X, Y); 
		} 
	} 
	public class ScreenUtils 
	{ 
		public static PointF RightPoint(ICanvas canvas, RectangleF unitrect) 
		{ 
			PointF leftpoint = unitrect.Location; 
			float x = leftpoint.X + unitrect.Width; 
			float y = leftpoint.Y + unitrect.Height; 
			return new PointF(x, y); 
		} 
		public static RectangleF ToScreen(ICanvas canvas, RectangleF unitrect) 
		{ 
			RectangleF r = new RectangleF(); 
			r.Location = canvas.ToScreen(new UnitPoint(unitrect.Location)); 
			r.Width = (float)Math.Round(canvas.ToScreen(unitrect.Width)); 
			r.Height = (float)Math.Round(canvas.ToScreen(unitrect.Height)); 
			return r; 
		} 
		public static RectangleF ToUnit(ICanvas canvas, Rectangle screenrect) 
		{ 
			UnitPoint point = canvas.ToUnit(screenrect.Location); 
			SizeF size = new SizeF((float)canvas.ToUnit(screenrect.Width), (float)canvas.ToUnit(screenrect.Height)); 
			RectangleF unitrect = new RectangleF(point.Point, size); 
			return unitrect; 
		} 
		public static RectangleF ToScreenNormalized(ICanvas canvas, RectangleF unitrect) 
		{ 
			RectangleF r = ToScreen(canvas, unitrect); 
			r.Y = r.Y - r.Height; 
			return r; 
		} 
		public static RectangleF ToUnitNormalized(ICanvas canvas, Rectangle screenrect) 
		{ 
			UnitPoint point = canvas.ToUnit(screenrect.Location); 
			SizeF size = new SizeF((float)canvas.ToUnit(screenrect.Width), (float)canvas.ToUnit(screenrect.Height)); 
			RectangleF unitrect = new RectangleF(point.Point, size); 
			unitrect.Y = unitrect.Y - unitrect.Height; //  
			return unitrect; 
		} 
		public static Rectangle ConvertRect(RectangleF r) 
		{ 
			return new Rectangle((int)r.Left, (int)r.Top, (int)r.Width, (int)r.Height); 
		} 
		public static RectangleF GetRect(UnitPoint p1, UnitPoint p2, double width) 
		{ 
			double x = Math.Min(p1.X, p2.X); 
			double y = Math.Min(p1.Y, p2.Y); 
			double w = Math.Abs(p1.X - p2.X); 
			double h = Math.Abs(p1.Y - p2.Y); 
			RectangleF rect = ScreenUtils.GetRect(x, y, w, h); 
			rect.Inflate((float)width, (float)width); 
			return rect; 
		} 
		public static RectangleF GetRect(double x, double y, double w, double h) 
		{ 
			return new RectangleF((float)x, (float)y, (float)w, (float)h); 
		} 
		public static Point ConvertPoint(PointF p) 
		{ 
			return new Point((int)p.X, (int)p.Y); 
		} 
	} 
	public class HitUtil 
	{ 
		public static bool PointInPoint(UnitPoint p, UnitPoint tp, float tpThresHold) 
		{ 
			if (p.IsEmpty || tp.IsEmpty) 
				return false; 
			if ((p.X < tp.X - tpThresHold) || (p.X > tp.X + tpThresHold)) 
				return false; 
			if ((p.Y < tp.Y - tpThresHold) || (p.Y > tp.Y + tpThresHold)) 
				return false; 
			return true; 
		} 
		 
		public static double Distance(UnitPoint p1, UnitPoint p2) 
		{ 
			return Distance(p1, p2, true); 
		} 
		public static double Distance(UnitPoint p1, UnitPoint p2, bool abs) 
		{ 
			double dx = p1.X - p2.X; 
			double dy = p1.Y - p2.Y; 
			if (abs) 
			{ 
				dx = Math.Abs(dx); 
				dy = Math.Abs(dy); 
			} 
			if (dx == 0) 
				return dy; 
			if (dy == 0) 
				return dx; 
			return Math.Sqrt(Math.Pow(dx, 2) + Math.Pow(dy, 2)); 
		} 
		public static double RadiansToDegrees(double radians) 
		{ 
			return radians * (180 / Math.PI); 
		} 
		public static double DegressToRadians(double degrees) 
		{ 
			return degrees * (Math.PI / 180); 
		} 
 
		public static RectangleF CircleBoundingRect(UnitPoint center, float radius) 
		{ 
			RectangleF r = new RectangleF(center.Point, new SizeF(0, 0)); 
			r.Inflate(radius, radius); 
			return r; 
		} 
		public static bool CircleHitPoint(UnitPoint center, float radius, UnitPoint hitpoint) 
		{ 
			// check bounding rect, this is faster than creating a new rectangle and call r.Contains 
			double leftPoint = center.X - radius; 
			double rightPoint = center.X + radius; 
			if (hitpoint.X < leftPoint || hitpoint.X > rightPoint) 
				return false; 
 
			double bottomPoint = center.Y - radius; 
			double topPoint = center.Y + radius; 
			if (hitpoint.Y < bottomPoint || hitpoint.Y > topPoint) 
				return false; 
 
			return true; 
		} 
		public static bool CircleIntersectWithLine(UnitPoint center, float radius, UnitPoint lp1, UnitPoint lp2) 
		{ 
			// check if both points are inside the circle, in that case the line does not intersect the circle 
			if ((Distance(center, lp1) < radius && Distance(center, lp2) < radius)) 
				return false; 
 
			// find the nearest point from the line to center, if that point is smaller than radius,  
			// then the line does intersect the circle 
			UnitPoint np = NearestPointOnLine(lp1, lp2, center); 
			double dist = Distance(center, np); 
			if (dist <= radius) 
				return true; 
			return false; 
		} 
		public static bool IsPointInCircle(UnitPoint center, float radius, UnitPoint testpoint, float halflinewidth) 
		{ 
			double dist = Distance(center, testpoint); 
			if ((dist >= radius - halflinewidth) && (dist <= radius + halflinewidth)) 
				return true; 
			return false; 
		} 
		public static UnitPoint NearestPointOnCircle(UnitPoint center, float radius, UnitPoint testpoint, double roundToAngleD) 
		{ 
			double A = LineAngleR(center, testpoint, DegressToRadians(roundToAngleD)); 
			double dx = Math.Cos(A) * radius; 
			double dy = Math.Sin(A) * radius; 
			double dist = Math.Sqrt(Math.Pow(dx,2) + Math.Pow(dy,2)); 
			return new UnitPoint((float)(center.X + dx), (float)(center.Y + dy)); 
		} 
		public static UnitPoint TangentPointOnCircle(UnitPoint center, float radius, UnitPoint testpoint, bool reverse) 
		{ 
			// c = center, p = testpoint, r = radius 
			// tangent point is formed by a rightangled triangle where 'cp' is the hyportenuse  
			// and the 2 legs are 'r' and cr. 
			double h = Math.Sqrt(Math.Pow(center.X - testpoint.X, 2) + Math.Pow(center.Y - testpoint.Y, 2)); 
			double r = radius; 
			if (Math.Abs(h) < r)	// point located inside the circle, no tangent point 
				return UnitPoint.Empty; 
 
			// A1 is the angle from center to testpoint 
			// A2 is the angle from center to testpoint when radius is used as height for rightangled triangle 
			// A3 is the remaining angle between 'cp' line (h) and the short leg of the tangent triangle 
			double A1 = LineAngleR(center, testpoint, 0); 
			double A2 = Math.Asin(r / h); 
			double A3 = Math.Acos(r / h); 
			 
			// The angle for the tangent point is 90 + diff between A1 and A2 
			double A = Math.PI/2 + (A1 - A2); 
			// if reverse, mirror around A1 
			if (reverse) 
				A = A1 - A3; 
			return LineEndpoint(center, A, radius); 
		} 
		public static UnitPoint PointOncircle(UnitPoint center, double radius, double angleR) 
		{ 
			double y = center.Y + Math.Sin(angleR) * radius; 
			double x = center.X + Math.Cos(angleR) * radius; 
			return new UnitPoint((float)x, (float)y); 
		} 
		public static UnitPoint LineEndpoint(UnitPoint lp1, double angleR, double length) 
		{ 
			double x = Math.Cos(angleR) * length; 
			double y = Math.Sin(angleR) * length; 
			return new UnitPoint(lp1.X + (float)x, lp1.Y + (float)y); 
		} 
		public static RectangleF LineBoundingRect(UnitPoint linepoint1, UnitPoint linepoint2, float halflinewidth) 
		{ 
			double x = Math.Min(linepoint1.X, linepoint2.X); 
			double y = Math.Min(linepoint1.Y, linepoint2.Y); 
			double w = Math.Abs(linepoint1.X - linepoint2.X); 
			double h = Math.Abs(linepoint1.Y - linepoint2.Y); 
			RectangleF boundingrect = ScreenUtils.GetRect(x, y, w, h); 
			boundingrect.Inflate(halflinewidth, halflinewidth); 
			return boundingrect; 
		} 
		public static bool IsPointInLine(UnitPoint linepoint1, UnitPoint linepoint2, UnitPoint testpoint, float halflinewidth) 
		{ 
			UnitPoint p1 = linepoint1; 
			UnitPoint p2 = linepoint2; 
			UnitPoint p3 = testpoint; 
 
			// check bounding rect, this is faster than creating a new rectangle and call r.Contains 
			double lineLeftPoint = Math.Min(p1.X, p2.X) - halflinewidth; 
			double lineRightPoint = Math.Max(p1.X, p2.X) + halflinewidth; 
			if (testpoint.X < lineLeftPoint || testpoint.X > lineRightPoint) 
				return false; 
 
			double lineBottomPoint = Math.Min(p1.Y, p2.Y) - halflinewidth; 
			double lineTopPoint = Math.Max(p1.Y, p2.Y) + halflinewidth; 
			if (testpoint.Y < lineBottomPoint || testpoint.Y > lineTopPoint) 
				return false; 
 
			// then check if it hits the endpoint 
			if (CircleHitPoint(p1, halflinewidth, p3)) 
				return true; 
			if (CircleHitPoint(p2, halflinewidth, p3)) 
				return true; 
 
			if (p1.Y == p2.Y) // line is horizontal 
			{ 
				double min = Math.Min(p1.X, p2.X) - halflinewidth; 
				double max = Math.Max(p1.X, p2.X) + halflinewidth; 
				if (p3.X >= min && p3.X <= max) 
					return true; 
				return false; 
			} 
			if (p1.X == p2.X) // line is vertical 
			{ 
				double min = Math.Min(p1.Y, p2.Y) - halflinewidth; 
				double max = Math.Max(p1.Y, p2.Y) + halflinewidth; 
				if (p3.Y >= min && p3.Y <= max) 
					return true; 
				return false; 
			} 
 
			// using COS law 
			// a^2 = b^2 + c^2 - 2bc COS A 
			// A = ACOS ((a^2 - b^2 - c^2) / (-2bc)) 
			double xdiff = Math.Abs(p2.X - p3.X); 
			double ydiff = Math.Abs(p2.Y - p3.Y); 
			double aSquare = Math.Pow(xdiff, 2) + Math.Pow(ydiff, 2); 
			double a = Math.Sqrt(aSquare); 
 
			xdiff = Math.Abs(p1.X - p2.X); 
			ydiff = Math.Abs(p1.Y - p2.Y); 
			double bSquare = Math.Pow(xdiff, 2) + Math.Pow(ydiff, 2); 
			double b = Math.Sqrt(bSquare); 
 
			xdiff = Math.Abs(p1.X - p3.X); 
			ydiff = Math.Abs(p1.Y - p3.Y); 
			double cSquare = Math.Pow(xdiff, 2) + Math.Pow(ydiff, 2); 
			double c = Math.Sqrt(cSquare); 
			double A = Math.Acos(((aSquare - bSquare - cSquare) / (-2 * b * c))); 
 
			// once we have A we can find the height (distance from the line) 
			// SIN(A) = (h / c) 
			// h = SIN(A) * c; 
			double h = Math.Sin(A) * c; 
 
			// now if height is smaller than half linewidth, the hitpoint is within the line 
			return h <= halflinewidth; 
		} 
		private static bool LinesIntersect(UnitPoint lp1, UnitPoint lp2, UnitPoint lp3, UnitPoint lp4, ref double x, ref double y,  
			bool returnpoint,  
			bool extendA, 
			bool extendB) 
		{ 
			// http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ 
			// line a is given by P1 and P2, point of intersect for line a (Pa) and b (Pb) 
			// Pa = P1 + ua ( P2 - P1 ) 
			// Pb = P3 + ub ( P4 - P3 ) 
 
			// ua(x) = ub(x) and ua(y) = ub (y) 
			// x1 + ua (x2 - x1) = x3 + ub (x4 - x3) 
			// y1 + ua (y2 - y1) = y3 + ub (y4 - y3) 
 
			// ua = ((x4-x3)(y1-y3) - (y4-y3)(x1-x3)) / ((x4-x3)(x2-x1) - (x4-x3)(y2-y1)) 
			// ub = ((x2-x1)(y1-y3) - (y2-y1)(x1-x3)) / ((y4-y3)(x2-x1) - (x4-x3)(y2-y1)) 
 
			// intersect point x = x1 + ua (x2 - x1) 
			// intersect point y = y1 + ua (y2 - y1)  
			double x1 = lp1.X; 
			double x2 = lp2.X; 
			double x3 = lp3.X; 
			double x4 = lp4.X; 
			double y1 = lp1.Y; 
			double y2 = lp2.Y; 
			double y3 = lp3.Y; 
			double y4 = lp4.Y; 
 
			double denominator = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)); 
			if (denominator == 0) // lines are parallel 
				return false; 
			double numerator_ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)); 
			double numerator_ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)); 
			double ua = numerator_ua / denominator; 
			double ub = numerator_ub / denominator; 
			// if a line is not extended then ua (or ub) must be between 0 and 1 
			if (extendA == false) 
			{ 
				if (ua < 0 || ua > 1) 
					return false; 
			} 
			if (extendB == false) 
			{ 
				if (ub < 0 || ub > 1) 
					return false; 
			} 
			if (extendA || extendB) // no need to chck range of ua and ub if check is one on lines  
			{ 
				x = x1 + ua * (x2 - x1); 
				y = y1 + ua * (y2 - y1); 
				return true; 
			} 
			if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) 
			{ 
				if (returnpoint) 
				{ 
					x = x1 + ua * (x2 - x1); 
					y = y1 + ua * (y2 - y1); 
				} 
				return true; 
			} 
			return false; 
		} 
		public static bool LinesIntersect(UnitPoint lp1, UnitPoint lp2, UnitPoint lp3, UnitPoint lp4) 
		{ 
			double x = 0; 
			double y = 0; 
			return LinesIntersect(lp1, lp2, lp3, lp4, ref x, ref y, false, false, false); 
		} 
		public static UnitPoint LinesIntersectPoint(UnitPoint lp1, UnitPoint lp2, UnitPoint lp3, UnitPoint lp4) 
		{ 
			double x = 0; 
			double y = 0; 
			if (LinesIntersect(lp1, lp2, lp3, lp4, ref x, ref y, true, false, false)) 
				return new UnitPoint(x, y); 
			return UnitPoint.Empty; 
		} 
		public static UnitPoint FindApparentIntersectPoint(UnitPoint lp1, UnitPoint lp2, UnitPoint lp3, UnitPoint lp4) 
		{ 
			double x = 0; 
			double y = 0; 
			if (LinesIntersect(lp1, lp2, lp3, lp4, ref x, ref y, true, true, true)) 
				return new UnitPoint(x, y); 
			return UnitPoint.Empty; 
		} 
		public static UnitPoint FindApparentIntersectPoint(UnitPoint lp1, UnitPoint lp2, UnitPoint lp3, UnitPoint lp4, bool extendA, bool extendB) 
		{ 
			double x = 0; 
			double y = 0; 
			if (LinesIntersect(lp1, lp2, lp3, lp4, ref x, ref y, true, extendA, extendB)) 
				return new UnitPoint(x, y); 
			return UnitPoint.Empty; 
		} 
		public static bool LineIntersectWithRect(UnitPoint lp1, UnitPoint lp2, RectangleF r) 
		{ 
			if (r.Contains(lp1.Point)) 
				return true; 
			if (r.Contains(lp2.Point)) 
				return true; 
 
			// the rectangle bottom is top in world units and top is bottom!, confused? 
			// check left 
			UnitPoint p3 = new UnitPoint(r.Left, r.Top); 
			UnitPoint p4 = new UnitPoint(r.Left, r.Bottom); 
			if (LinesIntersect(lp1, lp2, p3, p4)) 
				return true; 
			// check bottom 
			p4.Y = r.Top; 
			p4.X = r.Right; 
			if (LinesIntersect(lp1, lp2, p3, p4)) 
				return true; 
			// check right 
			p3.X = r.Right; 
			p3.Y = r.Top; 
			p4.X = r.Right; 
			p4.Y = r.Bottom; 
			if (LinesIntersect(lp1, lp2, p3, p4)) 
				return true; 
			return false; 
		} 
		public static UnitPoint LineMidpoint(UnitPoint lp1, UnitPoint lp2) 
		{ 
			UnitPoint mid = new UnitPoint(); 
			mid.X = (lp1.X + lp2.X) / 2; 
			mid.Y = (lp1.Y + lp2.Y) / 2; 
			return mid; 
		} 
		public static double LineAngleR(UnitPoint lp1, UnitPoint lp2, double roundToAngleR) 
		{ 
			if (lp1.X == lp2.X) 
			{ 
				if (lp1.Y > lp2.Y) 
					return Math.PI * 6/4; 
				if (lp1.Y < lp2.Y) 
					return Math.PI/2; 
				return 0; 
			} 
			// first find angle A 
			double adjacent = lp2.X - lp1.X; 
			double opposite = lp2.Y - lp1.Y; 
			// find angle A 
			double A = Math.Atan(opposite / adjacent); 
			if (adjacent < 0)			// 2nd and 3rd quadrant 
				A += Math.PI;			// + 180 
			if (adjacent > 0 && opposite < 0) // 4th quadrant 
				A += Math.PI * 2;		// +360 
			if (roundToAngleR != 0) 
			{ 
				double roundUnit = Math.Round(A / roundToAngleR); 
				A = roundToAngleR * roundUnit; 
			} 
			return A; 
		} 
		public static UnitPoint OrthoPointD(UnitPoint lp1, UnitPoint lp2, double roundToAngleR) 
		{ 
			return OrthoPointR(lp1, lp2, DegressToRadians(roundToAngleR)); 
		} 
		public static UnitPoint OrthoPointR(UnitPoint lp1, UnitPoint lp2, double roundToAngleR) 
		{ 
			return NearestPointOnLine(lp1, lp2, lp2, roundToAngleR); 
		} 
		public static UnitPoint NearestPointOnLine(UnitPoint lp1, UnitPoint lp2, UnitPoint tp) 
		{ 
			return NearestPointOnLine(lp1, lp2, tp, false); 
		} 
		public static UnitPoint NearestPointOnLine(UnitPoint lp1, UnitPoint lp2, UnitPoint tp, bool beyondSegment) 
		{ 
			if (lp1.X == lp2.X) 
			{ 
				// if both points are to below, then choose the points closest to tp.y 
				if (beyondSegment == false && lp1.Y < tp.Y && lp2.Y < tp.Y) 
					return new UnitPoint(lp1.X, Math.Max(lp1.Y, lp2.Y)); 
				// if both points are above, then choose the points closest to tp.y 
				if (beyondSegment == false && lp1.Y > tp.Y && lp2.Y > tp.Y) 
					return new UnitPoint(lp1.X, Math.Min(lp1.Y, lp2.Y)); 
				// else the line is on both sides and pick tp.x as y point 
				return new UnitPoint(lp1.X, tp.Y); 
			} 
			if (lp1.Y == lp2.Y) 
			{ 
				// if both points are to the right, then choose the points closest to tp.x 
				if (beyondSegment == false && lp1.X < tp.X && lp2.X < tp.X) 
					return new UnitPoint(Math.Max(lp1.X, lp2.X), lp1.Y); 
				// if both points are to the left, then choose the points closest to tp.x 
				if (beyondSegment == false && lp1.X > tp.X && lp2.X > tp.X) 
					return new UnitPoint(Math.Min(lp1.X, lp2.X), lp1.Y); 
				// else the line is on both sides and pick tp.x as x point 
				return new UnitPoint(tp.X, lp1.Y); 
			} 
			return NearestPointOnLine(lp1, lp2, tp, 0); 
		} 
		private static UnitPoint NearestPointOnLine(UnitPoint lp1, UnitPoint lp2, UnitPoint testpoint, double roundToAngleR) 
		{ 
			if (lp1.X == testpoint.X) 
			{ 
				UnitPoint tmp = lp1; 
				lp1 = lp2; 
				lp2 = tmp; 
			} 
			double A1 = LineAngleR(lp1, testpoint, 0); 
			double A2 = LineAngleR(lp1, lp2, roundToAngleR); 
			double A = A1 - A2; 
			double h1 = (lp1.X - testpoint.X) / Math.Cos(A1); 
			double h2 = Math.Cos(A) * h1; 
			double x = Math.Cos(A2) * h2; 
			double y = Math.Sin(A2) * h2; 
			x = lp1.X - x; 
			y = lp1.Y - y; 
			return new UnitPoint((float)x, (float)y); 
		} 
		public static double LineSlope(UnitPoint p1, UnitPoint p2) 
		{ 
			return (p2.Y - p1.Y) / (p2.X - p1.X); 
		} 
		public static UnitPoint CenterPointFrom3Points(UnitPoint p1, UnitPoint p2, UnitPoint p3) 
		{ 
			// http://local.wasp.uwa.edu.au/~pbourke/geometry/circlefrom3/ 
			// 3 points forms 2 lines a and b, slope of a is ma, b is mb 
			// ma = (y2-y1) / (x2-x1) 
			// mb = (y3-y2) / (x3-x2) 
			double ma = (p2.Y - p1.Y) / (p2.X - p1.X); 
			double mb = (p3.Y - p2.Y) / (p3.X - p2.X); 
			 
			// this is just a hacky work around for when p1-p2 are either horizontal or vertical. 
			// A real solution should be found, but for now this will work 
			if (double.IsInfinity(ma)) 
				ma = 1000000000000; 
			if (double.IsInfinity(mb)) 
				mb = 1000000000000; 
			if (ma == 0) 
				ma = 0.000000000001; 
			if (mb == 0f) 
				mb = 0.000000000001; 
 
			// centerpoint x = mamb(y1-y3) + mb(x1+x2) - ma(x2+x3) / 2(mb-ma) 
			double center_x = (ma * mb * (p1.Y - p3.Y) + mb * (p1.X + p2.X) - ma * (p2.X + p3.X)) / (2 * (mb - ma)); 
			double center_y = (-1 * (center_x - (p1.X + p2.X) / 2) / ma) + ((p1.Y + p2.Y) / 2); 
 
			//m_Center.m_y = -1*(m_Center.x() - (pt1->x()+pt2->x())/2)/aSlope +  (pt1->y()+pt2->y())/2; 
			return new UnitPoint((float)center_x, (float)center_y); 
		} 
	} 
	public class SelectionRectangle 
	{ 
		PointF m_point1; 
		PointF m_point2; 
		public SelectionRectangle(PointF mousedownpoint) 
		{ 
			m_point1 = mousedownpoint; 
			m_point2 = PointF.Empty; 
		} 
		public void Reset() 
		{ 
			m_point2 = PointF.Empty; 
		} 
		public void SetMousePoint(Graphics dc, PointF mousepoint) 
		{ 
			if (m_point2 != PointF.Empty) 
				XorGdi.DrawRectangle(dc, PenStyles.PS_DOT, 1, GetColor(), m_point1, m_point2); 
			m_point2 = mousepoint; 
			XorGdi.DrawRectangle(dc, PenStyles.PS_DOT, 1, GetColor(), m_point1, m_point2); 
		} 
		public Rectangle ScreenRect() 
		{ 
			float x = Math.Min(m_point1.X, m_point2.X); 
			float y = Math.Min(m_point1.Y, m_point2.Y); 
			float w = Math.Abs(m_point1.X - m_point2.X); 
			float h = Math.Abs(m_point1.Y - m_point2.Y); 
			if (m_point2 == PointF.Empty) 
				return Rectangle.Empty; 
			if (w < 4 || h < 4) // if no selection was made return empty rectangle (giving a 4 pixel threshold) 
				return Rectangle.Empty; 
			return new Rectangle((int)x, (int)y, (int)w, (int)h); 
		} 
		public RectangleF Selection(ICanvas canvas) 
		{ 
			Rectangle screenRect = ScreenRect(); 
			if (screenRect.IsEmpty) 
				return RectangleF.Empty; 
			return ScreenUtils.ToUnitNormalized(canvas, screenRect); 
		} 
		public bool AnyPoint() 
		{ 
			return (m_point1.X > m_point2.X); 
		} 
		private Color GetColor() 
		{ 
			if (AnyPoint()) 
				return Color.Blue; 
			return Color.Green; 
		} 
	} 
	public class MoveHelper 
	{ 
		List<IDrawObject> m_originals = new List<IDrawObject>(); 
		List<IDrawObject> m_copies = new List<IDrawObject>(); 
		UnitPoint m_originPoint = UnitPoint.Empty; 
		UnitPoint m_lastPoint = UnitPoint.Empty; 
		CanvasCtrl m_canvas; 
		public UnitPoint OriginPoint 
		{ 
			get { return m_originPoint; } 
		} 
		public UnitPoint LastPoint 
		{ 
			get { return m_lastPoint; } 
		} 
		public IEnumerable<IDrawObject> Copies 
		{ 
			get { return m_copies; } 
		} 
		public MoveHelper(CanvasCtrl canvas) 
		{ 
			m_canvas = canvas; 
		} 
		public bool IsEmpty 
		{ 
			get { return m_copies.Count == 0; } 
		} 
		public bool HandleMouseMoveForMove(UnitPoint mouseunitpoint) 
		{ 
			if (m_originals.Count == 0) 
				return false; 
			double x = mouseunitpoint.X - m_lastPoint.X; 
			double y = mouseunitpoint.Y - m_lastPoint.Y; 
			UnitPoint offset = new UnitPoint(x, y); 
			m_lastPoint = mouseunitpoint; 
			foreach (IDrawObject obj in m_copies) 
				obj.Move(offset); 
			m_canvas.DoInvalidate(true); 
			return true; 
		} 
		public void HandleCancelMove() 
		{ 
			foreach (IDrawObject obj in m_originals) 
				m_canvas.Model.AddSelectedObject(obj); 
			m_originals.Clear(); 
			m_copies.Clear(); 
			m_canvas.DoInvalidate(true); 
		} 
		public void HandleMouseDownForMove(UnitPoint mouseunitpoint, ISnapPoint snappoint) 
		{ 
			UnitPoint p = mouseunitpoint; 
			if (snappoint != null) 
				p = snappoint.SnapPoint; 
 
			if (m_originals.Count == 0) // first step of move 
			{ 
				foreach (IDrawObject obj in m_canvas.Model.SelectedObjects) 
				{ 
					m_originals.Add(obj); 
					m_copies.Add(obj.Clone()); 
				} 
				m_canvas.Model.ClearSelectedObjects(); 
				m_originPoint = p; 
				m_lastPoint = p; 
			} 
			else // move complete 
			{ 
				double x = p.X - m_originPoint.X; 
				double y = p.Y - m_originPoint.Y; 
				UnitPoint offset = new UnitPoint(x, y); 
 
				// do copy 
				if ((Control.ModifierKeys & Keys.Control) == Keys.Control) 
				{ 
					m_canvas.Model.CopyObjects(offset, m_originals); 
				} 
				else 
				{ 
					// do move 
					m_canvas.Model.MoveObjects(offset, m_originals); 
					foreach (IDrawObject obj in m_originals) 
						m_canvas.Model.AddSelectedObject(obj); 
				} 
				m_originals.Clear(); 
				m_copies.Clear(); 
 
			} 
			m_canvas.DoInvalidate(true); 
		} 
		public void DrawObjects(ICanvas canvas, RectangleF r) 
		{ 
			if (m_copies.Count == 0) 
				return; 
			canvas.Graphics.DrawLine(Pens.Wheat, canvas.ToScreen(OriginPoint), canvas.ToScreen(LastPoint)); 
			foreach (IDrawObject obj in Copies) 
				obj.Draw(canvas, r); 
		} 
	} 
	public class NodeMoveHelper 
	{ 
		List<INodePoint> m_nodes = new List<INodePoint>(); 
		UnitPoint m_originPoint = UnitPoint.Empty; 
		UnitPoint m_lastPoint = UnitPoint.Empty; 
		CanvasWrapper m_canvas; 
		public bool IsEmpty 
		{ 
			get { return m_nodes.Count == 0; } 
		} 
		public NodeMoveHelper(CanvasWrapper canvas) 
		{ 
			m_canvas = canvas; 
		} 
		public RectangleF HandleMouseMoveForNode(UnitPoint mouseunitpoint) 
		{ 
			RectangleF r = RectangleF.Empty; 
			if (m_nodes.Count == 0) 
				return r; 
			r = new RectangleF(m_originPoint.Point, new Size(0,0)); 
			r = RectangleF.Union(r, new RectangleF(mouseunitpoint.Point, new SizeF(0,0))); 
			if (m_lastPoint != UnitPoint.Empty) 
				r = RectangleF.Union(r, new RectangleF(m_lastPoint.Point, new SizeF(0, 0))); 
			 
			m_lastPoint = mouseunitpoint; 
			foreach (INodePoint p in m_nodes) 
			{ 
				if (r == RectangleF.Empty) 
					r = p.GetClone().GetBoundingRect(m_canvas); 
				else 
					r = RectangleF.Union(r, p.GetClone().GetBoundingRect(m_canvas)); 
				p.SetPosition(mouseunitpoint); 
				//m_canvas.RepaintObject(p.GetClone()); 
			} 
			return r; 
		} 
		public bool HandleMouseDown(UnitPoint mouseunitpoint, ref bool handled) 
		{ 
			handled = false; 
			if (m_nodes.Count == 0) // no nodes selected yet 
			{ 
				if (m_canvas.Model.SelectedCount > 0) 
				{ 
					foreach (IDrawObject obj in m_canvas.Model.SelectedObjects) 
					{ 
						INodePoint p = obj.NodePoint(m_canvas, mouseunitpoint); 
						if (p != null) 
							m_nodes.Add(p); 
					} 
				} 
				handled = m_nodes.Count > 0; 
				if (handled) 
					m_originPoint = mouseunitpoint; 
				return handled; 
			} 
			// update selected nodes 
			m_canvas.Model.MoveNodes(mouseunitpoint, m_nodes); 
			m_nodes.Clear(); 
			handled = true; 
			m_canvas.CanvasCtrl.DoInvalidate(true); 
			return handled; 
		} 
		public void HandleCancelMove() 
		{ 
			foreach (INodePoint p in m_nodes) 
				p.Cancel(); 
 
			m_nodes.Clear(); 
		} 
		public void DrawOriginalObjects(ICanvas canvas, RectangleF r) 
		{ 
			foreach (INodePoint node in m_nodes) 
				node.GetOriginal().Draw(canvas, r); 
		} 
		public void DrawObjects(ICanvas canvas, RectangleF r) 
		{ 
			if (m_nodes.Count == 0) 
				return; 
			//canvas.Graphics.DrawLine(Pens.Wheat, canvas.ToScreen(m_originPoint), canvas.ToScreen(m_lastPoint)); 
			foreach (INodePoint node in m_nodes) 
				node.GetClone().Draw(canvas, r); 
		} 
		public void OnKeyDown(ICanvas canvas, KeyEventArgs keyevent) 
		{ 
			foreach (INodePoint nodepoint in m_nodes) 
			{ 
				nodepoint.OnKeyDown(canvas, keyevent); 
				if (keyevent.Handled) 
					return; 
 
				IDrawObject drawobject = nodepoint.GetClone(); 
				if (drawobject == null) 
					continue; 
				drawobject.OnKeyDown(canvas, keyevent); 
			} 
		} 
	} 
	public class Utils 
	{ 
		public static IDrawObject FindObjectTypeInList(IDrawObject caller, List<IDrawObject> list, Type type) 
		{ 
			foreach (IDrawObject obj in list) 
			{ 
				if (object.ReferenceEquals(caller, obj)) 
					continue; 
				if (obj.GetType() == type) 
					return obj; 
			} 
			return null; 
		} 
	} 
}