using MvvmScaffoldFrame48.Model.StorageModel.ImageAlgorithm;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace MvvmScaffoldFrame48.DLL.ImageAlgorithm
{
public class BoundRectangleClass
{
///
/// 凸包点集合获取
///
/// 点集合
/// 凸包点集合
public List ConvexHull(List points)
{
points = points.OrderBy(o => o.X).ThenBy(o => o.Y).ToList();
var lower = new List();
foreach (var p in points)
{
while (lower.Count >= 2 && Cross(lower[lower.Count - 2], lower[lower.Count - 1], p) <= 0)
lower.RemoveAt(lower.Count - 1);
lower.Add(p);
}
var upper = new List();
for (int i = points.Count - 1; i >= 0; i--)
{
var p = points[i];
while (upper.Count >= 2 && Cross(upper[upper.Count - 2], upper[upper.Count - 1], p) <= 0)
upper.RemoveAt(upper.Count - 1);
upper.Add(p);
}
lower.RemoveAt(lower.Count - 1);
upper.RemoveAt(upper.Count - 1);
lower.AddRange(upper);
return lower;
}
///
/// 凸包最长长度
///
///
///
public BoundingRectangleMdoel RotatingCalipers(List hull)
{
BoundingRectangleMdoel result = null;
int n = hull.Count;
if (n == 1)
{
return result;
}
if (n == 2)
{
result = new BoundingRectangleMdoel
{
points = hull,
CenterPoint = new Point() { X = (hull[1].X + hull[0].X)/2, Y = (hull[1].Y + hull[0].Y)/2 },
Width = 0,
Height = Distance(hull[0], hull[1]),
Angle = Math.Atan2(hull[1].Y - hull[0].Y, hull[1].X - hull[0].X)
};
return result;
}
double maxDist = 0;
int pointIndex1 = 0;
int pointIndex2 = 0;
for (int i = 0; i < n; i++)
{
for (int j = i + 1; j < n; j++)
{
if (Distance(hull[i], hull[j]) > maxDist)
{
maxDist = Distance(hull[i], hull[j]);
pointIndex1 = i;
pointIndex2 = j;
}
}
}
double angle = Math.Atan2(hull[pointIndex2].Y - hull[pointIndex1].Y, hull[pointIndex2].X - hull[pointIndex2].X);
var rotatedPoints = hull.Select(p => RotatePoint(p, -angle)).ToList();
// 找到包围盒
int minX = rotatedPoints.Min(p => p.X);
int maxX = rotatedPoints.Max(p => p.X);
int minY = rotatedPoints.Min(p => p.Y);
int maxY = rotatedPoints.Max(p => p.Y);
int centerX = (minX + maxX) / 2;
int centerY = (minY + maxY) / 2;
int width = maxX - minX;
int height = maxY - minY;
// 构造矩形的四个顶点并旋转回原来的角度
var rectangle = new List
{
RotatePoint(new Point(minX, minY), angle),
RotatePoint(new Point(maxX, minY), angle),
RotatePoint(new Point(maxX, maxY), angle),
RotatePoint(new Point(minX, maxY), angle)
};
result = new BoundingRectangleMdoel
{
points = rectangle,
CenterPoint = RotatePoint(new Point() { X = centerX, Y = centerY }, angle),
Width = width,
Height = height,
Angle = angle
};
return result;
}
///
/// 计算凸包的最小外接矩形
///
/// 凸包顶点列表(按逆时针顺序排列)
/// 最小外接矩形的四个顶点
public List CalculateMinimumBoundingRectangle(List convexHull)
{
if (convexHull == null || convexHull.Count < 3)
return null;
double minArea = double.MaxValue;
List minRectangle = null;
int n = convexHull.Count;
// 遍历每一条边作为基准边
for (int i = 0; i < n; i++)
{
Point edgeStart = convexHull[i];
Point edgeEnd = convexHull[(i + 1) % n];
// 计算当前边的角度
double angle = Math.Atan2(edgeEnd.Y - edgeStart.Y, edgeEnd.X - edgeStart.X);
// 将所有点绕原点旋转-angle角度,使当前边与x轴平行
var rotatedPoints = convexHull.Select(p => RotatePoint(p, -angle)).ToList();
// 找到包围盒
int minX = rotatedPoints.Min(p => p.X);
int maxX = rotatedPoints.Max(p => p.X);
int minY = rotatedPoints.Min(p => p.Y);
int maxY = rotatedPoints.Max(p => p.Y);
// 计算面积
double area = (maxX - minX) * (maxY - minY);
// 如果面积更小,则更新最小矩形
if (area < minArea)
{
minArea = area;
// 构造矩形的四个顶点并旋转回原来的角度
var rectangle = new List
{
RotatePoint(new Point(minX, minY), angle),
RotatePoint(new Point(maxX, minY), angle),
RotatePoint(new Point(maxX, maxY), angle),
RotatePoint(new Point(minX, maxY), angle)
};
minRectangle = rectangle;
}
}
return minRectangle;
}
///
/// 绕原点旋转点
///
/// 待旋转的点
/// 旋转角度(弧度)
/// 旋转后的点
private static Point RotatePoint(Point point, double angle)
{
double cos = Math.Cos(angle);
double sin = Math.Sin(angle);
return new Point(
(int)(point.X * cos - point.Y * sin),
(int)(point.X * sin + point.Y * cos)
);
}
///
/// 计算点到线段的距离
///
/// 线段点1
/// 线段点2
/// 点三
///
private double DistanceToLine(Point a, Point b, Point c)
{
double area = Math.Abs(Area2(a, b, c));
double baseLength = Distance(a, b);
return area / baseLength;
}
///
/// 计算向量叉积
///
///
///
///
///
private int Cross(Point o, Point a, Point b) =>
(a.X - o.X) * (b.Y - o.Y) - (a.Y - o.Y) * (b.X - o.X);
///
/// 计算三角形面积的两倍
///
///
///
///
///
private int Area2(Point a, Point b, Point c) =>
(b.X - a.X) * (c.Y - a.Y) - (b.Y - a.Y) * (c.X - a.X);
///
/// 计算两点间距离
///
///
///
///
private double Distance(Point a, Point b)
{
int dx = a.X - b.X;
int dy = a.Y - b.Y;
return Math.Sqrt(dx * dx + dy * dy);
}
}
}