Thứ Hai, 21 tháng 12, 2015

Chủ đề: Tìm hiểu đường Bézier, ứng dụng của đường Bézier, Vẽ đồ thị nhiệt độ thời gian ứng dụng đường Bézier trên C#




I. Đường cong Bézier là gì?Ứng dụng của đường cong Bézier.

  • Đường cong Bézier là một đường cong tham số thường được sử dụng trong đồ họa máy tính và một số lĩnh vực khác. Dạng tổng quát hóa của đường cong Bézier trong không gian nhiều chiều được gọi là mặt phẳng Bézier, trong đó tam giác Bézier là một trường hợp đặc biệt.
  • Đường cong Bézier được công bố vào năm 1962 bởi kỹ sư người Pháp Pierre Bézier, người sử dụng nó để thiết kế thân ôtô. Nhưng việc nghiên cứu đường cong này thực tế đã được bắt đầu năm 1959 bởi nhà toán học Paul de Casteljau, ông sử dụng giải thuật De Casteljau để đánh giá các đường cong đó.
  • Về mặt ứng dụng, đường cong Bézier thường được sử dụng trong đồ họa vector để mô hình hóa các đường cong mượt và những đường cong đó có thể phóng to hoặc thu nhỏ theo tỉ lệ không gian giới hạn. "Đường dẫn", một khái niệm sử dụng trong các chương trình xử lý ảnh, được tạo bằng cách liên kết các đường cong Bézier với nhau. Đường cong Bézier còn được sử dụng như là một công cụ để điều khiển sự chuyển động.

II. Các đường cong Bézier.
Một đường cong Bézier được xác định bằng một tập hợp các điểm kiểm soát P0 đến Pn với n được gọi là bậc của nó. Điểm kiểm soát đầu và cuối là các điểm mút (điểm kết thúc) của đường cong, trong khi các điểm nằm giữa thường không nằm trên đường cong.

  • Đường cong Bézier tuyến tính bậc 1 (linear)
Với 2 điểm P0 và P1, đường cong Bézier tuyến tính là một đoạn thẳng nối liền hai điểm đó. Phương trình của đường cong này là:
B(t) = P0 + t(P1 - P0) = (1 - t)P0 + tP1         t∈[0,1]

  • Đường cong Bézier toàn phương hay bậc 2 (quadratic)
Đường cong Bézier bậc 2 được tạo bởi hàm B(t), với các điểm P0, P1, P2 cho trước, khi đó:
B(t) = (1 - t)[(1 - t)P0 + tP1] + t[(1 - t)P1 + tP2]             t∈[0,1]
và có thể biếu diễn thành tập hợp các điểm tương ứng trong đường Bézier tuyến tính được tạo bởi 2 đường Bézier tuyến tính từ P0, P1 và P1, P2. Nói 1 cách khác ta có thể viết lại rằng:
B(t) = (1 - t2)P0 + 2(1 - t)tP1 + t2P2                     t∈[0,1]
Đường cong này xuất phát từ P0, tiến đến P1 rồi chuyển hướng dần đến P2. Hay tiếp tuyến tại P0 và P2 cắt nhau tại P1. Điều này cũng dễ rút ra từ đạo hàm của đường cong Bézier:
B'(t) = 2(1 - t)(P1 - P0) + 2t(P2 - P1)                    t∈[0,1]
  •  Đường cong Bézier lập phương hay bậc 3 (cubic)
Với 4 điểm P0, P1, P2, P3 trên mặt phẳng không gian nhiều chiều có thể định nghĩa 1 đường cong Bézier bậc 3. Đường cong này bắt đầu từ điểm P0, đi theo hướng của điểm P1 và P2 trước khi kết thúc tại P3. Đường cong được hình thành thường không trực tiếp đi qua 2 điểm P1 và P2, và 2 điểm này chỉ mang tính định hướng cho đường cong.

Phương trình đường cong Bézier bậc 3 có thể định nghĩa bằng cách kết hợp 2 đường cong Bézier  bậc 2 với nhau, với phương trình đường cong Bézier  bậc 2 là Bpi,pj,pk, trong đó pi,pj,pk là các điểm của đường cong đó.
B(t) = (1 - t)Bp0,p1,p2(t) + tBp1,p2,p3                t∈[0,1]
  • Đường cong Bézier bậc cao.
Ta có thể xây dựng đường cong Bézier bậc cao 1 cách tương tự như bậc 1, bậc 2, bậc 3.
III. Vẽ đồ thị nhiệt độ thời gian trên C#.
1. Công cụ lập trình.
  • Ngôn ngữ C#.
  • Công cụ Visual Studio 2015.
