Bài viết này trình bày cách vẽ một biểu đồ tọa độ cực bằng GDI+. Phương pháp này có thể được áp dụng để vẽ các loại biểu đồ khác như hệ tọa độ Descartes hay biểu diễn dữ liệu theo hình thức quen thuộc.
Sau khi thêm đoạn mã này vào sự kiện Paint, bạn sẽ thấy các vòng tròn đồng tâm xuất hiện. Để cải thiện chất lượng hiển thị, hãy kích hoạt chế độ làm mờ răng cưa:
Bằng cách kết hợp tất cả các hàm trên, bạn sẽ có một biểu đồ tọa độ cực hoàn chỉnh.
Hiệu ứng cuối cùng
Trước tiên, chúng ta sẽ xem qua kết quả cuối cùng của biểu đồ. Biểu đồ này mô tả đường cong hướng của một ăng-ten, rất phù hợp để minh họa trong hệ tọa độ cực. Hệ thống được xây dựng trực tiếp trên form, nhưng bạn cũng có thể dễ dàng bao gói nó thành một control tùy chỉnh cho tiện lợi hơn khi sử dụng.Tạo vòng tròn đồng tâm
Để bắt đầu, chúng ta cần vẽ các vòng tròn đồng tâm. Điều này giúp tạo khung nền cho biểu đồ tọa độ cực.
private void VeVongTron(Graphics g, Rectangle khuVuc)
{
float chieuDai = Math.Min(khuVuc.Width, khuVuc.Height);
float banKinh = chieuDai / 2;
PointF tam = new PointF(
khuVuc.X + khuVuc.Width / 2,
khuVuc.Y + khuVuc.Height / 2
);
int soLuongVongTron = 5;
float buocChieuDai = chieuDai / soLuongVongTron;
float buocBanKinh = banKinh / soLuongVongTron;
RectangleF vongTron = new RectangleF();
vongTron.X = tam.X - banKinh;
vongTron.Y = tam.Y - banKinh;
vongTron.Width = vongTron.Height = chieuDai;
for (int i = 0; i < soLuongVongTron; i++)
{
g.DrawEllipse(Pens.Gray, vongTron);
vongTron.X += buocBanKinh;
vongTron.Y += buocBanKinh;
vongTron.Width -= buocChieuDai;
vongTron.Height -= buocChieuDai;
}
}
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
Vẽ các tia phát
Tiếp theo, chúng ta cần vẽ các tia phát từ tâm biểu đồ. Số lượng tia có thể điều chỉnh linh hoạt.
private void VeTiaPhat(Graphics g, PointF tam, float banKinh, int soTia)
{
if (soTia > 0)
{
float goc = 0;
float buocGoc = 360 / soTia;
for (int i = 0; i < soTia; i++)
{
PointF diemCuoi = TinhToanDiemCuoi(goc, banKinh, tam);
g.DrawLine(Pens.Gray, tam, diemCuoi);
goc += buocGoc;
goc %= 360;
}
}
}
private PointF TinhToanDiemCuoi(float goc, float banKinh, PointF tam)
{
float radian = goc * Math.PI / 180;
return new PointF(
(float)(banKinh * Math.Cos(radian) + tam.X),
(float)(banKinh * Math.Sin(radian) + tam.Y)
);
}
Thêm nhãn góc độ
Để tăng tính chuyên nghiệp, chúng ta sẽ thêm nhãn số độ cho mỗi tia phát. Cần chú ý đến vị trí của nhãn để tránh bị che lấp bởi các phần tử khác.
private void ThemNhanGoc(Graphics g, PointF tam, float banKinh, int soTia)
{
if (soTia > 0)
{
float goc = 0;
float buocGoc = 360 / soTia;
for (int i = 0; i < soTia; i++)
{
string nhan = $"{goc}°";
PointF diemCuoi = TinhToanDiemCuoi(goc, banKinh, tam);
PointF viTriNhan = DiemCuoi;
if (goc == 270)
viTriNhan.Y -= TextRenderer.MeasureText(nhan, this.Font).Height;
else if (goc > 90 && goc < 270)
viTriNhan.X -= TextRenderer.MeasureText(nhan, this.Font).Width;
else
viTriNhan.X += 8;
g.DrawString(nhan, this.Font, Brushes.Gray, viTriNhan);
goc += buocGoc;
goc %= 360;
}
}
}
Vẽ điểm dữ liệu
Cuối cùng, chúng ta cần vẽ các điểm dữ liệu lên biểu đồ. Mỗi điểm dữ liệu gồm hai giá trị: góc độ và giá trị tương ứng.
public class DuLieuCuc
{
public float Goc { get; set; }
public float GiaTri { get; set; }
public DuLieuCuc(float goc, float giaTri)
{
Goc = goc;
GiaTri = giaTri;
}
}
private PointF ChuyenDoiDiem(DuLieuCuc duLieu, float banKinh, PointF tam, float min, float max)
{
float r = banKinh * (duLieu.GiaTri - min) / (max - min);
float radian = duLieu.Goc * Math.PI / 180;
return new PointF(
(float)(r * Math.Cos(radian) + tam.X),
(float)(r * Math.Sin(radian) + tam.Y)
);
}
private void VeDuLieu(Graphics g, List<DuLieuCuc> danhSach, float banKinh, PointF tam, float min, float max)
{
for (int i = 0; i < danhSach.Count; i++)
{
int tiepTheo = (i + 1) % danhSach.Count;
PointF hienTai = ChuyenDoiDiem(danhSach[i], banKinh, tam, min, max);
PointF keSau = ChuyenDoiDiem(danhSach[tiepTheo], banKinh, tam, min, max);
g.DrawLine(Pens.Black, hienTai, keSau);
}
}