//矩形框控制元件新增背景圖片
rockRectControl.BackImage = bitmap;
//宣告一個矩形框,傳入左上角和右下角座標
RockRectangle rect = new RockRectangle();
var p1 = item.DistinguishRegion.LeftTopCorner;
var p2 = item.DistinguishRegion.RightBottomCorner;
rect.Rectangle = Rectangle.FromLTRB((int)p1.X, (int)p1.Y, (int)p2.X, (int)p2.Y);
//把矩形框新增到矩形框控制元件中,可以新增多個矩形
rockRectControl.RockRectangles.Add(rect);
//找到矩形控制元件中某一個矩形框
Rectangle r = rockRectControl.RockRectangles[i].Rectangle;
//直接讀取即可
var rp = new RockRegion();
rp.LeftTopCorner.X = r.X;
rp.LeftTopCorner.Y = r.Y;
rp.RightBottomCorner.X = r.Right;
rp.RightBottomCorner.Y = r.Bottom;
using System.Drawing;
namespace NcModule.Tools;
[Serializable]
public class RockRectangle
{
private List<LittleRectangle> littleRectangles = new List<LittleRectangle>();
public Rectangle Rectangle { set; get; }
internal List<LittleRectangle> GetLittleRectangles()
{
littleRectangles.Clear();
littleRectangles.Add(new LittleRectangle(Rectangle, PosSizableRect.LeftUp));
littleRectangles.Add(new LittleRectangle(Rectangle, PosSizableRect.LeftMiddle));
littleRectangles.Add(new LittleRectangle(Rectangle, PosSizableRect.LeftBottom));
littleRectangles.Add(new LittleRectangle(Rectangle, PosSizableRect.BottomMiddle));
littleRectangles.Add(new LittleRectangle(Rectangle, PosSizableRect.RightUp));
littleRectangles.Add(new LittleRectangle(Rectangle, PosSizableRect.RightBottom));
littleRectangles.Add(new LittleRectangle(Rectangle, PosSizableRect.RightMiddle));
littleRectangles.Add(new LittleRectangle(Rectangle, PosSizableRect.UpMiddle));
return littleRectangles;
}
public double RotationAngle { set; get; }
}
internal class LittleRectangle
{
//小矩形的寬度
private int rectangleWidth = 8;
/// <summary>
/// 矩形放大的倍數
/// </summary>
public static double Enlarge = 1;
/// <summary>
/// 小矩形的位置
/// </summary>
public PosSizableRect Location { set; get; }
public Rectangle Rectangle { set; get; }
public LittleRectangle(Rectangle rect, PosSizableRect location)
{
this.Location = location;
switch (location)
{
case PosSizableRect.LeftUp:
this.Rectangle = createRectSizableNode(rect.X, rect.Y); break;
case PosSizableRect.LeftMiddle:
this.Rectangle = createRectSizableNode(rect.X, rect.Y + +rect.Height / 2); break;
case PosSizableRect.LeftBottom:
this.Rectangle = createRectSizableNode(rect.X, rect.Y + rect.Height); break;
case PosSizableRect.BottomMiddle:
this.Rectangle = createRectSizableNode(rect.X + rect.Width / 2, rect.Y + rect.Height); break;
case PosSizableRect.RightUp:
this.Rectangle = createRectSizableNode(rect.X + rect.Width, rect.Y); break;
case PosSizableRect.RightBottom:
this.Rectangle = createRectSizableNode(rect.X + rect.Width, rect.Y + rect.Height); break;
case PosSizableRect.RightMiddle:
this.Rectangle = createRectSizableNode(rect.X + rect.Width, rect.Y + rect.Height / 2); break;
case PosSizableRect.UpMiddle:
this.Rectangle = createRectSizableNode(rect.X + rect.Width / 2, rect.Y); break;
default:
this.Rectangle = new Rectangle(); break;
}
}
private Rectangle createRectSizableNode(int x, int y)
{
int rectWidth = (int)(rectangleWidth * Enlarge);
if (rectWidth < rectangleWidth)
{
Enlarge = 1;
rectWidth = rectangleWidth;
}
return new Rectangle(x - rectWidth / 2, y - rectWidth / 2, rectWidth, rectWidth);
}
}
internal enum PosSizableRect
{
UpMiddle,
LeftMiddle,
LeftBottom,
LeftUp,
RightUp,
RightMiddle,
RightBottom,
BottomMiddle,
None
};
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Windows.Forms;
namespace NcModule.Tools;
public partial class RockRectControl : UserControl
{
private Color borderColor = Color.Green;
private float borderWidth = 2;
private float defaultFontSize = 16;
private List<RockRectangle> rockRectangles = new List<RockRectangle>();
//是否顯示序號
private bool isPrintNum = true;
private Font font = new Font("宋體", 16, FontStyle.Bold);
//背景圖片
private Image backImage = default!;
//圖片有效區域
private Rectangle effectiveRect = default(Rectangle);
//縮放比例,用double多次運算後會失真,故用百分比
private int zoomScale = 100;//圖片本身的縮放比例
private int oldZoomScale = 100;
private int zoomMinScale = 60;
private int zoomMaxScale = 500;
private int stepScale = 20;//每次縮放比例
private Bitmap cloneBackImage = default!;
private double imageScale;//真實圖片與顯示是的縮放比例
private Point realImageCorePoint = new Point();//真實圖片的中心點座標偏移量,當放大或拖拽 時中心點發生變更
private Point wheelPoint = new Point();//捲動時的座標
private bool zoomScaleIsUpdate = true;
public Image BackImage
{
set
{
this.backImage = value;
if (this.backImage != null)
{
//黑白圖,故格式用Format16bppRgb555,可以降低記憶體
cloneBackImage = new Bitmap(this.backImage.Width, this.backImage.Height, PixelFormat.Format16bppRgb555);
}
}
get { return this.backImage; }
}
/// <summary>
/// 矩形框的顏色
/// </summary>
public Color BorderColor
{
set { this.borderColor = value; }
get { return this.borderColor; }
}
/// <summary>
/// 矩形框邊框的粗細
/// </summary>
public float BorderWidth
{
set { this.borderWidth = value; }
get { return this.borderWidth; }
}
public List<RockRectangle> RockRectangles
{
get { return this.rockRectangles; }
}
public RockRectControl()
{
InitializeComponent();
this.init();
}
private void init()
{
//雙緩衝
this.DoubleBuffered = true;
}
private void setFitImageRect()
{
if (this.cloneBackImage == null)
{
return;
}
double imageAspect = this.cloneBackImage.Width * 1.0 / this.cloneBackImage.Height;
double controlAspect = this.Width * 1.0 / this.Height;
//以高為主
if (imageAspect < controlAspect)
{
double imageHeight = this.Height;
double imageWidth = imageHeight * imageAspect;
int x = (int)((this.Width - imageWidth) / 2);
this.effectiveRect = new Rectangle(x, 0, (int)imageWidth, (int)imageHeight);
}
else
{
//以寬為主
double imageWidth = this.Width;
double imageHeight = imageWidth / imageAspect;
int y = (int)((this.Height - imageHeight) / 2);
this.effectiveRect = new Rectangle(0, y, (int)imageWidth, (int)imageHeight);
}
this.imageScale = this.cloneBackImage.Width * 1.0 / this.effectiveRect.Width;
//放大的最大值,只能放大到圖片本來的大小
this.zoomMaxScale = (int)Math.Round(imageScale * 100);
}
//記錄移動前滑鼠的位置
private int oldCursorX, oldCursorY;
private int selectRectIndex = -1;
private PosSizableRect selectLocation = PosSizableRect.None;
protected override void OnMouseDown(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (this.effectiveRect.Contains(e.Location))
{
Point imageP = this.localPoint2ImagePoint(e.Location);
this.oldCursorX = imageP.X;
this.oldCursorY = imageP.Y;
this.changeCursor(imageP, true);
if (selectLocation != PosSizableRect.None)
{
return;
}
//判斷當前位置是在哪個矩形內
foreach (var item in this.RockRectangles)
{
if (this.isInRect(imageP, item.Rectangle, item.RotationAngle))
{
this.selectRectIndex = this.RockRectangles.IndexOf(item);
return;
}
}
}
}
selectRectIndex = -1;
}
protected override void OnMouseUp(MouseEventArgs e)
{
selectRectIndex = -1;
selectLocation = PosSizableRect.None;
this.Invalidate();
}
protected override void OnMouseMove(MouseEventArgs le)
{
if (le.Button == MouseButtons.Left)
{
if (this.selectRectIndex != -1)
{
Rectangle rect = this.RockRectangles[this.selectRectIndex].Rectangle;
Point e = this.localPoint2ImagePoint(le.Location);
switch (selectLocation)
{
case PosSizableRect.LeftUp:
rect.X += e.X - oldCursorX;
rect.Width -= e.X - oldCursorX;
rect.Y += e.Y - oldCursorY;
rect.Height -= e.Y - oldCursorY;
break;
case PosSizableRect.LeftMiddle:
rect.X += e.X - oldCursorX;
rect.Width -= e.X - oldCursorX;
break;
case PosSizableRect.LeftBottom:
rect.Width -= e.X - oldCursorX;
rect.X += e.X - oldCursorX;
rect.Height += e.Y - oldCursorY;
break;
case PosSizableRect.BottomMiddle:
rect.Height += e.Y - oldCursorY;
break;
case PosSizableRect.RightUp:
rect.Width += e.X - oldCursorX;
rect.Y += e.Y - oldCursorY;
rect.Height -= e.Y - oldCursorY;
break;
case PosSizableRect.RightBottom:
rect.Width += e.X - oldCursorX;
rect.Height += e.Y - oldCursorY;
break;
case PosSizableRect.RightMiddle:
rect.Width += e.X - oldCursorX;
break;
case PosSizableRect.UpMiddle:
rect.Y += e.Y - oldCursorY;
rect.Height -= e.Y - oldCursorY;
break;
default:
rect.X = rect.X + e.X - this.oldCursorX;
rect.Y = rect.Y + e.Y - this.oldCursorY;
break;
}
this.RockRectangles[this.selectRectIndex].Rectangle = rect;
this.oldCursorX = e.X;
this.oldCursorY = e.Y;
Invalidate();
}
}
else
{
if (this.effectiveRect.Contains(le.Location))
{
this.changeCursor(this.localPoint2ImagePoint(le.Location));
}
else
{
this.Cursor = Cursors.Default;
}
}
}
protected override void OnMouseWheel(MouseEventArgs e)
{
this.wheelPoint = this.localPoint2ImagePoint(e.Location);
if (e.Delta > 0)//上滾放大
{
if (this.zoomScale < this.zoomMaxScale)
{
this.stepScale = Math.Abs(this.stepScale);
this.zoomScale += this.stepScale;
}
}
else
{
//下滾縮小
if (this.zoomScale > this.zoomMinScale)
{
this.stepScale = -Math.Abs(this.stepScale);
this.zoomScale += this.stepScale;
}
}
this.Invalidate();
}
protected override void OnSizeChanged(EventArgs e)
{
this.Invalidate();
}
protected override void OnPaint(PaintEventArgs pe)
{
//背景圖片存在才繪製
if (this.cloneBackImage != null)
{
this.setFitImageRect();
//把矩形畫在背景圖片上
this.paintRect();
//把圖片繪製到介面上
this.paintImageToControl(pe.Graphics);
}
}
//在背景圖片上畫框
private void paintRect()
{
var g = Graphics.FromImage(cloneBackImage);
//畫背景圖
g.DrawImage(this.backImage, 0, 0, cloneBackImage.Width, cloneBackImage.Height);
//畫的線平滑
//g.InterpolationMode = InterpolationMode.Low;
//設定高質量,低速度呈現平滑程度
//g.SmoothingMode = SmoothingMode.HighSpeed;
g.CompositingQuality = CompositingQuality.AssumeLinear;
//在影象上矩形
using (var path = new GraphicsPath())
{
foreach (var item in this.RockRectangles)
{
//動態加粗線條
double enlarge = this.imageScale * 100 / this.zoomScale;
float nBorderWidth = (float)(this.borderWidth * enlarge);
if (nBorderWidth < this.borderWidth)
{
nBorderWidth = this.borderWidth;
}
LittleRectangle.Enlarge = enlarge;
this.font = new Font("宋體", (float)(this.defaultFontSize * enlarge), FontStyle.Bold);
path.Reset();
this.getPath(path, item.Rectangle, item.RotationAngle);
g.DrawPath(new Pen(this.borderColor, nBorderWidth), path);
//寫序號
if (this.isPrintNum)
{
string num = (this.RockRectangles.IndexOf(item) + 1).ToString();
g.DrawString(num, this.font, new SolidBrush(this.borderColor), this.getCenter(item.Rectangle));
}
//畫每個大矩形裡面的8個小矩形
//獲取8個小矩形
var littleRects = item.GetLittleRectangles();
Rectangle rect = item.Rectangle;
Point center = new Point(rect.X + rect.Width / 2, rect.Y + rect.Height / 2);
foreach (var littleRect in littleRects)
{
path.Reset();
this.getPath(path, littleRect.Rectangle, item.RotationAngle, center);
g.DrawPath(new Pen(this.borderColor, nBorderWidth), path);
}
}
}
g.Dispose();
}
//把圖片繪製到控制元件上
private void paintImageToControl(Graphics g)
{
//設定高質量插值法
g.InterpolationMode = InterpolationMode.High;
//設定高質量,低速度呈現平滑程度
g.SmoothingMode = SmoothingMode.HighQuality;
g.CompositingQuality = CompositingQuality.GammaCorrected;
//獲取圖片的的區域
int width = (int)(cloneBackImage.Width * 100 / zoomScale);
int height = (int)(cloneBackImage.Height * 100 / zoomScale);
//此時是以中心點來縮放的,如果以滑輪中心縮放,則需要知道實際圖片的width和height的縮放比例
//原理是放大後,滑鼠相對於控制元件座標不變,滑鼠向對於影象座標也不變
//realImageCorePoint在反覆計算時有極少誤差,所以當zoomScale不變化是,不更新realImageCorePoint
if (zoomScale != 100)
{
if (this.oldZoomScale != this.zoomScale)
{
realImageCorePoint.X = (int)Math.Round((this.stepScale * wheelPoint.X + (this.zoomScale - this.stepScale) * realImageCorePoint.X) * 1.0 / this.zoomScale);
realImageCorePoint.Y = (int)Math.Round((this.stepScale * wheelPoint.Y + (this.zoomScale - this.stepScale) * realImageCorePoint.Y) * 1.0 / this.zoomScale);
this.oldZoomScale = this.zoomScale;
zoomScaleIsUpdate = true;
}
else
{
//當放大停止後,需要重新重新整理一次
if (zoomScaleIsUpdate)
{
this.Invalidate();
zoomScaleIsUpdate = false;
}
}
}
else
{
realImageCorePoint.X = (int)((cloneBackImage.Width - width) / 2.0);
realImageCorePoint.Y = (int)((cloneBackImage.Height - height) / 2.0);
}
Rectangle srcRect = new Rectangle(realImageCorePoint.X, realImageCorePoint.Y, width, height);
g.DrawImage(cloneBackImage, this.effectiveRect, srcRect, GraphicsUnit.Pixel);
}
//控制元件中的點與元素影象的點轉換
private Point localPoint2ImagePoint(Point p)
{
p.X = (int)((p.X - (this.Width - this.effectiveRect.Width) / 2.0) * imageScale * 100 / zoomScale) + realImageCorePoint.X;
p.Y = (int)((p.Y - (this.Height - this.effectiveRect.Height) / 2.0) * imageScale * 100 / zoomScale) + realImageCorePoint.Y;
return p;
}
private bool isInRect(Point p, Rectangle rect, double angle)
{
Point centerP = this.getCenter(rect);
//獲取反旋轉後的點
return this.isInRect(p, rect, angle, centerP);
}
/// <summary>
/// 判斷某個點是否在矩形內
/// </summary>
/// <param name="p"></param>
/// <param name="rect"></param>
/// <param name="angle"></param>
/// <param name="centerP"></param>
/// <returns></returns>
private bool isInRect(Point p, Rectangle rect, double angle, Point centerP)
{
//獲取反旋轉後的點
Point rotateP = this.getRotatePoint(p, -angle, centerP);
return rect.Contains(rotateP);
}
/// <summary>
/// 改變滑鼠的圖示
/// </summary>
/// <param name="p"></param>
private void changeCursor(Point p, bool updateSelectData = false)
{
bool isInBigRect = false;
foreach (var item in this.RockRectangles)
{
if (this.isInRect(p, item.Rectangle, item.RotationAngle))
{
isInBigRect = true;
}
foreach (var littleRect in item.GetLittleRectangles())
{
//如果圖示在小矩形內
if (this.isInRect(p, littleRect.Rectangle, item.RotationAngle, this.getCenter(item.Rectangle)))
{
this.Cursor = this.getCursor(littleRect.Location);
if (updateSelectData)
{
this.selectRectIndex = this.RockRectangles.IndexOf(item);
this.selectLocation = littleRect.Location;
}
return;
}
}
}
if (isInBigRect)
{
this.Cursor = Cursors.SizeAll;
}
else
{
this.Cursor = Cursors.Default;
}
}
private Cursor getCursor(PosSizableRect p)
{
switch (p)
{
case PosSizableRect.LeftUp:
return Cursors.SizeNWSE;
case PosSizableRect.LeftMiddle:
return Cursors.SizeWE;
case PosSizableRect.LeftBottom:
return Cursors.SizeNESW;
case PosSizableRect.BottomMiddle:
return Cursors.SizeNS;
case PosSizableRect.RightUp:
return Cursors.SizeNESW;
case PosSizableRect.RightBottom:
return Cursors.SizeNWSE;
case PosSizableRect.RightMiddle:
return Cursors.SizeWE;
case PosSizableRect.UpMiddle:
return Cursors.SizeNS;
default:
return Cursors.Default;
}
}
//獲取矩形中心
private Point getCenter(Rectangle rect)
{
return new Point(rect.X + rect.Width / 2, rect.Y + rect.Height / 2);
}
/// <summary>
/// 獲取矩形旋轉後的路徑
/// </summary>
/// <param name="rectangle"></param>
/// <param name="angle"></param>
private void getPath(GraphicsPath path, Rectangle rect, double angle)
{
Point center = this.getCenter(rect);
this.getPath(path, rect, angle, center);
}
private void getPath(GraphicsPath path, Rectangle rect, double angle, Point center)
{
path.AddRectangle(rect);
var a = -angle * (Math.PI / 180);
var n1 = (float)Math.Cos(a);
var n2 = (float)Math.Sin(a);
var n3 = -(float)Math.Sin(a);
var n4 = (float)Math.Cos(a);
var n5 = (float)(center.X * (1 - Math.Cos(a)) + center.Y * Math.Sin(a));
var n6 = (float)(center.Y * (1 - Math.Cos(a)) - center.X * Math.Sin(a));
Matrix matrix = new Matrix(n1, n2, n3, n4, n5, n6);
path.Transform(matrix);
}
//p1繞center旋轉angle角度後點位
private Point getRotatePoint(Point p1, double angle, Point center)
{
//使用旋轉矩陣求值
System.Windows.Media.RotateTransform rotateTransform = new System.Windows.Media.RotateTransform(angle, center.X, center.Y);
System.Windows.Point p = new System.Windows.Point(p1.X, p1.Y);
System.Windows.Point p2 = rotateTransform.Transform(p);
Point result = new Point();
result.X = (int)p2.X;
result.Y = (int)p2.Y;
return result;
}
}
作者:Bonker 出處:http://www.cnblogs.com/Bonker QQ:519841366 |