2. Ý tưởng.
Vẽ đồ thị trực tiếp trên Form.
Tạo giao diện nhập trực tiếp trên Form.
3.Full Code.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

namespace DrawChart1_Application_Bezier
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //Cài đặt dữ liệu mẫu cho dataGridView1
            dataGridView1.Rows.Add();
            dataGridView1.Rows[0].HeaderCell.Value = "Năm 1";
            dataGridView1.Rows[0].Cells[0].Value = 16;
            dataGridView1.Rows[0].Cells[1].Value = 18;
            dataGridView1.Rows[0].Cells[2].Value = 20;
            dataGridView1.Rows[0].Cells[3].Value = 26;
            dataGridView1.Rows[0].Cells[4].Value = 30;
            dataGridView1.Rows[0].Cells[5].Value = 35;
            dataGridView1.Rows[0].Cells[6].Value = 40;
            dataGridView1.Rows[0].Cells[7].Value = 42;
            dataGridView1.Rows[0].Cells[8].Value = 37;
            dataGridView1.Rows[0].Cells[9].Value = 30;
            dataGridView1.Rows[0].Cells[10].Value = 23;
            dataGridView1.Rows[0].Cells[11].Value = 17;

            dataGridView1.Rows.Add();
            dataGridView1.Rows[1].HeaderCell.Value = "Năm 2";
            dataGridView1.Rows[1].Cells[0].Value = null;
            dataGridView1.Rows[1].Cells[1].Value = null;
            dataGridView1.Rows[1].Cells[2].Value = null;
            dataGridView1.Rows[1].Cells[3].Value = null;
            dataGridView1.Rows[1].Cells[4].Value = null;
            dataGridView1.Rows[1].Cells[5].Value = null;
            dataGridView1.Rows[1].Cells[6].Value = null;
            dataGridView1.Rows[1].Cells[7].Value = null;
            dataGridView1.Rows[1].Cells[8].Value = null;
            dataGridView1.Rows[1].Cells[9].Value = null;
            dataGridView1.Rows[1].Cells[10].Value = null;
            dataGridView1.Rows[1].Cells[11].Value = null;
        }

        private void Form1_Shown(object sender, EventArgs e)
        {
            dataGridView1.CurrentCell = dataGridView1.Rows[0].Cells[0];
            dataGridView1_MouseClick(null, null);

            chart1.Titles.Add("1234567890");
            chart1.Titles[0].Text = "BIỂU ĐỒ THEO DÕI NHIỆT ĐỘ TRONG NĂM";
        }

        private void dataGridView1_MouseClick(object sender, MouseEventArgs e)
        {
            //Tìm và đặt giá trị MAX cho trục Y
            int max = Convert.ToInt32(dataGridView1.CurrentRow.Cells[0].Value);
            for (int i = 0; i < 12; i++)
                if (max < Convert.ToInt32(dataGridView1.CurrentRow.Cells[i].Value))
                    max = Convert.ToInt32(dataGridView1.CurrentRow.Cells[i].Value);
            if (chart1.ChartAreas[0].AxisY.Maximum < max) chart1.ChartAreas[0].AxisY.Maximum = max;

            chart1.Series.Clear();
            for (int n = 0; n < dataGridView1.Rows.Count; n++) //Duyệt từ dòng đầu tiên đến dòng cuối cùng của dataGridView1
            {
                if (dataGridView1.Rows[n].Selected) //Nếu dòng thứ n được chọn thì thêm series cho dòng đó
                {
                    Series s = new Series();
                    for (int i = 0; i < 12; i++)
                    {
                        DataPoint p = new DataPoint();
                        p.XValue = i;
                        p.SetValueY(Convert.ToDouble(dataGridView1.Rows[n].Cells[i].Value));
                        p.AxisLabel = "Tháng " + (i + 1).ToString();
                        s.Points.Add(p);
                    }
                    chart1.Series.Add(s);
                }
            }
            foreach (Control a in Form1.ActiveForm.Controls)
                if (a.GetType().Name == "RadioButton")
                    if (((RadioButton)a).Checked)
                    {
                        rbSpline_Click(a, null);
                    }
            foreach (Control b in Form1.ActiveForm.Controls)
                if(b.GetType().Name == "RadioButton")
                    if(((RadioButton)b).Checked)
                    {
                        rbSplineArea_Click(b, null);
                    }
            foreach (Control c in Form1.ActiveForm.Controls)
                if (c.GetType().Name == "RadioButton")
                    if (((RadioButton)c).Checked)
                    {
                        rbLine_Click(c, null);
                    }
        }

        private void rbSpline_Click(object sender, EventArgs e)
        {
            chart1.Series[0].ChartType = SeriesChartType.Spline;
        }

        private void rbSplineArea_Click(object sender, EventArgs e)
        {
            chart1.Series[0].ChartType = SeriesChartType.SplineArea;
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if(checkBox1.Checked)
            {
                chart1.ChartAreas[0].Area3DStyle.Enable3D = true;
            }
            else
            {
                chart1.ChartAreas[0].Area3DStyle.Enable3D = false;
            }
        }

        private void rbLine_Click(object sender, EventArgs e)
        {
            chart1.Series[0].ChartType = SeriesChartType.Line;
        }
    }
}
IV. Hoàn thành.
  • Chọn chế độ Line: chỉ nối các điểm với nhau bằng các đoạn thẳng.
  • Chọn chế độ Spline: nối các điểm với nhau ứng dụng đường Bézier.
  • Chọn chế độ SplineArea: để vẽ hình khối.

  • Convert to 3D thì sẽ chuyển sang hình 3D (nhìn đẹp hơn nhưng có vẻ không hợp lý cho lắm, nếu xét nhiệt độ sôi của chất lỏng phụ thuộc vào các yếu tố sau: nhiệt độ, độ cao thì sẽ hợp lý hơn).

