C# – Vẽ đồ thị trong Y2 Visual Graph

Y2-Visual-Graph-hexagonal

Từ thắc mắc của bạn đọc, tôi sẽ giải thích phương pháp vẽ các đỉnh và cạnh của đồ thị trong chương trình Y2 Visual Graph. Các phần về đồ họa và hiệu ứng sẽ không được đề cập trong bài viết này.

Vẽ Đỉnh (Node, Vertice)

Khi bắt đầu làm chương trình này, do muốn đơn giản nên tôi tạo một lớp Node thừa kế từ System.Windows.Forms.Control. Do đây là một control nên việc xử lý sự kiện chuột sẽ dễ dàng hơn nhưng cũng khiến hiệu suất của chương trình giảm so với dùng GDI+.
Lớp Node này chỉ có 3 property chính, các phương thức còn lại chỉ cho mục đích tạo hiệu ứng:

// Node.cs
class Node : Control
{
	public char DisplayName { get; set; }
	public int Index { get; set; }
	public bool Selected { get; set; }
}

Để thêm một node, ta xử lý sự kiện OnMouseDown của control GraphUI. Khi công cụ đang được chọn là DrawingTools.Node. Khi một node được tạo, ta sẽ đồng thời thêm vào các sự kiện Node_MouseDown, Node_MouseMove và Node_MouseUp. Ba sự kiện này chính là nơi ta sẽ xử lý để di chuyển node và vẽ cạnh giữa hai node.

// GraphUI.cs
protected override void OnMouseDown(MouseEventArgs e)
{
	if (e.Button == MouseButtons.Left)
	{
		if (this.Tool == DrawingTools.Node)
		{
			int m = 'Z' - 'A' + 2;
			if (this.Controls.Count == m)
			{
				MessageBox.Show("You can only add " + m + " nodes to graph");
				return;
			}
			AddNewNode(e.Location);

		}
		else if (this.Tool == DrawingTools.Edge || this.Tool == DrawingTools.Eraser)
		{
			// select or delete a node
		}
	}
	OnContentChanged(null, null);
	base.OnMouseDown(e);
}

private void AddNewNode(Point location)
{
	Node n = new Node();
	n.Index = this.Controls.Count;
	n.DisplayName = (char)(n.Index + 'A' - 1);
	n.Location = location;
	this.Controls.Add(n);
	n.DoCreatingAnimation();
	n.Width = NODE_DIAMETER;
	n.Height = NODE_DIAMETER;

	n.MouseDown += new MouseEventHandler(Node_MouseDown);
	n.MouseMove += new MouseEventHandler(Node_MouseMove);
	n.MouseUp += new MouseEventHandler(Node_MouseUp);
}

Khi nhấn chuột để tạo node, bạn thấy rằng vị trí chuột sẽ nằm ở trung tâm của node. Điều này do tọa độ của node được dịch chuyển theo hướng giảm Left và Top, qua phương thức Node.DoCreatingAnimation().

Di Chuyển Node

Để di chuyển một node, ta cần hai sự kiện Node_MouseDown và Node_MouseMove:
Node_MouseDown: Lưu vị trí chuột hiện tại. Lưu ý đây là vị trí tương đối của chuột trên node (vị trí tính từ góc trái, trên cùng của node). Ta sẽ lưu lại vị trí này biến _startPoint để khi di chuyển node, vị trí chuột trên node vẫn giữ nguyên. Nếu không, vị trí chuột sẽ chuyển thành {0,0} khi di chuyển node.
Node_MouseMove: Từ vị trí chuột trên node, ta sẽ tính ra vị trí chuột trên vùng vẽ (GraphUI). Sau đó di chuyển node đến vị trí này sau khi trừ đi một khoảng cách _startPoint như đã nói ở trên.

Trong phần này, bạn cần hiểu được hai phương thức sau:
Control.PointToScreen(Point p): chuyển tọa độ tương ứng của p trên control thành tọa độ tuyệt đối trên màn hình.
Control.PointToClient(Point p): chuyển tọa độ tuyệt đối trên màn hình thành tọa độ tương đối trên control (ngược với phương thức trên).
Ta sử dụng đồng thời hai phương thức này để chuyển tọa độ tương đối trên một control thành tọa độ tương đối trên một control khác chứa nó, như trong đoạn code dưới đây:

