2026-03-30 17:09:29 +09:00

1012 lines
36 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
}
}