2 Nội dung nghiên cứu
2.32 thị được chương trình hiện thực
Để có thể xây dựng vị trí của các đỉnh với điều kiện các đỉnh này được phân bổ đều theo chiều kim đồng hồ, với tên đỉnh tăng dần từ A đến Z, tọa độ các đỉnh là ngẫu nhiên, ta áp dụng giải thuật như sau:
• Dựa vào số lượng các đỉnh, ta chia mặt phẳng ra thành các phần bằng nhau ứng với số lượng đỉnh. Cách chia thực hiện bằng phương pháp
- Tìm điểm tâm của mặt phẳng, và một điểm mốc (điểm gốc được lựa chọn nằm trên trục
Oy, có tọa độx bằng với tọa độx của điểm tâm)
- Ta giả sử mặt phẳng là một hình tròn có bán kính bằng khoảng cách giữa tâm đến điểm gốc, ta chia đường tròn thành các phần bằng nhau ứng với số lượng đỉnh. Góc giữa chúng ta gọi làα
- Từ điểm gốc (nằm trênOy), ta xoay điểm gốc theo một gócαđể có điểm tiếp theo. Công thức xoay như sau
GọiP(x , y)vàC(x, y)lần lượt là tọa độ của điểm cần xoay và điểm trung tâm,dx vàdy
là khoảng cách từ điểm cần xoay đến điểm trung tâm theo phươngx vày, αlà góc cần xoay (αđược tính theo radian), ta có công thức sau:
dx=px−centerx (2.1)
dy=py−centery (2.2)
px=dx.cosα−dy.sinα+centerx (2.3)
py =dx.sinα+dy.cosα+centery (2.4) Giả sử trong trường hợp ta có 4 đỉnh, thì các đỉnh sinh ra sẽ lần lượt theo hình sau:
- Tiếp theo, ta sẽ bắt đầu xây dựng tọa độ các điểm ngẫu nhiên dựa theo các vùng giới hạn đã được tính toán bên trên. Đầu tiên, ta sinh ra một điểm ngẫu nhiên, và xét xem nó có nằm trong vùng đó hay không. Phương pháp tính dựa trên thuật toán sau
Thuật toán để xét 1 điểm có nắm trong một tam giác hay không:
Hình 2.34: Minh họa điểm nằm trong và nằm ngoài tam giác
Ta đã biết được tọa độ của các đỉnhA, B, C và điểmS. Vì vậy ta có thể dễ dàng suy ra được chiều dài của các cạnh của tam giác cũng như chiều dài của điểmS dựa theo công thức sau: GọiAvàB là tọa độ 2 điểm cần tính khoảng cách
d=q
(Bx−Ax)2+ (By−Ay)2 (2.5) GọiS là diện tích tam giác có độ dài 3 cạnh lần lượt làa, b, c. Ta có công thức tính diện tích như sau (Công thức Heron10
)
p=a+b+c
2 (2.6)
S=p
p(p−a)(p−b)(p−c) (2.7) Gọi S là diện tích của tam giác ABC; S1, S2, S3 lần lượt là diện tích của các tam giác
SAB, SBC, SAC.Điểm S nằm trong tam giác ABC khi và chỉ khi S=S1+S2+S3. Ngoài ra, sau khi có tất cả các điểm, ta cũng cần phải kiểm tra hình được tạo ra có phải là một tứ giác lồi hay không. Việc kiểm tra này nhằm giúp cho đồ thị trực quan hơn. Phương pháp là xét xem tất cả các đỉnh có nằm về cùng một phía của đường thẳng hay không
10
Heron là nhà toán học và vật lý vùng Alexandria, không biết ngày sinh và ngày mất. Các công trình của ông về các chủ đề về toán học và vật lý học thì quá phong phú về nội dung cũng như nhiều về số lượng tới mức mà người ta thường xem ông là một tác gia bách khoa trong lĩnh vực này. Có những lý do giả định rằng ông là một người Ai Cập được huấn luyện theo kiểu Hy Lạp. Trong mọi luận văn của ông thường nhắm đến tính hữu dụng thực tiễn hơn là tính hoàn chỉnh về lý thuyết, điều đó cho thấy có sự pha trộn giữa Hy Lạp và phương Đông. Ông quan tâm đến việc xây dựng một nền móng khoa học cho kỹ thuật và cho trắc địa .
Các công trình của Héron có thể chia thành hai lớp : hình học (công trình Metrica) và cơ học. Các công trình về hình học nói đến các vấn đề đo lường còn các công trình về cơ học thì mô tả các thiết bị cơ học rất khéo léo (công trìnhPneumatica, DioptravàCatotrica)
Công trình quan trọng nhất củaHeron là "Metrica" về hình học gồm ba quyển và được tìm thấy ởConstantinple bởiR. Schonevào năm 1896. Quyển I nói về việc đo diện tích của hình vuông, hình chữ nhật, hình tam giác, hình thang, các tứ giác đặc biệt khác nhau, các đa giác đều , vòng tròn và các cung tròn, ellip, diện tích các hình trụ, hình nón, hình cầu và đới cầu .Trong tác phẩm này, Heron đã rút ra được một công thức nổi tiếng để tính diện tích một tam giác theo ba cạnh, người ta đã lấy tên ông để đặt cho công thức này làCông thức Heron.Heroncòn đưa ra cách tính xấp xỉ về căn bậc hai của một số nguyên không chính phương. Quyển II củaMetricanói về cách tính thể tích các hình nón, trụ, hình hộp, hình lăng trụ, hình chóp, hình nón cụt, hình cầu, các đới cầu .. Quyển III nói về cách chia một số diện tích và thể tích các thành phần theo các tỉ số cho trước. (Theo fgt.vnexpress.net)
Hình 2.35: Minh họa đa giác lồi
Hình 2.36: Minh họa đa giác lõmChi tiết cài đặt được mô tả trong đoạn mã sau: Chi tiết cài đặt được mô tả trong đoạn mã sau:
1 // xay dung toa do cac dinh
2 p r i v a t e void s e t V e r t e x L o c a t i o n ()
3 {
4 Point center = new Point () ; 5 center . x = d i m e n s i o n . width /2; 6 center . y = d i m e n s i o n . height /2; 7
8 // tap hop cac diem xac dinh tap d u o n g t h a n g
9 ArrayList < Point > points = new ArrayList < Point >() ; 10
11 // dua diem goc vao
12 points . add ( new Point ( center .x , 0) ) ; 13
14 // xac dinh goc giua cac dt
15 double alpha = 360/ V . size () ; 16
17 // lap vong tim cac diem con lai
25
26 /* - - - */
27
28 // tinh toan toa do cac diem dua vao cong thuc H e r o n
29 // neu diem nam t r o n g mien -> S = S1 + S2 + S3
30 // sinh ngau n h i e n toa do t r o n g mien
31 b o o l e a n stop = false ; 32
33 while (! stop )
34 {
35 Random r = new Random () ;
36 Point P1 , P2 ; 37 int index = 0; 38 39 int S , S1 , S2 , S3 ; 40 41 for ( V e r t e x 2 D v : V ) 42 {
43 P1 = points . get ( index ) ;
44 P2 = points . get (( index +1) % points . size () ) ; 45
46 // g2d . d r a w L i n e ( P1 . x , P1 . y , P2 . x , P2 . y ) ;
47
48 while ( true )
49 {
50 // sinh ngau n h i e n 1 diem
51 Point randP = new Point ( r . n e x t I n t ( d i m e n s i o n . width -50) , 52 r . n e x t I n t ( d i m e n s i o n . height -50) ) ; 53 54 S = ( int ) G e o m e t r y 2 D . t r i a n g l e A r e a ( G e o m e t r y 2 D . d i s t a n c e ( center , P1 ) , 55 G e o m e t r y 2 D . d i s t a n c e ( center , P2 ) , 56 G e o m e t r y 2 D . d i s t a n c e ( P1 , P2 ) ) ; 57 58 S1 = ( int ) G e o m e t r y 2 D . t r i a n g l e A r e a ( G e o m e t r y 2 D . d i s t a n c e ( center , P1 ) , 59 G e o m e t r y 2 D . d i s t a n c e ( center , randP ) , 60 G e o m e t r y 2 D . d i s t a n c e ( randP , P1 ) ) ; 61 62 S2 = ( int ) G e o m e t r y 2 D . t r i a n g l e A r e a ( G e o m e t r y 2 D . d i s t a n c e ( randP , P1 ) , 63 G e o m e t r y 2 D . d i s t a n c e ( randP , P2 ) , 64 G e o m e t r y 2 D . d i s t a n c e ( P1 , P2 ) ) ; 65 66 S3 = ( int ) G e o m e t r y 2 D . t r i a n g l e A r e a ( G e o m e t r y 2 D . d i s t a n c e ( center , P2 ) , 67 G e o m e t r y 2 D . d i s t a n c e ( randP , center ) , 68 G e o m e t r y 2 D . d i s t a n c e ( randP , P2 ) ) ; 69
70 // gioi han sai so
71 if ( S1 + S2 + S3 <= S + 10 && S1 + S2 + S3 >= S - 10) 72 { 73 v . s e t L o c a t i o n ( randP ) ; 74 break ; 75 } 76 } 77 78 index ++; 79 } 80
81 // kiem tra dieu kien tot -> da giac loi
83 stop = true ;
84 }
85
86 }
Nối các cạnh sau khi đã có thông tin vị trí các đỉnh
Sau khi thực hiện việc bố trí vị trí các đỉnh, tiếp theo ta cần phải nối các đỉnh đó lại để có một đồ thị hoàn chỉnh. Việc nối các đỉnh của đồ thị có 3 loại kiểu nối khác nhau: nối thẳng, nối vòng và nối kiểu lò xo (kiểu nối lò xo chỉ dùng trong trường hợp đó là đồ thị tự do). Các kiểu nối này được định nghĩa sẵn trong lớp hỗ trợ đồ họaGLGraphics2D mà chúng tôi sẽ đề cập ở phần sau. Tùy thuộc vào dạng của đồ thị mà ta sẽ có cách nối giữa các đỉnh khác nhau. Vớiđồ thị thường, việc nối các cạnh được căn cứ theo thứ tự của các cạnh đó xuất hiện. Các cạnh được vẽ theo thứ tự ngược chiều kim đồng hồ như hình sau:
Hình 2.37: Đồ thị thường
Ta xét đồ thị bên trên, xét tại đỉnh A, thứ tự của các cạnh lần lượt làAB, AD rồi mới tới
AC. Theo đó, ta cần vẽ cạnh AB trước, cạnh AD luôn phải đứng sau cạnhAD và cạnhAC
phải đứng sau cạnhAD
Thuật toán để vẽ như sau:
- Bắt đầu từ cạnh đầu tiên, ta xét xem nếu trước đó không có cạnh nào, thì đây là cạnh đầu tiên của đỉnh được vẽ. Vẽ cạnh đầu tiên và lưu lại thông tin của cạnh đó. Ngoài ra, ta còn có thêm biếnpreEdgecó nhiệm vụ lưu lại góc của cạnh vừa vẽ so với cạnh đầu tiên
1 // khac dinh bat dau
2 if ( p r e E d g e != null )
3 {
4 if ( e . g e t F r o m () . ge t N a m e () . c o m p a r e T o ( p r e E d g e . g e t F r o m () . g e t N a m e () ) != 0)
5 {
cạnh đang xét với góc của cạnh đã được vẽ trước đó so với cạnh cũ - thì cạnh này sẽ được vẽ bình thường.
Hình 2.38: Minh họa vẽ cạnh trong trường hợp gócpreEdge=DAC\và góc đang xét làα=DAB\ với
α > preEdge
Ngược lại, trong trường hợp góc đang xét nhỏ hơn thì cạnh đó sẽ được vẽ cong để luôn luôn đảm bảo góc của cạnh sau luôn lớn hơn góc của cạnh trước đó như minh họa sau. - Lặp lại quá trình này cho các đỉnh còn lại, ta có đồ thị hoàn hỉnh như Hình 2.19
Hình 2.39: Minh họa vẽ cạnh trong trường hợp gócpreEdge=DAC\và góc đang xét làα=DAB\ với
α < preEdge
Đoạn code minh họa cho quá trình xét các cạnh để tìm ra góc kề giữa các cạnh:
1 for ( Edge2D e : E )
2 {
3 // khac dinh bat dau
4 if ( pr e E d g e != null ) 5 { 6 if ( e . g e t F r o m () . g e t N a m e () . c o m p a r e T o ( p r e E d g e . g e t F r o m () . g e t N a me () ) != 0) 7 { 8 p r e E d g e = null ; 9 p r e A n g l e = -1;
10 } 11 } 12 13 d r a w E d g e ( graphics2d , e , preEdge , p r e A n g l e ) ; 14 15 if ( p re E d g e != null ) 16 {
17 // neu nam cung 1 dinh
18 p r e A n g l e = a n g l e 2 E d g e (e , p r e E d ge ) ; 19 20 if ( p r e A n g l e > 0) 21 { 22 p r e E d g e = e ; 23 } 24 else 25 { 26 p r e E d g e = null ; 27 p r e A n g l e = 0; 28 } 29 } 30 else 31 { 32 p r e E d g e = e ; 33 p r e A n g l e = 0; 34 } 35 }
Đoạn mã minh họa cho việc vẽ cạnh trước và cạnh sau:
1 if ( p r e E d g e == null ) // neu t r u o c no k h o n g co canh nao
2 g2d . d r a w L i n e ( from , to ) ;
3 else
4 {
5 // tinh goc giua canh moi va canh cu
6 double angle = a n g l e 2 E d g e (e , p r e E d g e ) ; 7
8 if ( angle > p r e A n g l e || angle < 0) // nam sau -> ve binh t h u o n g
9 g2d . d r a w L i n e ( from , to ) ; 10 else 11 { 12 // ve kieu cong L a g r a n g e 13 14 // ... 15 16 g2d . d r a w L a g r a n g e ( arrP ) ; 17 18 } 19 }
Vớiđồ thị tự do, công việc đơn giản hơn là chỉ cần vẽ các cạnh nối với các đỉnh mà không cần quan tâm đến thứ tự cần vẽ. Đoạn code sau minh họa cho việc vẽ nối các cạnh trong đồ thị tự do
8 g2d . d r a w L i n e ( from , to ) ; 9
10 return ;
11 }
Sau khi thực hiện các thao tác vẽ, kết quả ta thu được là
Hình 2.40: Đồ thị tự do
Các lớp hỗ trợ
Cũng tương tự như trong không gian 3 chiều, với không gian 2 chiều chúng tôi cũng xây dựng một số lớp hỗ trợ để giúp cho việc cài đặt đồ thị. Các lớp hỗ trợ này cũng gồm có 2 phần chính là các lớp hỗ trợ về đồ họa và các lớp hỗ trợ về toán học. Cac lớp hỗ trợ bao gồm những lớp sau.
• Các lớp hỗ trợ về đồ họa:GLCanvas2D, GLGraphics2D
• Các lớp hỗ trợ về toán học:Geometry2D, LineEq, Polymonial, Term
• Các lớp khác:EdgeDlg2D
Trong không gian 2 chiều, một số lớp có sự thay đổi như:Các lớp hỗ trợ về toán học
• Lớp LineEq giúp hiện thực một đối tượng đường thẳng trong không gian 2 chiều theo công thức
A.x+B.y+C= 0. Ngoài ra, đối tượng đường thằng này còn cho phép người dùng thế vào 1 điểm để xem điểm đó thuộc miền nào của đường thẳng với công thức.
1 public double r e s u b s t i t u t i o n ( Point p )
2 {
3 return A * p . x + B * p . y + C ;
• LớpGeometry2D cũng tương tự như lớpGeometry3D, lớp này cung cấp các phương thức ỗ trợ tinh toán như: xoay 1 điểm trong không gian, tính góc giữa 3 điểm, giữa 2 đoạn thẳng, tính diện tích tam giác, chuyển đổi từ radians sang độ và ngược lại ...
Một số phương thức tiêu biểu của lớp này có thể kể đến như:
- Tính khoảng cách giữa 2 điểm trong không gian 2 chiều theo công thức sau:
d=q
(Bx−Ax)2+ (By−Ay)2 (2.8) - Xoay một điểm quanh 1 điểm trục cho trước. Giả sử ta có điểmP, điểm này xoay quanh điểm
C cho trước với một góc làαthì ta sẽ có công thức sau
dx=Px−Cx (2.9)
dy=Py−Cy (2.10)
Px=dx.cosα−dy.sinα+Cx (2.11)
Py=dy.sinα+dy.cosα+CY (2.12) (2.13) - Lấy góc giữa 3 điểm cho trước. Giả sử ta có 3 điểm A, B, C thì góc α=ABC\được tính
bằng công thức sau a=distance(B, A) (2.14) b=distance(B, C) (2.15) c=distance(A, C) (2.16) α= arccosa 2+b2−c2 2ab (2.17)
• LớpTerm: đây là lớp hiện thực cho khái niệm số hạng. Một số hạng gồm có 2 thành phần chính là:hệ số và số mũ. Ví dụ như2x3 là một số hạng. Ngoài ra, còn có chức năng thế số vào một số hạng.
• LớpPolymonial 11
hiện thực cho khái niệmđa thức một biến. Một đa thức một biến gồm có nhiều số hạng, có dạng như sau
p(x) =a0+a1x+a2x2+...+an−1xn−1+anxn (2.18) Các hệ sốai∈Rlà một đa thức một biến trênR. Nếuan6= 0 thìp(x)là đa thức một biến bậcn. Đa thức trên có thể viết ngắn gọn nhờ kí hiệuσ(sigma) là
p(x) =
i=0
X
n
aixi (2.19)
Với việc cài đặt đa thức, ngoài chức năng nhập đa thức, lớp đa thức này còn có chức năng tính toán, rút gọn cũng như cộng, trừ, nhân đa thức ...
GLGraphics2D Đây là lớp giao tiếp với thư viện đồ họa OpenGL bên dưới để thực hiện các thao thác vẽ trên hình học phẳng. Bản chất của GLGraphics2D cũng tương tự như GLGraphics3D nhưng đã được giới hạn vùng nhìn và sử dụng phép chiếu song song để hiện thực môi trường không gian 2 chiều. Trong lớp hỗ trợ này, chúng tôi cài đặt một số thành phần chính bao gồm
• Phần khởi tạo: người dùng cần phải cung cấp các thông số để khởi tạo đối tượng GLGraphics2D trong phần này. Thuộc tính của GLGraphics2D gồm có:
1 GL2 m_gl ; 2 GLU m_glu ; 3 GLUT m_glut ; 4 T e x t R e n d e r e r m_tr ; 5 6 D i m e n s i o n d i m e n s i o n ; 7
8 C o l o r3 f m _ d r a w C o l o r = new C o l o r 3 f ( Color . BLACK ) ; 9 C o l o r3 f m _ f i l l C o l o r = new C o l o r 3 f ( Color . WHITE ) ;
Phương thức khởi tạo giờ đây bao gồm thông tin của đối tượng vẽ và kích thước của vùng vẽ:
1 // ... 2 // dat che do ve 2 d 3 m_gl . g l M a t r i x M o d e ( GL2 . G L _ P R O J E C T I O N ) ; 4 m_gl . g l Or t h o (0 , d i m e n s i o n . g e t W i d t h () , d i m e n s i o n . g e t H e i g h t () , 0 , 0 , 1) ; 5 m_gl . g l M a t r i x M o d e ( GL2 . G L _ M O D E L V I E W ) ; 6
7 // ... cai dat mot so t h u o c tinh khac
Chế độ vẽ trong không gian 2 chiều được hiện thực qua phép chiếu song song glOrthor với các thông tin (left, right, bottom, top, nearVal, farVal).
• Các phương thức vẽ: cũng giống nhưGLGraphics3D, trongGLGraphics2D ta cũng cần định nghĩa một số phương thức vẽ khác nhau. Trong không gian 2 chiều, các phương thức vẽ gồm có: vẽ đường thẳng, đường tròn, ellipse, vẽ cung, vẽ đường benzier, đường cong dựa trên việc nội suy đa thức Lagrange...
- Vẽ đường thẳng: đường thẳng là một trong những đường cơ bản được OpenGL hỗ trợ. Do đó, ta chỉ cần dựa vào những hàm OpenGL cung cấp sẵn là có thể hiện thực được phương thức này. Phương thức này nhận vào tọa độ hai điểmA, B và tiến hành vẽ đường thẳng nối tọa độ giữa hai điểm đó. Cài đặt như sau:
1 public void d r a w L i n e ( P o i n t 2 D A , P o i n t 2 D B )