在上一篇 介紹完 可改變大小的直線後,這篇是進階版
上一篇有個問題 不能直接移動直線,但 直線不是我的重點,有興趣的人可以參考這篇的移動
首先一樣會用到上一篇可移動的方框,這是有4個,在圓的上下左右
這次可以自己畫圓,而且可以畫多個圓,所以需要一個 List 來存圓
簡單演算法
一個圓
圓的上下左右有個方框可以選擇
直接點圓,判別是哪個圓
點在圓上,可拖曳
拖曳 重新繪圖
點選方框,判別是哪個圓裡的方框
點選方框,可放大縮小
一樣,要用到 GraphicPath
首先 建立一個 圓的Class
(方框的 class 請參考上一篇 Bitmap 進階繪圖(一) )
/// <summary>
/// 畫一個可移動的圓的結構
/// </summary>
public class Circle
{
public List<MovePoint> mp = new List<MovePoint>(); //圓四個可選取的框
private Rectangle _rect; //圓的大小
private Point o; //圓中心所在位置
private int r = 5; //圓的半徑
private int l = 100; //紀錄圓的直徑
private int dx, dy; //圓移動時的偏移值
private bool flagDraw=false;
public bool Select=false;
public int mpIndex=0;
private GraphicsPath gp ; //傳回圓的 路徑
private Pen pen = new Pen(Color.Blue, 1); //畫此圓的畫筆與顏色
/// <summary>
/// 新建一個圓
/// </summary>
/// <param name="e"></param>
public Circle(Point e){
O = e;
Rect = new Rectangle(e.X - r/2 , e.Y -r/2 , r, r );
MovePoint mmp = new MovePoint(new Point (e.X , e.Y-r/2));
mp.Add(mmp);
mmp = new MovePoint(new Point(e.X-r/2, e.Y ));
mp.Add(mmp);
mmp = new MovePoint(new Point(e.X + r/2,e.Y ));
mp.Add(mmp);
mmp = new MovePoint(new Point(e.X ,e.Y+r/2));
mp.Add(mmp);
}
//----------------------------------------------------------------------------------------------
/// <summary>
/// 取得此圓的 畫框
/// </summary>
public Rectangle Rect{
get{return this._rect;}
private set{ this._rect = value;}
}
//----------------------------------------------------------------------------------------------
//by Rhine
/// <summary>
/// 取得 設定畫筆
/// </summary>
public Pen DrawPan
{
get { return pen; }
set { pen = value; }
}
//----------------------------------------------------------------------------------------------
/// <summary>
/// 此圓的中心點
/// </summary>
public Point O{
get { return this.o; }
private set { this.o = value;}
}
//----------------------------------------------------------------------------------------------
/// <summary>
/// 是否可以移動 變大小
/// </summary>
public bool FlagDraw{
get { return this.flagDraw;}
set { this.flagDraw = value;}
}
//----------------------------------------------------------------------------------------------
//by Rhine
/// <summary>
/// 得到變動後的 圓的 GraphicPath
/// </summary>
public GraphicsPath graphicPath
{
get { return this.gp; }
private set { }
}
//----------------------------------------------------------------------------------------------
/// <summary>
/// 裡面的方框是否被選到
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
public bool mpSelect(Point e)
{
for (byte i = 0; i < 4; i++)
{
if (mp[i].CheckSelect(e))
return true;
}
return false;
}
//----------------------------------------------------------------------------------------------
//by Rhine
/// <summary>
/// 判別是否選到此圓
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
public bool CircleSelect(Point e)
{
if (Rect.Contains(e))
{ //如果有選到圓,記錄點選的位置與圓中心的參數
dx = e.X - O.X ;
dy = e.Y - O.Y;
return true;
}
else
return false;
}
//----------------------------------------------------------------------------------------------
/// <summary>
/// 放大 縮小 繪圖
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
public GraphicsPath draw(Point e)
{
l = (LineDist(O, e)) * 2;
Rect = new Rectangle(O.X - l / 2, O.Y - l / 2, l, l);
mp[0].move(new Point(O.X, O.Y - l / 2));
mp[1].move(new Point(O.X - l / 2, O.Y));
mp[2].move(new Point(O.X + l / 2, O.Y));
mp[3].move(new Point(O.X, O.Y + l / 2));
Rectangle ce = new Rectangle(O.X - 2, O.Y - 2, 4, 4); //在中心的位置加入個小圓
gp = new GraphicsPath() ;
gp.AddEllipse(ce);
gp.AddEllipse(Rect);
gp.AddRectangle(mp[0].rect);
gp.AddRectangle(mp[1].rect);
gp.AddRectangle(mp[2].rect);
gp.AddRectangle(mp[3].rect);
return gp;
}
//----------------------------------------------------------------------------------------------
//by Rhine
//當移動圓 時用
public GraphicsPath Move(Point e)
{
O = new Point((e.X - dx), (e.Y - dy));
Rect = new Rectangle(O.X - l / 2, O.Y - l / 2, l, l);
mp[0].move(new Point(O.X, O.Y - l / 2));
mp[1].move(new Point(O.X - l / 2, O.Y));
mp[2].move(new Point(O.X + l / 2, O.Y));
mp[3].move(new Point(O.X, O.Y + l / 2));
Rectangle ce = new Rectangle(O.X - 2, O.Y - 2, 4, 4); //在中心的位置加入個小圓
gp.Reset();
gp.AddEllipse(Rect);
gp.AddEllipse(ce);
gp.AddRectangle(mp[0].rect);
gp.AddRectangle(mp[1].rect);
gp.AddRectangle(mp[2].rect);
gp.AddRectangle(mp[3].rect);
return gp;
}
/// <summary>
/// 計算兩點距離
/// </summary>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <returns></returns>
public static int LineDist(Point p1, Point p2)
{
return (int)Math.Sqrt(Math.Pow((p1.X - p2.X), 2) + Math.Pow((p1.Y - p2.Y), 2));
}
}
設定好後 一樣開啟一個 From 裡面放一個 Picturebox 與一個 buttom
一樣,需要在 Picturebox 上設定 Mouse_up Mouse_down and Mouse_Move 事件
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
[SerializableAttribute]
public enum GCCollectionMode { };
Bitmap bm;
Graphics g;
GraphicsPath gp;
List<Circle> cir = new List<Circle>();
bool drawCircle = false; //是否可以畫圓
bool drawPic = false; //是否重新繪圖
bool drawMove = false; //是否可移動
int cir_index = -1; //記錄目前所選取的圓
//---------------------------------------------------------------------------------------------------
private void Form2_Load(object sender, EventArgs e)
{
bm = new Bitmap(pictureBox1.Width, pictureBox1.Height);
g = Graphics.FromImage(bm);
gp = new GraphicsPath();
}
//---------------------------------------------------------------------------------------------------
/// <summary>
/// 新增一個圓用
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
if (drawCircle)
drawCircle = false;
else
drawCircle = true;
}
//---------------------------------------------------------------------------------------------------
/// <summary>
/// 滑鼠點 下
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (drawCircle)
{
drawPic = true;
cir_index = cir.Count;
CDraw.Circle c = new CDraw.Circle(e.Location);
cir.Add(c);
gp.AddEllipse(cir[cir_index].Rect);
g.DrawPath(new Pen(Color.Lime), gp);
pictureBox1.BackgroundImage = bm;
}
else
{
if (cir.Count > 0)
{
for (int i = 0; i < cir.Count; i++)
{
if (cir[i].mpSelect(e.Location)) //先看是否選到第i個圓的小框
{
cir_index = i;
drawPic = true;
drawCircle = true;
break;
}
if (cir[i].CircleSelect(e.Location)) //看是否選到圓
{
cir_index = i;
drawMove = true;
drawCircle = true;
cir[cir_index].FlagDraw = true;
break;
}
}
}
}
}
//by Rhine
/// <summary>
/// 滑鼠 上
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
if (cir.Count > 0)
{
cir[cir_index].FlagDraw = false;
drawPic = false;
drawCircle = false;
drawMove = false;
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (drawCircle) //是否要畫圓
{
bm = new Bitmap(pictureBox1.Width, pictureBox1.Height);
g = Graphics.FromImage(bm);
if (drawPic) //是否伸縮 畫圓
{ cir[cir_index].draw(e.Location); }
if (drawMove) //是否移動畫圓
{ cir[cir_index].Move(e.Location); }
for (int i = 0; i < cir.Count; i++)
{
g.DrawPath(cir[i].DrawPan, cir[i].graphicPath); //把每個圓的軌跡 畫出
}
pictureBox1.BackgroundImage = bm;
}
}
}
成果
先畫兩個圓
左邊的往下移動 右邊的放大
希望不會寫的太複雜,後面應該會再加幾個功能
比如 有選擇才顯示 小框
可改變顏色. 是否弄個Stack 來做移到最上層..等等
不過那也要等有空再來弄了
ps. 因為自己在用是包成將圓 與 小框另外包出 Class ,有出問題請檢察一下 NameSapce