Cảm ơn các bạn đã theo dõi. Nếu có bất kỳ ý kiến gì xin hãy để lại bình luận bên dưới.


Chủ Nhật, 20 tháng 12, 2015

Chủ đề: Vẽ đồng hồ treo tường bằng C#


I.                   Ý tưởng.
  • Dùng hàm DrawEclipe để vẽ mặt đồng hồ.
  • Dùng hàm FillPolygon để vẽ kim đồng hồ.


II.                Vẽ đồng hồ.
  •        Vẽ kim dây.

  1.  Chọn điểm O là trung tâm của đồng hồ. Cần xác định thêm 3 điểm là A, P1, P2 để vẽ kim đồng hồ.
  2. Chọn điểm O là trung tâm của đồng hồ. Cần xác định thêm 3 điểm là A, P1, P2 để vẽ kim đồng hồ.
  3. Xác định điểm A.
  4. Nhận xét: Kim dây cứ mỗi giây sẽ nhích 1 cái. Khi kim dây nhích đủ 60 cái thì sẽ quay được 1 vòng và kim dây sẽ trở lại vị trí ban đầu. 60s kim nhích được 360°, sẽ có 1s kim sẽ nhích được 6°. Góc lệch của A so với trục Oy tại thời điểm t là:Ĉ = (số giây *6) *2Π/360°. Tọa độ điểm A là xA = xO + bán kính * sinĈ; yA = yO + bán kính * cosĈ.
  5. Xác định điểm P1, P2 , OP1, OP2, sẽ có 1 góc lệch so với OA là 1°. Hàm tính P1, P2 tương tự như A.
  6. Xác định B. Góc của OB sẽ lệch so với OA 1 góc 180°. Hàm tính B tương tự như A.
  • Vẽ kim phút, kim giờ.
