1012 lines
36 KiB
C#
1012 lines
36 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Drawing;
|
||
using System.Windows.Forms;
|
||
using System.Xml;
|
||
|
||
namespace CoreUnit.Control.UserControl.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;
|
||
}
|
||
|
||
public static UnitPoint operator -(UnitPoint left, UnitPoint right)
|
||
{
|
||
left.X -= right.X;
|
||
left.Y -= right.Y;
|
||
return left;
|
||
}
|
||
|
||
public static UnitPoint operator /(UnitPoint left, double right)
|
||
{
|
||
left.X /= right;
|
||
left.Y /= right;
|
||
return left;
|
||
}
|
||
|
||
public static UnitPoint operator *(UnitPoint left, double right)
|
||
{
|
||
left.X *= right;
|
||
left.Y *= right;
|
||
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(List<UnitPoint> points, double width)
|
||
{
|
||
double minX = double.MaxValue, minY = double.MaxValue, maxX = double.MinValue, maxY = double.MinValue;
|
||
|
||
foreach (UnitPoint pt in points) {
|
||
minX = Math.Min(pt.X, minX);
|
||
minY = Math.Min(pt.Y, minY);
|
||
|
||
maxX = Math.Max(pt.X, maxX);
|
||
maxY = Math.Max(pt.Y, maxY);
|
||
}
|
||
RectangleF rect = ScreenUtils.GetRect(minX, minY, Math.Abs(maxX - minX), Math.Abs(maxY - minY));//<2F><><EFBFBD><EFBFBD> Rectangle<6C><65> <20><>ǥ<EFBFBD><C7A5><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> canvas<61><73> <20><>ǥ<EFBFBD><C7A5><EFBFBD><EFBFBD> <20>ٸ<EFBFBD> -> Y<><59><EFBFBD><EFBFBD> <20>ݴ<EFBFBD><DDB4>εǾ<CEB5><C7BE><EFBFBD>
|
||
|
||
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 RectangleF GetRect(UnitPoint center, double radius, double width)
|
||
{
|
||
double x = center.X - radius;
|
||
double y = center.Y - radius;
|
||
|
||
RectangleF rect = ScreenUtils.GetRect(x, y, radius * 2, radius * 2);
|
||
rect.Inflate((float)width, (float)width);
|
||
return rect;
|
||
}
|
||
|
||
public static Point ConvertPoint(PointF p)
|
||
{
|
||
return new Point((int)p.X, (int)p.Y);
|
||
}
|
||
}
|
||
|
||
public class HitUtil
|
||
{
|
||
public static UnitPoint RotatePoint(double angle, UnitPoint pt, UnitPoint center)//angle--> <20><>
|
||
{
|
||
float rad = (float)(angle * Math.PI / 180);
|
||
double X = (pt.X - center.X) * Math.Cos(rad) - (pt.Y - center.Y) * Math.Sin(rad) + center.X;
|
||
double Y = (pt.X - center.X) * Math.Sin(rad) + (pt.Y - center.Y) * Math.Cos(rad) + center.Y;
|
||
|
||
return new UnitPoint(X, Y);
|
||
}
|
||
|
||
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 bool IsPointInArc(UnitPoint center, float radius, UnitPoint testpoint, float halflinewidth, RectangleF ClientRect)
|
||
{
|
||
// IsPointInCircle<6C><65> Arc <20><><EFBFBD><EFBFBD> <20>߰<EFBFBD>.
|
||
double dist = Distance(center, testpoint);
|
||
if ((ClientRect.X - halflinewidth <= testpoint.X) && (testpoint.X <= ClientRect.X + ClientRect.Width + halflinewidth) &&
|
||
(ClientRect.Y - halflinewidth <= testpoint.Y) && (testpoint.Y <= ClientRect.Y + ClientRect.Height + halflinewidth) &&
|
||
(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 double AngleROncircle(UnitPoint center, UnitPoint point)
|
||
{
|
||
double dx = point.X - center.X;
|
||
double dy = point.Y - center.Y;
|
||
|
||
/*if(dx == 0)
|
||
{
|
||
if(dy > 0)
|
||
{
|
||
return DegressToRadians(90);
|
||
}
|
||
else if(dy <0)
|
||
{
|
||
return DegressToRadians(270);
|
||
}
|
||
else
|
||
{
|
||
return 0;
|
||
}
|
||
}
|
||
else*/
|
||
{
|
||
double theta = Math.Atan2(dy, dx);
|
||
|
||
// 0 ~360<36><30><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD>Ͻ<EFBFBD>Ű<EFBFBD><C5B0> <20><><EFBFBD>ؼ<EFBFBD>.
|
||
if (-Math.PI <= theta && theta <= 0) {
|
||
theta = theta + 2 * Math.PI;
|
||
}
|
||
|
||
return theta;
|
||
}
|
||
}
|
||
|
||
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>();
|
||
CanvasCtrl m_canvas;
|
||
|
||
UnitPoint m_originPoint = UnitPoint.Empty;
|
||
public UnitPoint OriginPoint {
|
||
get { return m_originPoint; }
|
||
}
|
||
|
||
UnitPoint m_lastPoint = UnitPoint.Empty;
|
||
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, CanvasCtrl.eCommandType commandType)
|
||
{
|
||
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 (commandType == CanvasCtrl.eCommandType.copy)//m_canvas(System.Windows.Forms.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();
|
||
|
||
// <20><><EFBFBD><EFBFBD> <20><> <20>̵<EFBFBD><CCB5><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>Ŀ<EFBFBD><C4BF><EFBFBD> <20><>ü<EFBFBD><C3BC> <20><><EFBFBD>ø<EFBFBD><C3B8><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>ɾ<EFBFBD>Ÿ<EFBFBD><C5B8><EFBFBD><EFBFBD> <20>ʱ<EFBFBD>ȭ <20>Ѵ<EFBFBD>.
|
||
m_canvas.CommandDeselectAll();
|
||
m_canvas.CommandEscape();
|
||
}
|
||
|
||
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;
|
||
}
|
||
}
|
||
}
|