// GraphUI.cs
void Node_MouseMove(object sender, MouseEventArgs e)
{
	Control ctl = (Control)sender;

	if (e.Button == MouseButtons.Left)
	{
		Point p = this.PointToClient(ctl.PointToScreen(e.Location));
		if (this.Tool == DrawingTools.Select || this.Tool == DrawingTools.Node)
		{
			if (p.X > 0 && p.Y > 0 && p.X < this.Width && p.Y < this.Height)
			{
				p.X -= _startPoint.X;
				p.Y -= _startPoint.Y;

				ctl.Location = p;

				Invalidate(); // repaint
			}
		}
		else if (this.Tool == DrawingTools.Edge)
		{
			// draw edge
		}
	}
}

Vẽ Cạnh (Edge)

Việc vẽ một đoạn thẳng được thực hiện theo trình tự sau:
MouseDown: Lưu lại vị trí chuột (_startPoint) và index của node được nhấn (_startIndex).
MouseMove: Vẽ một đoạn thẳng từ _startPoint đến vị trí chuột hiện tại.
MouseUp: Lấy node tại vị trí chuột với phương thức Control.GetChildAtPoint() và tạo một edge mới với hai node đã được xác định và thêm vào danh sách.

// GraphUI.cs
void Node_MouseDown(object sender, MouseEventArgs e)
{
	Node ctl = (Node)sender;
	if (e.Button == MouseButtons.Left)
	{
		// ...
		if (this.Tool == DrawingTools.Edge)
		{
			_startPoint = this.PointToClient((ctl.PointToScreen(e.Location)));
			_startIndex = ctl.Index;
		}
	}
}
void Node_MouseMove(object sender, MouseEventArgs e)
{
	Control ctl = (Control)sender;

	if (e.Button == MouseButtons.Left)
	{
		// ...
		if (this.Tool == DrawingTools.Edge)
		{
			Point p2 = this.PointToClient(ctl.PointToScreen(e.Location));
			using (Graphics g = this.CreateGraphics())
			{
				g.DrawLine(Pens.Red, _startPoint, p2);
				Invalidate();
			}
		}
	}
}

private void Node_MouseUp(object sender, MouseEventArgs e)
{
	Node ctl = (Node)sender;
	if (e.Button == MouseButtons.Left)
	{
		if (this.Tool == DrawingTools.Edge)
		{
			Point p2 = this.PointToClient(ctl.PointToScreen(e.Location));
			Node node = this.GetChildAtPoint(p2) as Node;
			if (node != null)
			{
				_edges.Add(new Edge(_startIndex, node.Index));
			}
		}
		Invalidate();
	}
}

Phần còn lại chỉ là vẽ các edge trong danh sách lên màn hình.

YinYangIt’s Blog

Advertisements

13 thoughts on “C# – Vẽ đồ thị trong Y2 Visual Graph

  1. mình muốn đi theo trọng tâm công việc!
    không muốn mất nhiều thời gian! mong duơcj giúp đỡ!
    – mình muốn làm 3 cái Buttion tự đổi màu cung luc tư Xanh sang vàng sang dỏd theo timer trong 30s! có đồng hồ chạy phía dưới!
    – minh muốn vẽ lưới tròn trục ở giữa form có thể thay đổi kích thước 120, 60, 40, quan nut bution
    – minh muốn có 1 chất điểm chuyển động từ vòng ngoài của lưới di vào tâm luói vừa đi vừa nháp nháy! và một chất diểm đi từ tâm đi ra cũng thế thế có thể điền khiển được gặp nhau trong toa đô,< ,25km
    – mình muốm chất điểm đo đi đến đâu sẽ có thông báo về tọa đo CỰ ly, Phương vị, góc tà, độ cao.
    rât mong giúp đỡ và hậu tạ!

    Trả lời

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s