Tương tự vẽ kim dây.
III.             Souch Code.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace drawclock
{
    public partial class Form1 : Form
    {
        Timer t = new Timer();
        int Width = 300, Height = 300, slength = 140, mlength = 110, hlength = 80, aniseclength = 100, anisecbehindlength = 40;

        int cx, cy;

        Bitmap bmp;
        Graphics g;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            bmp = new Bitmap(Width++, Height++);
            cx = Width / 2;
            cy = Height / 2;
            this.BackColor = Color.White;
            t.Interval = 1000;
            t.Tick += new EventHandler(this.t_Tick);
            t.Start();

        }
        private void t_Tick(object sender, EventArgs e)
        {
            g = Graphics.FromImage(bmp);
            int ss = DateTime.Now.Second;
            int mm = DateTime.Now.Minute;
            int hh = DateTime.Now.Hour;
            int[] handCoord = new int[2];
            int[] aniCoord1 = new int[2];
            int[] aniCoord2 = new int[2];
            int[] aniCoord3 = new int[2];
            g.Clear(Color.White);
            // Vẽ đồng hồ
           
            g.DrawEllipse(new Pen(Color.Black), 0, 0, Width, Height);
            g.FillEllipse(new SolidBrush(Color.Black), 145, 145, 10, 10);
            g.DrawString("12", new Font("Area", 12), Brushes.Black, new PointF(140, 0));
            g.DrawString("1", new Font("Area", 12), Brushes.Black, new PointF(215, 22));
            g.DrawString("2", new Font("Area", 12), Brushes.Black, new PointF(270, 75));
            g.DrawString("3", new Font("Area", 12), Brushes.Black, new PointF(286, 140));
            g.DrawString("4", new Font("Area", 12), Brushes.Black, new PointF(260, 227));
            g.DrawString("5", new Font("Area", 12), Brushes.Black, new PointF(205, 270));
            g.DrawString("6", new Font("Area", 12), Brushes.Black, new PointF(142, 282));
            g.DrawString("7", new Font("Area", 12), Brushes.Black, new PointF(75, 267));
            g.DrawString("8", new Font("Area", 12), Brushes.Black, new PointF(25, 227));
            g.DrawString("9", new Font("Area", 12), Brushes.Black, new PointF(0, 140));
            g.DrawString("10", new Font("Area", 12), Brushes.Black, new PointF(13, 75));
            g.DrawString("11", new Font("Area", 12), Brushes.Black, new PointF(65, 22));
            Point O = new Point(cx, cy);
            // ve kim day
            handCoord = msCoord(ss, slength);
            Point A = new Point(handCoord[0], handCoord[1]);
            aniCoord1 = anisecCoord1(ss, aniseclength);
            Point X1 = new Point(aniCoord1[0], aniCoord1[1]);
            aniCoord2 = anisecCoord2(ss, aniseclength);
            Point X2 = new Point(aniCoord2[0], aniCoord2[1]);
            Pen p = new Pen(Color.Black, 1f);
            Point[] SS = { O, X1, A, X2 };
            g.FillPolygon(new SolidBrush(Color.Green), SS);
            aniCoord3 = anisecCoord3(ss, anisecbehindlength);
            Point C = new Point(aniCoord3[0], aniCoord3[1]);
            g.DrawLine(new Pen(Color.Green, 3f), O, C);


            //ve kim phut
            handCoord = msCoord(mm, mlength);
            Point B = new Point(handCoord[0], handCoord[1]);
            aniCoord1 = anisecCoord11(mm, aniseclength);
            Point Y1 = new Point(aniCoord1[0], aniCoord1[1]);
            aniCoord2 = anisecCoord22(mm, aniseclength);
            Point Y2 = new Point(aniCoord2[0], aniCoord2[1]);
            Pen q = new Pen(Color.Black, 1f);
            Point[] MM = { O, Y1, B, Y2 };
            aniCoord3 = anisecCoord33(mm, 20);
            Point D = new Point(aniCoord3[0], aniCoord3[1]);
            g.DrawLine(new Pen(Color.Black, 3f), O, D);
            g.FillPolygon(new SolidBrush(Color.Black), MM);


            //ve kim gio

            handCoord = hrCoord(hh % 12 , mm, 80);
            Point E = new Point(handCoord[0], handCoord[1]);
            g.DrawLine(new Pen(Color.Red, 5f), O, E);
            handCoord = hrCoord1(hh % 12 , mm, 20);
            Point F = new Point(handCoord[0], handCoord[1]);
            g.DrawLine(new Pen(Color.Red, 3f), O, F);
            pictureBox1.Image = bmp;

            this.Text = "Bay gio la  -   " + hh + " : " + mm + " : " + ss + "";

            g.Dispose();

        }
        //ham tinh kim day
        private int[] msCoord(int sg, int cdkd) //sg la so giay, cdkd la chieu dai kim day
        {
            int[] coord = new int[2];
            sg *= 6;
            if (sg >= 0 && sg <= 180)
            {
                coord[0] = cx + (int)(cdkd * Math.Sin(Math.PI * sg / 180));
                coord[1] = cy - (int)(cdkd * Math.Cos(Math.PI * sg / 180));
            }
            else
            {
                coord[0] = cx - (int)(cdkd * -Math.Sin(Math.PI * sg / 180));
                coord[1] = cy - (int)(cdkd * Math.Cos(Math.PI * sg / 180));
            }
            return coord;
        }

        private int[] msCoord1(int sg, int cdkd) //sg la so giay, cdkd la chieu dai kim day
        {
            int[] coord = new int[2];
            sg *= 1/6;
            if (sg >= 0 && sg <= 180)
            {
                coord[0] = cx + (int)(cdkd * Math.Sin(Math.PI * sg / 180));
                coord[1] = cy - (int)(cdkd * Math.Cos(Math.PI * sg / 180));
            }
            else
            {
                coord[0] = cx - (int)(cdkd * -Math.Sin(Math.PI * sg / 180));
                coord[1] = cy - (int)(cdkd * Math.Cos(Math.PI * sg / 180));
            }
            return coord;
        }
        private int[] anisecCoord1(int val, int hlen)
        {
            int[] coord = new int[2];
            val = val * 6 + 1;

            coord[0] = cx + (int)(hlen * Math.Sin(Math.PI * val / 180));
            coord[1] = cy - (int)(hlen * Math.Cos(Math.PI * val / 180));

            return coord;
        }
        private int[] anisecCoord2(int val, int hlen)
        {
            int[] coord = new int[2];
            val = val * 6 - 1;

            coord[0] = cx + (int)(hlen * Math.Sin(Math.PI * val / 180));
            coord[1] = cy - (int)(hlen * Math.Cos(Math.PI * val / 180));

            return coord;
        }
        private int[] anisecCoord3(int val, int hlen)
        {
            int[] coord = new int[2];
            val = val * 6 + 180;

            coord[0] = cx + (int)(hlen * Math.Sin(Math.PI * val / 180));
            coord[1] = cy - (int)(hlen * Math.Cos(Math.PI * val / 180));

            return coord;
        }
        private int[] anisecCoord11(int val, int hlen)
        {
            int[] coord = new int[2];
            val = val * 6 + 2;

            coord[0] = cx + (int)(hlen * Math.Sin(Math.PI * val / 180));
            coord[1] = cy - (int)(hlen * Math.Cos(Math.PI * val / 180));

            return coord;
        }
        private int[] anisecCoord22(int val, int hlen)
        {
            int[] coord = new int[2];
            val = val * 6 - 2;

            coord[0] = cx + (int)(hlen * Math.Sin(Math.PI * val / 180));
            coord[1] = cy - (int)(hlen * Math.Cos(Math.PI * val / 180));

            return coord;
        }
        private int[] anisecCoord33(int val, int hlen)
        {
            int[] coord = new int[2];
            val = val * 6 + 180;

            coord[0] = cx + (int)(hlen * Math.Sin(Math.PI * val / 180));
            coord[1] = cy - (int)(hlen * Math.Cos(Math.PI * val / 180));

            return coord;
        }
        private int[] hrCoord(int hval, int mval, int hlen)
        {
            int[] coord = new int[2];



            int val = (int)((hval * 30) + (mval * 0.5));
            if (val >= 0 && val <= 180)
            {
                coord[0] = cx + (int)(hlen * Math.Sin(Math.PI * val / 180));
                coord[1] = cy - (int)(hlen * Math.Cos(Math.PI * val / 180));
            }
            else
            {
                coord[0] = cx - (int)(hlen * -Math.Sin(Math.PI * val / 180));
                coord[1] = cy - (int)(hlen * Math.Cos(Math.PI * val / 180));
            }
            return coord;

        }
        private int[] hrCoord1(int hval, int mval, int hlen)
        {
            int[] coord = new int[2];



            int val = (int)((hval * 30) + (mval * 0.5));
            if (val >= 0 && val <= 180)
            {
                coord[0] = cx - (int)(hlen * Math.Sin(Math.PI * val / 180));
                coord[1] = cy + (int)(hlen * Math.Cos(Math.PI * val / 180));
            }
            else
            {
                coord[0] = cx + (int)(hlen * -Math.Sin(Math.PI * val / 180));
                coord[1] = cy + (int)(hlen * Math.Cos(Math.PI * val / 180));
            }
            return coord;

        }
        private int[] anisecCoord111(int sg, int sp, int cdkg)
        {
            int[] coord = new int[2];
            int val = (int)((sg * 30) + (sp * 0.5));
            val = val * 6 + 5;

            coord[0] = cx + (int)(sp * Math.Sin(Math.PI * val / 180));
            coord[1] = cy - (int)(sp * Math.Cos(Math.PI * val / 180));

            return coord;
        }
        private int[] anisecCoord222(int sg, int sp, int cdkg)
        {
            int[] coord = new int[2];
            int val = (int)((sg * 30) + (sp * 0.5));
            val = val * 6 - 5;

            coord[0] = cx + (int)(sp * Math.Sin(Math.PI * val / 180));
            coord[1] = cy - (int)(sp * Math.Cos(Math.PI * val / 180));

            return coord;
        }
        private int[] anisecCoord333(int sg, int sp, int cdkg)
        {
            int[] coord = new int[2];
            int val = (int)((sg * 30) + (sp * 0.5));
            val = val * 6 + 180;

            coord[0] = cx + (int)(sp * Math.Sin(Math.PI * val / 180));
            coord[1] = cy - (int)(sp * Math.Cos(Math.PI * val / 180));

            return coord;
        }
    }
}
IV.            Hình ảnh minh họa.
Cảm ơn bạn đã theo dõi. Nếu có bất kỳ ý kiến gì xin hãy để lại bình luận bên dưới.