2 Nội dung nghiên cứu
2.29 Minh họa hoạt động của chương trình vẽ đồ thị trong không gian 3 chiều
Đồ thị không gian 3 chiều là đồ thị mà trong đó vị trí các đỉnh, các cạnh của nó được hiện thực trên môi trường không gian 3 chiều. Không những vậy, với chương trình này, ta còn có thể tương tác với đồ thị trong không gian 3 chiều thông qua các tính năng như xem đồ thị trong không gian, chỉnh sửa lại đồ thị ...
Không gian vẽ
Trong đồ thị 3 chiều, không gian vẽ sẽ là không gian 3 chiều. Trong không gian này, một đỉnh sẽ nhận 3 giá trị x, y, z làm tọa độ của chúng. Gốc tọa độ của không gian nằm ở chính giữa màn hình. Vì lấy OpenGL làm thư viện đồ họa để xây dựng nên chương trinh này, do đó không gian 3 chiều ở đây tuân theo quy tắc bàn tay phải, tức là chiều x hướng từ trái sang phải, chiều y từ dưới lên trên và chiều z từ trong ra ngoài.
Hình 2.30: Hệ trục tọa độ trong không gian 3 chiều của OpenGL7
Cài đặt cấu trúc đồ thị
Với không gian 3 chiều, đồ thị được tổ chức thành 3 thành phần chính sau:
• Đỉnh (vertex): đối tượng mô tả thông tin một đỉnh của đồ thị. Vì trong không gian 3 chiều, đối tượng này nhận 3 giá trị(x, y, z) - tương ứng với chiều rộng, chiều cao và chiều sâu - làm tọa độ của chúng. Ngoài ra, đối tượng này còn chứa thông tin tên của đỉnh đó. Đối tượng này được hiện thực thành một lớp, thành phần của lớp đó bao gồm 2 thành phần chính là tên và tọa độ của đỉnh này. 1 public class V e r t e x 3 D { 2 p r i v at e String name ; 3 p r i v at e P o i n t 3 f l o c a t i o n = null ; 4 5 // ... 6 }
Cùng với các thành phần đó, trong lớp Vertex3D còn có các phương thức để thao tác với đồ thị nhưgetName()- lấy tên đỉnh, setName(...)- đặt tên dinh,getLocation()- lấy về thông tin tọa độ của đỉnh,setLocation(...) - đặt vị trí cho đỉnh đó
• Cạnh (edge): đối tượng mô tả thông tin của một cạnh trong đồ thị. Thông tin của cạnh gồm có tên cạnh, 2 đỉnh đầu cuối của cạnh này ...
Đối tượng cạnh cũng được hiện thực thành một lớp với tên gọi Edge3D, trong lớp Edge3D chứa những thông tin cần thiết cho cạnh được tạo.
1 public class Edge3D {
2 p r i v at e V e r t e x 3 D from , to ; 3 p r i v at e String name ; 4
5 // ..
được đặt theo thứ tự mà chúng được thêm vào, như cạnh thứ nhất được thêm vào sẽ làa, tương tự cho các cạnh ở sau.
• Đồ thị (graph): đối tượng mô tả thông tin của một đồ thị. Trong đồ thị bao gồm các tập chứa thông tin các đỉnh, các cạnh .. Đồ thị là thành phần quan trọng nhất trong chương trình, với đồ thị trong không gian 3 chiều, chúng tôi tổ chức như sau
1 public class G r a p h 3 D {
2 public ArrayList < Vertex3D > V = new ArrayList < Vertex3D >() ; 3 public ArrayList < Edge3D > E = new ArrayList < Edge3D >() ; 4
5 // ..
6 }
Đồ thị được cài đặt ở đây bao gồm một tập các đỉnh và các cạnh nối với đỉnh đó. Đồ thị này cho phép thêm hay bớt các đỉnh, các cạnh thông qua các hàmget/set được cài đặt sẵn. Đồng thời, đồ thị có khả năng hiển thị trên không gian 3 chiều thông qua phương thứcdraw(...) - phương thức này nhận vào một "không gian vẽ" và hiển thị lên trên không gian đó. Đồ thị được hiển thị qua các bước sau:
1- Xây dựng tọa độ của các đỉnh của đồ thị ứng với không gian vẽ. Ở bước này đồ thị sẽ sinh ra các giá trị ngẫu nhiên trong không gian 3 chiều, và đặt tọa độ đó tương ứng cho mỗi đỉnh của chúng. Tuy nhiên, nếu chỉ sinh tọa độ một cách ngẫu nhiên như vậy, sẽ có những lúc các đỉnh của đồ thị sẽ bị chồng chéo lên nhau. Chính vì thế, chúng tôi giới hạn khoảng cách giữa các đỉnh với nhau phải lớn hơn 0.5 (trong không gian 3 chiều, giới hạn vẽ được chúng tôi quy định từ -1.0 - 1.0). Việc tính toán sắp xếp các đỉnh như thế được hiện thực qua thuật toán sau
1 b o o l e a n stop = false ; 2 while (! stop ) 3 { 4 p = new P o i n t 3 f ( r . n e x t F l o a t () , r . n e x t F l o a t () , r . n e x t F l o a t () ) ; 5 6 int j ; 7 8 for ( j = i - 1; j >= 0; j - -) 9 { 10 if ( G e o m e t r y 3 D . d i s t a n c e ( V . get ( j ) . g e t L o c a t i o n () , p ) < 0.5) 11 break ; 12 } 13
14 if ( j < 0) // chay het vong lap - thoa toan bo dk
15 stop = true ;
16 }
Giải thuật là tính toán khoảng cách của các đỉnh trong không gian 3 chiều, nếu tọa độ của đỉnh đó thỏa mãn điều kiện là nó cách các đỉnh khác một khoảng ít nhất là 0.5 thì sẽ chọn; nếu không thỏa điều kiện thì sẽ sinh ra tọa độ khác để kiểm tra. Khi tất cả các đỉnh đều thỏa mãn điều kiện được đặt ra, thì tiếp tục tiến hành bước vẽ các cạnh của đồ thị.
2- Việc vẽ các cạnh của đồ thị được thực hiện qua đoạn mã sau:
1 P o i n t 3 f from , to ;
2 from = e . g e t F r o m () . g e t L o c a t i o n () ; 3 to = e . getTo () . g e t L o c a t i o n () ; 4
3- Cuối cùng, đồ thị sẽ vẽ các đỉnh và tên đỉnh tương ứng lên trên vùng không gian vẽ.
Đồ thị không thể được vẽ nếu thiếu các lớp hỗ trợ việc vẽ đồ thị, trong đề tài này chúng tôi tách biệt ra các lớp hỗ trợ của đồ thị 2 chiều với đồ thị 3 chiều.
Các lớp hỗ trợ
Các lớp hỗ trợ được chúng tôi xây dựng với chức năng hỗ trợ việc vẽ đồ thị trong không gian 3 chiều. Các lớp hỗ trợ này được chia làm 2 phần chính gồm có: các lớp hỗ trợ về đồ họa - các lớp này có chức năng làm môi trường để cho đồ thị có thể tương tác được và các lớp hỗ trợ việc tính toán - ví dụ như tính khoảng cách, tính góc xoay. Ngoài ra còn một số lớp hỗ trợ cho việc tổ chức chương trình như lớp
Point3f là hiện thực của một điểm trong không gian 3 chiều, hay lớp Color3f là hiện thực cho một màu khi sử dụng với môi trường OpenGL; cũng như các lớp hộp thoại hỗ trợ nhập dữ liệu, chỉnh sửa dữ liệu cho đồ thị. Các lớp hỗ trợ gồm có lớp sau:
• Các lớp hỗ trợ về đồ họa:GLCanvas3D, GLGraphics3D
• Các lớp hỗ trợ về toán học:Geometry3D
• Các lớp khác:EdgeDlg3D, Point3f, Color3f
Các lớp hỗ trợ này đóng một vai trò không thể thiếu trong việc hiện thực đồ thị trong không gian 3 chiều. Một số điểm nhấn trong các lớp hỗ trợ mà chúng tôi đã cài đặt như sau:
GLCanvas3D8
Đây là lớp hiện thực hóa môi trường không gian 3 chiều. Chức năng của lớp này tương tự như một nơi để ta có thể vẽ lên. Việc vẽ lên trênGLCanvas3D được thông qua một đối tượng làGL, đối tượng này có thể được hiểu như là hiện thực của thư viện đồ họaOpenGLtrên môi trường Java. Để có thể sử dụng được lớp này, ta cần phải cài đặt các thành phần chính sau: Thành phần đầu tiên là phương thức thiết lập của đối tượng này, trong phương thức thiết lập này, ta cài đặt các thuộc tính cho môi trường vẽ, ví dụ như khử răng cưa, làm nét vẽ mượt hơn. Đoạn mã cài đặt như sau:
1 public G L C a n v a s 3 D ( G L C a p a b i l i t i e s I m m u t a b l e c a p a b i l i t i e s I m m u t a b l e , M a i n A p p l e t p a r e n t A p p l e t ) 2 { 3 super ( c a p a b i l i t i e s I m m u t a b l e ) ; 4 this . a d d G L E v e n t L i s t e n e r ( this ) ; 5 6 t h e A p p l e t = p a r e n t A p p l e t ; 7 8 // gan m o u s e l i s t e n e r va m o u s e m o t i o n l i s t e n e r 9 a d d M o u s e L i s t e n e r ( new M o u s e A d a p t e r () { 10 @ O v e r r i d e 11 public void m o u s e P r e s s e d ( M o u s e E v e n t e ) { 12 // TODO Auto - g e n e r a t e d m e t h o d stub
13 super . m o u s e P r e s s e d ( e ) ; 14 15 // S y s t e m . out . p r i n t l n (" M o u s e p r e s s e d ") ; 16 17 p r e P o i n t = e . g e t P o i n t () ; 18 19 // gan m o u s e m o t i o n
25 // TODO Auto - g e n e r a t e d m e t h o d stub 26 super . m o u s e R e l e a s e d ( e ) ; 27 28 r e m o v e M o u s e M o t i o n L i s t e n e r ( m o t i o n H a n d l e ) ; 29 } 30 }) ; 31 32 }
Ngoài việc cài đặt các thuôc tính cho môi trường vẽ, tương tự như một thành phần của Java, ta có thể thêm vào cácListener để xử lý các sự kiện trong môi trường không gian 3 chiều. Ở đoạn mã trên, môi trường không gian 3 chiều được cài đặt để có thể nhận được các sự kiện chuột khác nhau, và tùy theo sự kiện chuột xảy ra như thê nào mà xử lý tương ứng với sự kiện đó. Để lớpGLCanvas3D có thể hoạt động, ta cần phải kế thừa các phương thức đã được xây dựng sẵn:
• Phương thứcinit(...): phương thức này được tự động gọi khi đối tượng GLCanvas3D được tạo, nhiệm vụ của phương thức này nhằm khởi tạo môi trường, ngoài ra nếu cần cài đặt thêm những thuộc tính khác thì ta sẽ thêm vào trong phương thức này. Đặc điểm của phương thức này chỉ được chạy một lần duy nhất khi khởi tạo đối tượng.
• Phương thứcreshape(...): phương thức này được gọi tự động mỗi khi có sự thay đổi của không gian vẽ. Ví dụ như thay đổi chiều rộng, chiều cao của vùng vẽ, thay đổi các thuộc tính ...
• Phương thức display(...): phương thức được chương trình tự động gọi, trong phương thức này, người dùng cần định nghĩa sẽ vẽ gì trong chương trình.
Cài đặt lớpGLCanvas3D như sau:
1 public class G L C a n v a s 3 D e x t e n d s G L C a n v a s i m p l e m e n t s G L E v e n t L i s t e n e r { 2 3 p r i v a te G L G r a p h i c s 3 D g r a p h i c s 3 D ; 4 5 // ... 6 7 public G L C a n v a s 3 D ( M a i n A p p l e t p a r e n t A p p l e t ) { 8 // ... 9 } 10 11 public G L C a n v a s 3 D ( G L C a p a b i l i t i e s I m m u t a b l e c a p a b i l i t i e s I m m u t a b l e , M a i n A p p l e t p a r e n t A p p l e t ) 12 { 13 // ... 14 } 15 16 @ O v e r r i d e
17 public void init ( G L A u t o D r a w a b l e d r a w a b l e ) {
18 GL2 gl = d r a w a b l e . getGL () . getGL2 () ; // lay ve O p e n G L c o n t e x t
19
20 g r a p h i c s 3 D = new G L G r a p h i c s 3 D ( gl ) ;
21 }
22
23 @ O v e r r i d e
24 public void r e s h a p e ( G L A u t o D r a w a b l e drawable , int x , int y , int width , int height ) { 25
26 // dieu c h i n h lai kich t h u o c vung ve
27 g r a p h i c s 3 D . s e t S i z e ( width , height ) ;
28 }
29
31 public void d i s p l a y ( G L A u t o D r a w a b l e d r a w a b l e ) { 32 // ... 33 34 // ve do thi 35 if (! t h e A p p l e t . g r a p h 3 d . i s E m p t y () ) 36 t h e A p p l e t . g r a p h 3 d . draw ( graphics3D , g e t C a m e r a () ) ; 37 } 38 39 @ O v e r r i d e
40 public void d i s p o s e ( G L A u t o D r a w a b l e arg0 ) { 41 // TODO Auto - g e n e r a t e d m e t h o d stub
42 // ...
43 }
44 }
GLGraphics3D
Mục đích của chương trình là sử dụngOpenGLđể thực hiện các thao tác vẽ hình trong không gian. Tuy nhiên,OpenGLchỉ bao gồm các hàm API, cùng với một số phương thức vẽ các hình cơ bản như đưởng thẳng, hình tam giác ... không đủ để đáp ứng yêu cầu của chương trình. Vì thế, chúng tôi xây dựng lớp
GLGraphics3D với mục đích đơn giản hóa việc vẽ hình. LớpGLGraphics3D sẽ tương tác trực tiếp với các hàm thư việnOpenGLđể thực thi các thao tác vẽ. Ta không cần quan tâm đối tượng này làm việc đó như thể nào mà chỉ cần sử dụng các phương thức được cung cấp sẵn ở dạng đơn giản, trực quan. Trong lớp này, chúng tôi đã cài đặt một số phương thức cơ bản giúp cho việc thao tác với thư viện đồ họa bên dưới dễ dàng hơn, ví dụ như phương thức vẽ đường thẳng, vẽ chuỗi ... Với lớp hỗ trợ đồ họa này, chúng tôi cài đặt gồm có 3 phần chính: phần khởi tạo, các phương thức vẽ và các phương thức tùy chỉnh thuộc tính của lớp đồ họa.
• Phần khởi tạo: trong phần này, ta cần cung cấp các thông số choGLGraphics3D, để đối tượng này có thể hoạt động được. Thuộc tính củaGLGraphics3D gồm các thông số sau:
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 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 ) ; 7 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 ) ;
Một vài thuộc tính quan trọng của lớpGLGraphics3D có tác dụng như sau:
- GL2: Thành phần chính của lớp GLGraphics3D, tất cả các phương thức vẽ đều thông qua thành phần này để có thể vẽ được trên vùng cần vẽ.
- TextRenderer: Hỗ trợ việc vẽ chữ trong không gian 3D.
Ngoài ra, ta còn cần khởi tạo thông tin của vùng cần vẽ thông qua phương thứcsetSize(...). Trong không gian 3 chiều, các thông tin để khởi tạo vùng cần vẽ ngoài chiều rộng và chiều cao, ta cần phải khai báo phép chiếu ta sử dụng. Nhưng để đơn giản cho việc sử dụng, các thông tin này đã được cài đặt sẵn. Do vậy, ta chỉ cần đưa chiều dài và chiều rộng của vùng cần vẽ. Minh họa cài đặt như sau:
7 m_glu . g l u P e r s p e c t i v e (45.0 , aspect , 0.0 , 100.0) ; // cai dat goc nhin , ty le va do xa gan
8
9 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 ) ;
• Các phương thức vẽ: sau khi cài đặt việc khởi tạo đối tượng GLGraphics3D, để có thể cung cấp khả năng vẽ hình choGLGraphics3D, ta cần định nghĩa các phương thức vẽ tương ứng trong lớp này. Một số phương thức vẽ cơ bản có thể là:drawLine(...),drawString(...)... Các phương thức vẽ này giúp cho đơn giản hóa việc vẽ hình. Ta chỉ cần gọi phương thức vẽ tương ứng, đối tượng sẽ xử lý dựa theo thông tin đã được cung cấp sẵn chứ không cần phải thao tác trực tiếp với các hàm API củaOpenGL. Ví dụ như hàm vẽ một đường thẳng dưới đây:
1 public void d r a w L i n e ( P o i n t 3 f A , P o i n t 3 f B ) 2 { 3 m_gl . g l P u s h M a t r i x () ; 4 5 // dat mau d u o n g t h a n g 6 m_gl . g l C o l o r 3 f ( m _ d r a w C o l o r .R , m _ d r a w C o l o r .G , m _ d r a w C o l o r . B ) ; 7 8 m_gl . g l Be g i n ( GL2 . G L _ L I N E S ) ; 9 m_gl . g l V e r t e x 3 f ( A .x , A .y , A . z ) ; 10 m_gl . g l V e r t e x 3 f ( B .x , B .y , B . z ) ; 11 m_gl . glEnd () ; 12 13 m_gl . g l P o p M a t r i x () ; 14 }
• Các phương thức khác: ngoài các phương thức khởi tạo và phương thức vẽ, lớpGLGraphics3D còn cung cấp một loạt các phương thức giúp ta tùy chỉnh thuộc tính vẽ, tùy chỉnh không gian vẽ như xoay hình, đổi màu vẽ, dịch chuyển hình...
Geometry3D
Trong quá trình xây dựng phần vẽ đồ thị trong không gian 3 chiều, chương trình có sử dụng một số các tính toán trong không gian 3 chiều. Vì vậy, chúng tôi xây dựng lớp Geometry3D nhằm mục đích cung cấp một thư viện gồm các hàm tính toán khác nhau. Nhưng do giới hạn của chương trình, do đó chúng tôi chỉ cài đặt những hàm nào mà thật sự cần sử dụng để tránh sự dư thừa không cần thiết. Một trong số đó là hàm tính khoảng cách trong không gian 3 chiều. Hàm này nhận vào tọa độ của 2 điểm bất kì trong không gian 3 chiều và trả về khoảng cách giữa chúng. Giả sử tọa độ của 2 điểm trong không gian lần lượt làx1, y1, z1 vàx2, y2, z2, công thức của hàm này như sau:
d=p
(x2−x1)2+ (y2−y1)2+ (z2−z1)2
Edge3D
Để có thể tạo ra giao diện hộp thoại giao tiếp với dùng nhận dữ liệu cho đồ thị trong môi trường không gian 3 chiều, chúng tôi xây dựng một lớp hộp thoại mới với chức năng cho phép nhập các thông tin về đồ thị không gian 3 chiều là số cạnh và quan hệ các cạnh đó. Ngoài ra, lớp hộp thoại này còn có chức năng chỉnh sửa lại cấu trúc đồ thị dựa vào thông tin đồ thị được nhận vào. Trong hộp thoại này, các thuộc tính của nó bao gồm
1 p r i v a te final JPanel c o n t e n t P a n e l = new JPanel () ; 2
3 // mang chua cac C h e c k Box
4 p r i v a te ArrayList < JCheckBox > m _ c h e c k B o x e s = new ArrayList < JCheckBox >() ; 5
6 p r i v a te G r a p h 3 D m _ g r a p h ; 7 p r i v a te b o o l e a n o k P r e s s e d ;
8 p r i v a te b o o l e a n m o d i f y M o d e = false ;
Đối tượng đồ thị này hoạt động như sau:
- Đầu tiên, đối tượng hộp thoại này nhận vào thông tin của đồ thị bao gồm cấu trúc của đồ thị và số đỉnh của đồ thị đó. Dựa vào số lượng đỉnh được nhận vào, hộp thoại sẽ tự động xây dựng quan hệ giữa các đỉnh dựa trên thông tin số lượng đỉnh cho trước. Ví dụ như ta có đỉnh A, thì chương