TRƯỜNG ĐẠI HỌC BÁCH KHOA KHOA CÔNG NGHỆ THÔNG TIN ĐỒ ÁN LẬP TRÌNH TÍNH TOÁN Xây dựng và biểu diễn đường cong B-spline Người hướng dẫn : NGUYỄN TẤN KHÔI Sinh viên thực hiện : Trương Tấn Sang Lớp 21T_DT2 Nhóm: Nh10 Lương Công Thịnh Lớp 21T_DT2 Nhóm: Nh10 Đà Nẵng 27/05 MỤC LỤC MỤC LỤC 1 DANH MỤC HÌNH VẼ 2 MỞ ĐẦU 3 1.TỔNG QUAN ĐỀ TÀI 4 2.CƠ SỞ LÝ THUYẾT 4 2.1.Ý tưởng 4 2.2.Cơ sở lý thuyết 4 3.TỔ CHỨC CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN 9 3.1.Phát biểu bài toán 9 3.2.Cấu trúc dữ liệu 9 3.3.Thuật toán 9 3.3.1Thuật toán sử dụng phương trình 4.CHƯƠNG TRÌNH VÀ KẾT QUẢ 10 11 4.1.Tổ chức chương trình 11 4.2.Ngôn ngữ cài đặt 12 4.3.Kết quả 12 4.3.1.Giao diện chính của chương trình 12 4.3.2.Kết quả thực thi chương trình 14 4.3.3.Nhận xét, đánh giá 18 5.KẾT LUẬN VÀ HƯỚNG PHÁT TRIỂN 19 5.1.Kết luận 19 5.2.Hướng phát triển 19 6.Tài liệu tham khảo 19 Phụ lục 20 1 MỞ ĐẦU Đường cong, hầu như có thể luôn luôn tìm được một đường cong rõ ràng ở khắp mọi nơi, việc nghiên cứu cách biến đổi đường cong đó bằng cách thay đổi việc biểu diễn nó theo tham số đi kèm biến nó trở thành đường cong tham số, hỗ trợ giúp đỡ trong các vấn đề thiết kế đồ họa và kĩ thuật một cách triệt để. Đường cong tham số được biểu diễn thông qua tham số, việc thay đổi tham số sẽ làm biến đổi đường cong theo từng mục đích của người sử dụng, một trong số các kiểu đường cong là đường cong tham số bezier và đường cong B-spline. Đường cong tham số Bézier, được triển khai lần đầu tiên bởi Paul de Casteljau, sử dụng thuật toán Casteljau để đánh giá đường cong, và chính thức trở nên nổi tiếng vào năm 1962 bởi kỹ sư Pierre Bézier cho mục đích thiết kế ô tô. Đường cong tham số Bézier được sử dụng trong đồ họa máy tính và một số lĩnh vực khác. Tuy nhiên đường cong Bézier không mang tính cục bộ. Ta có thể hiệu chỉnh các đỉnh điều khiển của đường cong Bézier để tạo ra loại đường cong mong muốn. Tuy nhiên, khi thay đổi bất kỳ đỉnh điều khiển nào cũng sẽ gây ra sự thay đổi trên toàn bộ đường cong. Đường cong B-spline, tổng quát hơn và được xem là dạng tổng quát cùa đường cong Bézier, mang nhiều điểm tương đồng với đường cong Bézier nhưng có nhiều đặc tính mong muốn hơn. Số lượng thông tin mà đường cong B-spline yêu cầu nhiều hơn so với đường cong bézier. Đường cong B-spline luôn có thể trở thành đường cong bezier bất cứ khi nào, và cung cấp khả năng kiểm soát linh hoạt hơn nhờ vào đặc tính yêu cầu nhiều thông tin của nó. Khác với đường cong bezier, việc tha đổi một đỉnh điều khiển sẽ dẫn đến sự thay đổi trong toàn bộ được cong, việc thay đổi một đỉnh điều khiển trong đường cong B-spline không yêu cầu việc thay đổi ở toàn bộ đường cong. Từ những thuộc tính đó, về thực tế, khi muốn biểu diễn những đường cong và mặt cong tham số phức tạp, người ta sử dụng đường cong B-spline. Việc tìm hiểu và nắm bắt được cách thức biến và biểu diễn đường cong Bspline dựa trên tham số sẽ là nội dung chính của đề tài này. Đề tài được thực hiện và kiểm chứng kết quả dựa trên việc bổ sung các cơ sở lí thuyết, các ví dụ, bộ số mẫu và tham chiếu với kết quả chuẩn từ các nguồn ngoài, kết hợp với nhận xét, đánh giá từ giảng viên hỗ trợ hướng dẫn để đề tài được triển khai theo hướng đúng nhất và hiệu quả nhất. 2 1. TỔNG QUAN ĐỀ TÀI Nghiên cứu tính chất, đặc điểm của đường cong tham số B-spline. Mối liên hệ giữa các khái niệm, tính chất với nhau và triển khai, xây dựng công thức của đường cong Bspline phụ thuộc vào tham số. Triển khai bằng tay ví dụ, tham chiếu kết quả từ nguồn bên ngoài được hỗ trợ. Thiết kế bộ số mẫu, tính toán kết quả dựa trên công thức được xây dựng từ trước và đối chiếu với các nguồn ngoài để kiểm chứng độ chính xác và chỉnh sửa các yếu tố liên quan khi xảy ra sai sót. Tham chiếu ví dụ, công thức, phương thức hoạt động và cách làm từ các nguồn được hỗ trợ. Viết chương trình cho đầu vào là các bộ số mẫu và đầu ra là kết quả từ tham số với công thức xây dựng, lưu trữ và đối chiếu với kết quả thực, hiệu chỉnh chương trình, tăng hiệu suất và thử với nhiều bộ số khác nhau. Viết chương trình vẽ hình từ kết quả từ mỗi sự thay đổi với tham số truyền vào. 2.CƠ SỞ LÝ THUYẾT 2.1.Ý tưởng Tìm hiểu và nắm bắt các khái niệm liên quan đến đường cong tham số B-spline, những khái niệm sẽ hỗ trợ và giúp đỡ cho các thao tác cơ bản trong việc nghiên cứu và triển khai đường cong tham số B-spline, tìm hiểu và triển khai ra các mối liên hệ cố hữu của những thuộc tính của đường cong tham số B-spline. Mối liên hệ cũng như sự phụ thuộc lẫn nhau của các thuộc tính, ảnh hưởng đến hình dạng, tính chất và đặc điểm hình ảnh, số liệu thực tế của đường cong. 2.2.Cơ sở lý thuyết - Các khái niệm + Bậc của đường cong B-spline + Các đỉnh điều khiển + Vector nút Các loại vector nút : + Vector nút đồng nhất (uniform) : các vector nút liền kề cách nhau một khoảng là hằng số. VD : [0,1,2,3,4,5,6,7,8] - + Vector nút open uniform : {𝑡𝑖 = 𝑡1 , 𝑖 ≤ 𝑑 − 1 𝑡𝑖+1 − 𝑡𝑖 = 𝑐𝑜𝑛𝑠𝑡, 𝑑 − 1 ≤ 𝑖 ≤ 𝑛 − 1 𝑡𝑖 = 𝑡𝑑+𝑛+1 , 𝑖 ≥ 𝑛 − 1 VD : [0,0,0,0,1,2,3,4,4,4,4] (d = 3) 3 + Vector nút không đồng nhất : vector nút sau không bé hơn vector nút trước VD : [0,0,1,1.5,2,3,4,9] - Lý thuyết đường cong B-spline + Gọi bậc của đường cong là d (d ≥ 1) + Đường cong có n+1 đỉnh điều khiển P0 ,P1,...Pn (n+1 ≥d) + Đường cong có d+n+2 nút điều khiển { t0, t1, t2, ...,tn+d+1 } + Khi đó ta có phương trình cơ bản của đường cong B-spline: 𝑛 𝑃 (𝑡 ) = ∑ 𝑁𝑖,𝑑 (𝑡 ). 𝑃𝑖 (𝑡𝑚𝑖𝑛 ≤ 𝑡 < 𝑡𝑚𝑎𝑥 ) 𝑖=0 ▪ Trong đó : ⮚ 𝑁𝑗,0 (𝑡) = {1 (𝑡𝑗 ≤ 𝑡 < 𝑡𝑗+1 ) 𝑛𝑔ượ𝑐 𝑙ạ𝑖 𝑏ằ𝑛𝑔 0 } ⮚ 𝑁𝑗,𝑘 (𝑡 ) = 𝑡−𝑡𝑗 𝑡𝑗+𝑘 −𝑡𝑗 𝑁𝑗,𝑘−1 (𝑡) + 𝑡𝑗+𝑘+1 −𝑡 𝑡𝑗+𝑘+1 −𝑡𝑗+1 𝑁𝑗+1,𝑘−1 (𝑡) ⮚ 𝑡𝑚𝑖𝑛 = 𝑡𝑑 , 𝑡𝑚𝑎𝑥 = 𝑡𝑛+1 ▪ Với ti ≤ t ≤ ti+1 , xét Nj,k : ⮚ Nếu j ≥ i+1 thì Nj,k = 0 ⮚ Nếu j+k < i thì Nj,k = 0 ⮚ Vậy với [𝑗 ≥ 𝑖 + 1 𝑗 ≤ 𝑖 − 𝑘 − 1 thì Nj,k = 0 ▪ Để tính P(t) với ti ≤ t < ti+1 , ta tính các giá trị từ Ni-d,d(t) cho đến Ni,d(t), khi đó giá trị của P(t) sẽ là : 𝑖 ∑ 𝑁𝑗,𝑑 (𝑡 ). 𝑃𝑗 𝑗=𝑖−𝑑 Ví dụ minh họa : Ví dụ 1 : Cho B-spline bậc k = 3 có n+1 = 4 đỉnh điều khiển : 𝑃0 (10,0,0); 𝑃1 (20,30,0), 𝑃2 (50,40,0), 𝑃3 (80,0,0) Các vector nút : U = 𝑢0 = 1, 𝑢1 = 2, 𝑢2 = 3, 𝑢3 = 4, 𝑢4 = 5, 𝑢5 = 6, 𝑢6 = 7, 𝑢7 = 8 Chọn u = 4.5 → 𝑢3 ≤ 𝑢 < 𝑢4 P(u) = 𝑁0,3 𝑃0 + 𝑁1,3 𝑃1 + 𝑁2,3 𝑃2 + 𝑁3,3 𝑃3 4 𝑁2,1 = 0 + 𝑁3,1 = 𝑁1,2 = 𝑁2,2 = 𝑁3,2 = 𝑁0,3 = 𝑁1,3 = 𝑁2,3 = 𝑁3,3 = Vậy 𝑢4 −𝑢 𝑢4 −𝑢3 𝑢−𝑢3 𝑢4 −𝑢3 𝑢−𝑢1 𝑢3 −𝑢1 𝑢−𝑢2 𝑢4 −𝑢2 𝑢−𝑢3 𝑢5 −𝑢3 𝑢−𝑢0 𝑢3 −𝑢0 𝑢−𝑢1 𝑢4 −𝑢1 𝑢−𝑢2 𝑢5 −𝑢2 𝑢−𝑢3 𝑢6 −𝑢3 . 𝑁3,0 = 0.5 . 𝑁3,0 + 0 = . 𝑁1,1 + . 𝑁2,1 + . 𝑁3,1 + . 𝑁0,2 + . 𝑁1,2 + . 𝑁2,2 + . 𝑁3,2 + 1 0.5 𝑢4 −𝑢 𝑢4 −𝑢2 𝑢5 −𝑢 𝑢5 −𝑢3 𝑢6 −𝑢 𝑢6 −𝑢4 𝑢4 −𝑢 𝑢4 −𝑢1 𝑢5 −𝑢 𝑢5 −𝑢1 𝑢6 −𝑢 𝑢6 −𝑢2 𝑢7 −𝑢 𝑢7 −𝑢3 1 = = 1 2 1 2 0.5 1 . 𝑁2,1 = 0 + . 𝑁3,1 = . 𝑁4,1 = 2 . 𝑁3,2 = . 𝑁4,2 = 2 1.5 1 2 . + 2 2 2 2 2 0.5 1 . = 2.5 1 3 . + 8 1.5 3 3 . + 4 0.5 1 3 8 3 3 4 1 8 1 48 1.5 3 . = 23 4 48 2.5 1 23 3 . = 8 . +0= 8 8 . = . +0= 3 1 1.5 1 0.5 1 . 𝑁1,2 = 0 + . 𝑁2,2 = . = 48 1 48 𝑃(4.5) = 𝑁0,3 𝑃0 + 𝑁1,3 𝑃1 + 𝑁2,3 𝑃2 + 𝑁3,3 𝑃3 1 23 23 1 = 48 𝑃0 + 48 𝑃1 + 48 𝑃2 + 48 𝑃3 = (35.417, 33.542, 0) Ví dụ 2 : 5 6 3.TỔ CHỨC CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN 3.1.Phát biểu bài toán Để thực hiện tính toán, cần dữ liệu của đường cong B-spline gồm : - Bậc của đường cong (gọi là d, với d ≥ 1) Các đỉnh điều khiển : P0 ,P1,...Pn (n+1 ≥d) - Các nút điều khiển : t0, t1, t2, ...,tn+d+1 - Một điểm trên đường cong B-spline (gọi là u, với u ∈ [td , tn ] Với bộ dữ liệu đầu vào ( input ) trên, kết quả nhận được sau quá trình tính toán ( output ) là tọa độ của điểm với tham số được truyền vào ban đầu 𝑛 𝑃 (𝑡 ) = ∑ 𝑁𝑖,𝑑 (𝑡 ). 𝑃𝑖 (𝑡𝑚𝑖𝑛 ≤ 𝑡 < 𝑡𝑚𝑎𝑥 ) 𝑖=0 3.2.Cấu trúc dữ liệu Chủ yếu sử dụng mảng động để lưu trữ và xử lý dữ liệu 3.3.Thuật toán - Để tính các giá trị từ Ni-d,d(t) cho đến Ni,d(t), ta cần tính các giá trị từ Ni-d+1,d-1(t) cho đến Ni,d-1(t). Tương tự, để tính các giá trị từ Ni-d+1,d-1(t) cho đến Ni,d-1(t), ta cần tính được các giá trị từ từ Ni-d+2,d-2(t) cho đến Ni,d-2(t) và tiếp tục. - Sơ đồ dưới đây giải thích cho phương pháp đã nêu, trong đó : ↓ đại diện cho biểu thức 𝑡𝑗+𝑘+1 −𝑡 𝑡𝑗+𝑘+1 −𝑡𝑗+1 𝑁𝑗+1,𝑘−1 (𝑡) và ↙ đại diện cho biểu thức 𝑡−𝑡𝑗 𝑡𝑗+𝑘 −𝑡𝑗 𝑁𝑗,𝑘−1 (𝑡 ) 7 - Thuật toán trên tính được các giá trị từ Ni-d,d(t) cho đến Ni,d(t). Bên cạnh đó, ta đã có công thức : 𝑃(𝑡 ) = ∑𝑛𝑖=0 𝑁𝑖,𝑑 (𝑡 ). 𝑃𝑖 (𝑡𝑚𝑖𝑛 ≤ 𝑡 < 𝑡𝑚𝑎𝑥 ). Từ đó dễ dàng tính được kết quả của P(t). 3.3.1Thuật toán sử dụng phương trình - Để tính các giá trị từ Ni-d,d(t) cho đến Ni,d(t), ta sử dụng thuật toán quy hoạch động để xây dựng mảng từ dưới lên, nghĩa là tính các giá trị bắt đầu từ Nj,0 ; Nj,1 ; ... Nj,k ; ... Nj,d1 ; Nj,d ( 𝑗 ∈ [𝑖 − 𝑑, 𝑖] ). Với mỗi giá trị Nj,k , ta có thể tính được nhờ giá trị của Nj-1,k-1 và Nj,k-1 đã tính được trước đó. Với cách làm này, ta cho giá trị của Ni,0 là 1 - Với k = 0, số bước tính là 1, với k = 1, số bước tính là 2. Vậy nên, với một giá trị k, ta có k+1 bước tính. - Do đó, để xây dựng mảng theo thứ tự từ dưới lên, ta có tổng cộng 1 + 2 + 3 + ... + d + (d+1) = (𝑑+1).(𝑑+2) 2 phép tính. Vậy độ phức tạp của thuật toán gần với O(d2). - Để lưu các kết quả tính được, có thể tạo các mảng có độ dài lần lượt là 1,2,...,d+1. Khi đó thuật toán sẽ tốn bộ nhớ O(d2). - Một cách làm khác là tạo ra 2 mảng, mảng thứ nhất dùng để chứa các giá trị ở bậc thấp hơn Nj,k-1, mảng thứ hai dùng để chứa các giá trị ở bậc Nj,k đang xét. Do đó có thể tính được các giá trị ở mảng thứ hai nhờ giá trị ở mảng thứ nhất. Sau khi đã tính xong hết các giá trị ở bậc k, ta gán các phần tử ở mảng thứ hai vào mảng thứ nhất, sau đó tiếp tục tính toán ở bậc k+1. Cách làm này chỉ tốn bộ nhớ O(d) 8 4.CHƯƠNG TRÌNH VÀ KẾT QUẢ 4.1.Tổ chức chương trình Chia chương trình thành các modules nhỏ, mang các chức năng, nhiệm vụ riêng biệt. Các modules lần lượt chịu trách nhiệm cho: khai báo, định nghĩa và xây dựng công thức cùng các tính chất liên quan đến đường cong tham số B-spline; khai triển và thực hiện việc xây dựng ma trận tương xứng; nạp chồng các toán tử tính toán có sẵn cho mục đích sử dụng với cấu trúc dữ liệu, các thư viện bên thứ ba; xây dựng, định nghĩa tính chất một điểm với các thuộc tính về tọa độ trong không gian ba chiều. Trong đó: - Module mang các chức năng liên quan đến định nghĩa và mô tả đường cong Bspline chứa class B-spline có các thuộc tính gồm độ của đường cong, vector nút, vector các điểm làm đỉnh điều khiển và một số vector, các đối tượng phục vụ trong việc tính toán với giải thuật De Boor. Class B-spline có các hàm thành phần gồm có các chức năng: lấy số liệu về độ của đường cong, vector nút, vector các tọa độ các điểm làm đỉnh điều khiển từ input. Hàm getMinAgentIndex dùng để lấy chỉ số của vector nút sao cho mọi tham số đầu vào đều không được bé hơn vector nút đấy. Hàm getMaxAgentIndex dùng để lấy chỉ số của vector nút sao cho mọi tham số đầu vào đều bé hơn vector nút đấy. - Module mang chức năng tạo lập ma trận và các hàm thực hiện những việc liên quan đến ma trận gồm có class Matrix với thuộc tính hàng, cột và vector dữ liệu ma trận. Có các hàm thành phần lấy dữ liệu về hàng ,cột từ ma trận được tạo lập. - Module nạp chồng toán tử, chức năng chính gồm có nạp chồng các toán tử toán học có sẵn cho việc tính toán trực tiếp lên các đối tượng được tạo từ các kiểu dữ liệu tự định nghĩa, các đối tượng từ các thư viện bên thứ 3 lẫn nạp chồng các toán tử nhập xuất trực tiếp cho đối tượng mà không thông qua các thao tác từng phần. - Module định nghĩa và mô tả một điểm trong không gian ba chiều, gồm các thuộc tính về tọa độ và các hàm thành phần phục vụ lấy dữ liệu tọa độ từ đối tượng được tạo ra - Module định nghĩa và mô tả tham số của đa thức bậc n. Có chức năng liên quan đến việc tính toán giữa các đa thức với nhau và tính giá trị của đa thức theo tham số đầu vào Mỗi module được chia thành các file riêng cùng tên khác định dạng file gồm hai định dạng là /.h/ và /.cpp/. Các file có định dạng /.h/ chứa các tiền khai báo cho thuộc tính và hàm thành viên của kiểu dữ liệu tự tạo cùng với các lời tiền khai báo hàm liên quan và được khai báo ngược lại ở đầu những file có sử dụng đến. Các file có định dạng /.cpp/ chứa các đoạn mã thực thi, được liên kết với chính file cùng tên có định dạng /.h/ để đảm bảo độ 9 chính xác về tên định danh, các đoạn mã thực thi này là nội dung của các lời tiền khai báo nằm ở file định dạng /.h/. Sử dụng thư viện bên thứ 3 ( OPENGL và GLUT để thao tác với thư viện OPENGL) để thực hiện các tác vụ vẽ hình và biểu diễn trong không gian ba chiều khi nhìn từ các góc khác nhau thông qua thao tác xoay không gian tưởng tượng. Có chức năng kiểm tra kết quả của việc tính toán đường cong B-spline một cách trực quan, sinh động. 4.2.Ngôn ngữ cài đặt Sử dụng ngôn ngữ c++ 4.3.Kết quả 4.3.1.Giao diện chính của chương trình Giao diện console Giao diện console, gồm các chức năng yêu cầu người dùng nhập vào các thuộc tính của đường cong B-spline: bậc của đường cong, số đỉnh và tọa độ của mỗi đỉnh, vector nút điều khiển, vector nút phụ thuộc vào tính chất không đồng nhất của đường cong B-spline để phục vụ quá trình tính toán. 10 Giao diện đồ họa Giao diện đồ họa, được vẽ bằng OPENGL, chứa đường cong B-spline được xây dựng với các thuộc tính được người dùng cung cấp. Giao diện đồ họa cho phép người dùng thao tác xoay để nhìn đường cong B-spline trong không gian ba chiều 4.3.2.Kết quả thực thi chương trình - Test 1, input gồm: + Bậc của đường cong B-spline: 3 + Bốn đỉnh điều khiển, lần lượt có các tọa độ: P1( -4; -4; 0 ), P2( -2; 4; 0 ), P3( 2; -4; 0), P4( 4; 4; 0 ) + Vector nút: V( 0.0; 0.0; 0.0; 0.0; 1.0; 1.0; 1.0; 1.0 ) Với các tham số u chạy từ 0 đến 1, có các tọa độ điểm tương ứng, được tính toán từ phương trình đường cong tham số B-spline. Một số tọa độ mẫu đã được tính toán, thành output của bài toán như: p(0.0) = ( -4.00; -4.00; 0 ) p(0.1) = ( -3.34; -2.05; 0 ) p(0.2) = ( -2.59; -0.86; 0 ) p(0.3) = ( -1.77; -2.06; 0 ) p(0.4) = ( -0.90; -0.03; 0 ) p(0.5) = ( 0; 0; 0 ) p(0.6) = ( 0.90; 0.03; 0 ) p(0.7) = ( 1.77; 0.26; 0 ) 11 p(0.8) = ( 2.59; 0.86; 0 ) p(0.9) = ( 3.34; 2.05; 0 ) - Test 2, input gồm: + Bậc của đường cong B-spline: 2 + Bốn đỉnh điều khiển, lần lượt có các tọa độ: P1( 0; -4; 0 ), P2( -5; 1; 0 ), P3( -2.5; 3.6; 0), P4( 0; 1.6; 0 ), P5( 2.5; 3.6; 0 ), P5( 5; 1; 0 ), P6( 0; -4; 0 ) 12 + Vector nút: V( 0.0; 0.0; 0.0; 0.33; 0.44; 0.56; 0.67; 1.0; 1.0; 1.0 ) - Với các tham số u chạy từ 0 đến 1, có các tọa độ điểm tương ứng, được tính toán từ phương trình đường cong tham số B-spline. Một số tọa độ mẫu đã được tính toán, thành output của bài toán như sau: p(0.00) = ( 0; -4; 0 ) p(0.65) = ( 2.92; 3.11; 0 ) p(0.05) = ( -1.35; -2.57; 0 ) p(0.70) = ( 3.43; 2.53; 0 ) p(0.10) = ( -2.38; -1.27; 0 ) p(0.75) = ( 3.63; 1.79; 0 ) p(0.15) = ( -3.11; -0.11; 0 ) p(0.80) = ( 3.53; 0.91; 0) p(0.20) = ( -3.53; 0.91; 0 ) p(0.85) = ( 3.11; -0.11; 0 ) p(0.25) = ( -3.63; 1.79; 0 ) p(0.90) = ( 2.38; -1.27; 0) p(0.30) = ( -3.43; 2.53; 0 ) p(0.95) = ( 1.35; -2.57; 0 ) p(0.35) = ( -2.92; 3.11; 0 ) p(0.40) = ( -2.14; 3.14; 0 ) p(0.45) = ( -1.12; 2.50; 0 ) p(0.50) = ( 0; 2.10; 0 ) p(0.55) = ( 1.12; 2.50; 0 ) p(0.60) = ( 2.14; 3.14; 0 ) 13 - Test 3, với bậc d của đường cong là 1000, input quá lớn nên chỉ có thể hiển thị bằng hình vẽ 14 4.3.3.Nhận xét, đánh giá Về cơ bản, hoàn thành yêu cầu nhập thông tin về thuộc tính, các số liệu liên quan đến đường cong B-spline để triển khai và tính toán cho ra được tọa độ các điểm trong không gian, thuộc đường cong B-spline thông qua công thức và các phương pháp tính toán đã được triển khai bên trong phần lệnh thực thi của chương trình với tham số được truyền vào, bằng việc thay đổi tham số sẽ cho ra các tọa độ khác nhau theo mục đích của người dùng. Hoàn thành được việc vẽ mô hình đường cong tham số B-spline dựa trên input, xoay được trong không gian ba chiều để quan sát đường cong theo các hướng khác nhau, hình vẽ đủ rõ ràng để làm tham khảo cho kết quả tính toán và đối chiếu với kết quả chuẩn khi cần. Phần giao diện tương tác đầy đủ thông tin, rõ ràng mạch lạc dễ theo dõi quá trình tính toán. Về thuật toán được sử dụng để tính toán trong chương trình, đánh giá các tiêu chí về độ phức tạp về thời gian, độ phức tạp về bộ nhớ và độ khó của giải thuật thì thuật toán đang sử dụng là đủ phù hợp để thực hiện được chương trình so với việc sử dụng các thuật toán tối ưu hơn về riêng các yếu tố thời gian, độ phức tạp … Về cấu trúc chương trình, việc chia chương trình thành các modules với chức năng riêng biệt, mỗi module được chia thành các file cùng tên khác định dạng, trong đó chỉ rõ chức năng của từng module thông qua định danh đủ rõ ràng để đánh giá được chương trình mà không cần xem qua phần lệnh thực sự. Mặc dù sự thiếu kinh nghiệm và kiến thức có thể khiến cho việc chia modules chưa hoàn toàn rõ ràng, nhưng chương trình cũng được 15 làm rõ ràng theo hết khả năng hiện tại, và đủ đáp ứng việc hiểu chương trình mà không cần đọc hết phần thân chương trình. 5.KẾT LUẬN VÀ HƯỚNG PHÁT TRIỂN 5.1.Kết luận - Đường cong tham số B-spline ứng dụng rất nhiều vào đời sống, trong việc phát triển đồ họa và kỹ thuật. Trong những đường cong phức tạp, có sự tham gia của quá nhiều thành phần và cần độ chính xác cao thì đường cong B-spline là một lựa chọn tốt để áp dụng. Với các đặc tính của đường cong tham số B-spline không đồng nhất, trong đó đường cong đồng nhất là một trường hợp con của đường cong không đồng nhất, người dùng có thể dựa vào đó phát triển công thức xây dựng đường cong và áp dụng theo mục đích. Đề án nghiên cứu được phát triển lấy đường cong B-spline làm chủ đạo để trước hết làm rõ được chức năng của đường cong, và triển khai, xây dựng công thức, vẽ được hình minh họa, thỏa mãn được những nhu cầu cơ bản trong việc sử dụng đường cong tham số dựa trên những số liệu vào. Nhờ vào việc nghiên cứu đề án đã hiểu được nhiều hơn về đường cong, cũng như cách xây dựng, và cách đường cong phụ thuộc vào tham số, phục vụ quá trình phát triển trong tương lai. 5.2.Hướng phát triển - Về cơ bản, cấu trúc dữ liệu cùng thuật toán được sử dụng trong chương trình là vừa đủ để có thể minh họa chính xác cách hoạt động và mô tả được tọa độ điểm trên đường cong dựa trên tham số đầu vào, cũng như vẽ hình minh họa trong giới hạn cho phép. Vì thế, có thể xem chương trình hiện tại như một phần mềm tham khảo khi tìm hiểu về đường cong B-spline, cách mô tả hình dạng và cách tọa độ thay đổi dựa trên tham số nếu có có nhu cầu. Một phiên bản vừa đủ chức năng cơ bản của các trang web mô tả đường cong tham số B-spline nổi tiếng như nurbscalculator.in. - Khi cải tiến chương trình, xây dựng giao diện giao tiếp đồ họa, và hiển thị tất cả thông tin trực quan hơn, cũng như tích hợp thêm cách khai triển các đường cong khác như bezier thì có thể xem chương trình là một phần mềm hoàn chỉnh và đủ tính xác đáng trong học thuật để làm nguồn tham chiếu chuẩn cho người dùng nếu cần. - Các modules được tạo ra trong dự án này có thể tái sử dụng vào nhiều mục đích khác nhau, bên cạnh đó việc thiết kế chương trình theo hướng này còn giúp cho việc chỉnh sửa, mở rộng dễ dàng hơn. 6.Tài liệu tham khảo ● http://www.cl.cam.ac.uk/teaching/1999/AGraphHCI/SMAG/node4.html 16 ● A general matrix representation for non-uniform B-spline subdivision with boundary control by G. Casciola a, L. Romani ● Trang web http://nurbscalculator.in/ ● Đường cong Bézier và B-spline - Khoa CNTT Đại học Bách Khoa Đà Nẵng Phụ lục Point3d.h #ifndef POINT3D_H #define POINT3D_H class Point3D{ private: double m_x{}; double m_y{}; double m_z{}; public: Point3D (double x, double y, double z); Point3D () = default; double getPoint_x() const ; double getPoint_y() const ; double getPoint_z() const ; }; #endif Point3d.cpp #include "Point3D.h" Point3D::Point3D (double x, double y, double z) : m_x{x}, m_y{y}, m_z{z} {} double Point3D::getPoint_x() const { return m_x; } double Point3D::getPoint_y() const{ return m_y; } double Point3D::getPoint_z() const{ return m_z; } 17 Polynomial.h #ifndef POLYNOMIAL_H_INCLUDED #define POLYNOMIAL_H_INCLUDED #include <vector> class Polynomial{ private: std::vector<float> _data{}; //coefficients of x^n + x^n-1 + ... + x + 1 public: Polynomial() = default; Polynomial(std::vector<float>); const std::vector<float>& getData() const{return _data;}; int getDegree() const {return _data.size()-1;} float calcPolynomial(float) const; }; #endif // POLYNOMIAL_H_INCLUDED Polynomial.cpp #include "Polynomial.h" #include <vector> Polynomial::Polynomial(std::vector<float> inp) : _data{inp} {}; float Polynomial::calcPolynomial(float variable) const{ float res = _data[0]; for(int i = 1; i < _data.size(); i++) res = res*variable + _data[i]; return res; } Matrix.h #ifndef MATRIX_H_INCLUDED #define MATRIX_H_INCLUDED #include <vector> class Matrix { private: int m_row{}; int m_collumn{}; std::vector< std::vector<float> > m_data{}; 18 public: Matrix(const std::vector< std::vector<float> > &); Matrix() = default; int getRow() const{return m_row;} int getCollumn() const{return m_collumn;}; std::vector< std::vector<float> > const &getData() const{return m_data;} }; #endif // MATRIX_H_INCLUDED Matrix.cpp #include "Matrix.h" #include <vector> #include <algorithm> Matrix::Matrix(const std::vector< std::vector<float> > &inputData){ int row = inputData.size(); int col = 0; for(int i = 0; i < row;i++) col = std::max(col,(int)(inputData[i].size())); m_data.resize(row); for(int i = 0; i < row;i++){ m_data[i].resize(col,0.0f); for(int j = 0; j < (int)(inputData[i].size());j++){ m_data[i][j] = inputData[i][j]; } } m_row = row; m_collumn = col; } OperatorOverloading.h #ifndef OVERLOADING_H #define OVERLOADING_H #include "Point3D.h" #include "Matrix.h" #include "Polynomial.h" #include <iostream> Point3D operator+ (const Point3D& fstP, const Point3D& secP); 19 Point3D operator* (const Point3D& point, double k); Point3D operator* (double k, const Point3D& point); std::istream& operator>> (std::istream& in, Point3D& point); std::ostream& operator<< (std::ostream& out,const Point3D& point); Matrix operator+(const Matrix&,const Matrix&); Matrix operator*(const Matrix&,const Matrix&); Polynomial operator+(const Polynomial&,const Polynomial&); Polynomial operator*(const Polynomial&,const Polynomial&); #endif OperatorOverloading.cpp #include "OperatorOverloading.h" #include <iomanip> #include <iostream> #include <algorithm> Point3D operator+ (const Point3D& fstP, const Point3D& secP){ double x = fstP.getPoint_x() + secP.getPoint_x(); double y = fstP.getPoint_y() + secP.getPoint_y(); double z = fstP.getPoint_z() + secP.getPoint_z(); return Point3D{x,y,z}; } Point3D operator* (const Point3D& point, double k){ double x = point.getPoint_x()*k; double y = point.getPoint_y()*k; double z = point.getPoint_z()*k; return Point3D{x,y,z}; } Point3D operator* (double k, const Point3D& point){ return Point3D(point*k); } std::istream& operator>> (std::istream& in, Point3D& point){ double x{}, y{}, z{}; in >> x; in >> y; in >> z; point = Point3D{x,y,z}; 20 return in; } std::ostream& operator<< (std::ostream& out,const Point3D& point){ out << std::fixed << std::setprecision(2); out << '(' << point.getPoint_x() << ';' << point.getPoint_y() << ';' << point.getPoint_z() << ')' ; return out; } /* void getNumber(std::ifstream& inf, double& number){ inf >> number; while ( inf.fail()){ inf.clear(); inf.ignore(1,'\n'); // inf >> number; } } std::ifstream& operator>> (std::ifstream& inf, Point3D& point){ double x{}, y{}, z{}; getNumber(inf, x); getNumber(inf, y); getNumber(inf, z); point = Point3D(x,y,z); return inf; } std::ofstream& operator<< (std::ofstream& outf, Point3D& point){ outf << '(' << point.getPoint_x() << ';' << point.getPoint_y() << ';' << point.getPoint_z() << ')' ; }*/ Matrix operator* (const Matrix &fstM,const Matrix &secM) // fstM = firstMatrix, secM = secondMatrix { if(fstM.getCollumn() != secM.getRow()) { std::cerr<<"Khong the thuc hien phep nhan ma tran "<<fstM.getRow()<<"x"<<fstM.getCollumn() <<" va ma tran "<<secM.getRow()<<"x"<<secM.getCollumn()<<"\n"; return Matrix(); 21 } int newRow{fstM.getRow()}; int newCol{secM.getCollumn()}; const std::vector< std::vector<float> > &fstData = fstM.getData(); const std::vector< std::vector<float> > &secData = secM.getData(); std::vector< std::vector<float> > result(newRow, std::vector<float>(newCol)); for(int i = 0; i < newRow; i++) //loop through all row of the first matrix { for(int j = 0; j < newCol;j++){ float sum{0}; for(int k = 0; k < fstM.getCollumn();k++){ sum += fstData[i][k]*secData[k][j]; } result[i][j] = sum; } } return Matrix(result); } Matrix operator+ (const Matrix &fstM,const Matrix &secM) { if((fstM.getRow() != secM.getRow()) || (fstM.getCollumn() != secM.getCollumn())){ std::cerr<<"Khong the thuc hien phep cong ma tran"<<fstM.getRow()<<"x"<<fstM.getCollumn() <<" va ma tran "<<secM.getRow()<<"x"<<secM.getCollumn()<<"\n"; return Matrix(); } int row = fstM.getRow(); int col = fstM.getCollumn(); std::vector< std::vector<float> > resData(row,std::vector<float>(col)); for(int i = 0; i < row;i++){ for(int j = 0; j < col;j++){ resData[i][j] = fstM.getData()[i][j] + secM.getData()[i][j]; } } return Matrix(resData); } Polynomial operator+(const Polynomial &fstP, const Polynomial &secP){ const std::vector<float> &fstData = fstP.getData(); const std::vector<float> &secData = secP.getData(); 22 int resSize = std::max(fstData.size(),secData.size()); std::vector<float> res(resSize,0); for(int i = 0; i < resSize; i++){ int a = fstData.size()-1-i; int b = secData.size()-1-i; res[resSize-1-i] = ((a >= 0)? fstData[a] : 0) + ((b >= 0)? secData[b] : 0); } return Polynomial(res); } Polynomial operator*(const Polynomial &fstP, const Polynomial &secP){ if(secP.getDegree() < 0) return fstP; if(fstP.getDegree() < 0) return secP; const std::vector<float> &fstData = fstP.getData(); const std::vector<float> &secData = secP.getData(); int resSize = fstP.getDegree() + secP.getDegree(); std::vector<float> res(resSize,0); for(int i = 0; i < (int)fstData.size();i++){ for(int j = 0; j < (int)secData.size();j++){ res[i+j] += fstData[i]*secData[j]; } } return Polynomial(res); } 23 Bspline.h #ifndef BSPLINE_H_INCLUDED #define BSPLINE_H_INCLUDED #include "Point3D.h" #include <vector> class Bspline{ private: int m_deg{}; std::vector<double> m_knots{}; std::vector<Point3D> m_ctrlPoints{}; // knots[minAgentIndex] <= agent < knots[maxAgentIndex] int m_minAgentIndex{}; int m_maxAgentIndex{}; public: Bspline(int,const std::vector<double> &,const std::vector<Point3D> &); Bspline() = default; int getDegree() const{return m_deg;} int getMinAgentIndex() const {return m_minAgentIndex;} int getMaxAgentIndex() const {return m_maxAgentIndex;} std::vector<double> const &getKnots() const {return m_knots;} std::vector<Point3D> const &getCtrlPoints() const {return m_ctrlPoints;} }; // generate an array of value from N(i-d,d) to N(i,d) std::vector<double> DeBoor(std::vector<double> const &,double,int,int); // calculate the position of an agent Point3D calcDeBoor(const Bspline &,double); //divide given bspline into segments and return an array of each segment's position std::vector<Point3D> calcBsplineSeg(const Bspline &,int); #endif // BSPLINE_H_INCLUDED Bspline.cpp #include "Bspline.h" #include "point3D.h" #include "OperatorOverloading.h" #include "Debugger.h" 24 #include "Polynomial.h" #include <iostream> #include <vector> #include <algorithm> //define bspline class Bspline::Bspline(int degree,const std::vector<double>& knots,const std::vector<Point3D>& controlPoints) : m_deg {degree}, m_knots {knots}, m_ctrlPoints {controlPoints}, m_minAgentIndex {degree}, m_maxAgentIndex {(int)controlPoints.size()} //m_ prefix mean member {} // -----------------------------------bspline calculation-----------------------std::vector<double> DeBoor(std::vector<double> const &knots,double agent,int idx, int deg){ // knots[idx] <= agent < knots[idx+1] std::vector<double> res(deg+1,0),temp(deg+1,0); // used to build the triangle temp[0] = 1; for(int cDeg = 1; cDeg <= deg; cDeg++)//build bottom-up { for(int offset = 0; offset <= cDeg; offset++)// calculate from N(icDeg+1,cDeg) to N(i,cDeg) { double a{0}, b{0}; int j{idx-cDeg+offset}; int k{cDeg}; if(offset != 0 && knots[j+k] != knots[j]) { a = ((agent - knots[j])/(knots[j+k]-knots[j]))*temp[offset-1]; } if(offset < cDeg && knots[j+k+1] != knots[j+1]) { b = ((knots[j+k+1] - agent)/(knots[j+k+1]-knots[j+1]))*temp[offset]; } res[offset] = a+b; } temp = res; } return res; } 25 std::vector<double> matrixMethod(std::vector<double> const &knots,double agent,int idx, int deg){ // knots[idx] <= agent < knots[idx+1] std::vector<Polynomial> res(deg+1),temp(deg+1); // used to build the triangle temp[0] = Polynomial({0}); for(int cDeg = 1; cDeg <= deg; cDeg++)//build bottom-up { for(int offset = 0; offset <= cDeg; offset++)// calculate from N(icDeg+1,cDeg) to N(i,cDeg) { Polynomial a,b; int j{idx-cDeg+offset}; int k{cDeg}; if(offset != 0 && knots[j+k] != knots[j]) { std::vector<float> t{1/(knots[j+k]-knots[j]) , -knots[j]/(knots[j+k]knots[j])}; a = Polynomial(t)*temp[offset-1]; } if(offset < cDeg && knots[j+k+1] != knots[j+1]) { std::vector<float> t{-1/(knots[j+k+1]-knots[j+1]) , knots[j+k+1]/(knots[j+k+1]-knots[j+1])}; b = Polynomial(t)*temp[offset]; } res[offset] = a+b; } temp = res; } std::vector<double> resVector(deg+1); for(int i = 0; i <= deg;i++) { resVector[i] = res[i].calcPolynomial(agent); } return resVector; } Point3D calcDeBoor(const Bspline &bsplineData,double agent){ const std::vector<double> &knots = bsplineData.getKnots(); const std::vector<Point3D> &ctrlPoints = bsplineData.getCtrlPoints(); 26 std::vector<double>::const_iterator it; std::vector<double> resVector; //calculate idx that knots[idx] <= agent < knots[idx+1] and idx < maxAgentIdx it = std::upper_bound(knots.begin(),knots.end(),agent); int idx = it - knots.begin()-1; if(idx > bsplineData.getMaxAgentIndex()) idx = bsplineData.getMaxAgentIndex()-1; int deg = bsplineData.getDegree(); Point3D res(0.0,0.0,0.0); resVector = DeBoor(knots,agent,idx,deg); for(int i = 0; i < deg+1;i++) { res = res + resVector[i]*ctrlPoints[i+(idx - deg)]; } return res; } std::vector<Point3D> calcBsplineSeg(const Bspline &splineData,int segmentNum){ std::vector<Point3D> result; double minAgent = splineData.getKnots()[splineData.getMinAgentIndex()]; double maxAgent = splineData.getKnots()[splineData.getMaxAgentIndex()]; float agentDist = (maxAgent - minAgent)/segmentNum; //push all agent position from min to nearly max for(double t = minAgent; t < maxAgent; t+= agentDist) { Point3D agentPoint = calcDeBoor(splineData,t); result.push_back(agentPoint); std::cout<<"P("<<t<<") = "<<agentPoint<<"\n"; } //push maximum agent position result.push_back(calcDeBoor(splineData,maxAgent)); return result; } Main.cpp #include "Modules/Point3D.h" #include "Modules/Bspline.h" #include "Modules/OperatorOverloading.h" 27 #include <GL/glut.h> #include <iostream> #include <vector> #include <math.h> #include <windows.h> #define DEFAULT_WIDTH 640 #define DEFAULT_HEIGHT 480 const int maxDegree = 1000; const int segmentNum = 20; const GLfloat minZoom = 0.01f; const GLfloat maxZoom = 100.0f; const GLfloat zoomSpeed = 0.1f; const GLfloat rotateSpeed = 5.0f; const GLdouble orthoNear = -50.0f; const GLdouble orthoFar = 50.0f; std::vector<Point3D> segPoints; Bspline splineData; GLfloat zoom = 1.0f; GLfloat orthoWidth = 10.0f; GLfloat orthoHeight = 10.0f; GLfloat axisLength = 100.0f; GLfloat curScreenWidth = DEFAULT_WIDTH; GLfloat curScreenHeight = DEFAULT_HEIGHT; Point3D bsplineCenter; Point3D camPosition; Point3D objectRotation; template <typename T> T getMin(T a, T b) { return (a > b)? b : a; } template <typename T> T getMax(T a, T b){ return (a > b)? a : b; } Bspline initBsplineStdin(){ int deg,pointCount; 28 std::vector<Point3D> ctrlPoints; std::vector<double> ctrlKnots; //Nhap bac cua bspline std::cout<<"Nhap bac cua bspline trong khoang gia tri [1,"<<maxDegree<<"] : "; std::cin>>deg; while(deg <= 0 || deg > maxDegree) { std::cout<<"Khong hop le, moi nhap lai bac cua bspline trong khoang gia tri [1,"<<maxDegree<<"] : "; std::cin>>deg; } //Nhap dinh dieu khien std::cout<<"Moi nhap so dinh dieu khien : "; std::cin>>pointCount; while(pointCount < deg) { std::cout<<"So dinh dieu khien khong be hon bac cua bspline\n"; std::cout<<"Moi nhap so dinh dieu khien : "; std::cin>>pointCount; } for(int i = 0; i < pointCount;i++) { float x,y,z; std::cout<<"Nhap toa do cua dinh thu "<<i+1<<" : "; std::cin>>x>>y>>z; std::cout<<"Toa do cua dinh thu "<<i+1<<" :\t("<<x<<","<<y<<","<<z<<")\n"; ctrlPoints.push_back(Point3D(x,y,z)); } ctrlPoints.shrink_to_fit(); //Nhap nut dieu khien for(int i = 0; i < deg + pointCount + 1; i++) { double t; std::cout<<"Moi nhap nut dieu khien thu "<<i+1<<" : "; std::cin>>t; ctrlKnots.push_back(t); } ctrlKnots.shrink_to_fit(); 29 return Bspline(deg,ctrlKnots,ctrlPoints); } void init(){ //white background glClearColor(1.0f,1.0f,1.0f,1.0f); glClearDepth(1.0f); glEnable(GL_DEPTH_TEST); } void initBspline(){ splineData = initBsplineStdin(); system("cls"); std::cout<<"Du lieu dau vao la B-spline bac "<<splineData.getDegree()<<" co :\n"; std::cout<<"Cac dinh dieu khien : \n"; for(Point3D p : splineData.getCtrlPoints()) std::cout<<p<<"\n"; std::cout<<"Cac vector nut : "; for(double x : splineData.getKnots()) std::cout<<x<<" "; std::cout<<"\n"; //using segPoints to draw bspline whenever reshape function called segPoints = calcBsplineSeg(splineData,segmentNum); //calculate center of bspline Point3D firstPoint = splineData.getCtrlPoints()[0]; double minX,maxX,minY,maxY,minZ,maxZ; minX = maxX = firstPoint.getPoint_x(); minY = maxY = firstPoint.getPoint_y(); minZ = maxZ = firstPoint.getPoint_z(); for(auto it = splineData.getCtrlPoints().begin(); it != splineData.getCtrlPoints().end();it++) { double thisX,thisY,thisZ; thisX = (*it).getPoint_x(); thisY = (*it).getPoint_y(); thisZ = (*it).getPoint_z(); minX = getMin(thisX,minX); minY = getMin(thisY,minY); minZ = getMin(thisZ,minZ); maxX = getMax(thisX,maxX); 30 maxY = getMax(thisY,maxY); maxZ = getMax(thisZ,maxZ); } bsplineCenter = Point3D((minX+maxX)/2,(minY+maxY)/2,(minZ+maxZ)/2); orthoWidth = orthoHeight = getMax(getMax(fabs(maxX-minX),fabs(maxYminY)),fabs(maxZ-minZ)); axisLength = orthoWidth/5; //Set camera position to object center camPosition = Point3D(bsplineCenter.getPoint_x(),bsplineCenter.getPoint_y(),bsplineCenter.getPoint_z ()-0.1f); //std::cout<<"Camera position : "<<camPosition.getPoint_x()<<","<<camPosition.getPoint_y()<<","<<camPosition.getP oint_z()<<"\n"; } void drawBspline(){ //glMatrixMode(GL_MODELVIEW); //glLoadIdentity(); glBegin(GL_LINE_STRIP); glColor3f(1.0f,0.0f,0.0f); for(auto it = segPoints.begin(); it!=segPoints.end(); it++) { Point3D thisPoint = *it; glVertex3f(thisPoint.getPoint_x(),thisPoint.getPoint_y(),thisPoint.getPoint_z()); } glEnd(); //draw line through all control points glBegin(GL_LINE_STRIP); glColor3f(0.8f,0.8f,0.8f); // grey for(auto it = splineData.getCtrlPoints().begin(); it != splineData.getCtrlPoints().end(); it++) { Point3D point = *it; glVertex3f(point.getPoint_x(),point.getPoint_y(),point.getPoint_z()); } 31 glEnd(); } void drawAxis(){ //glMatrixMode(GL_MODELVIEW); //glLoadIdentity(); glPushMatrix(); glTranslatef(bsplineCenter.getPoint_x(),bsplineCenter.getPoint_y(),bsplineCenter.getPoi nt_z()); // draw axis at center of bspline glBegin(GL_LINES); //x axis glColor3f(1.0f,0.0f,0.0f); glVertex3f(0.0f,0.0f,0.0f); glVertex3f(axisLength,0.0f,0.0f); //y axis glColor3f(0.0f,1.0f,0.0f); glVertex3f(0.0f,0.0f,0.0f); glVertex3f(0.0f,axisLength,0.0f); //z axis glColor3f(0.0f,0.0f,1.0f); glVertex3f(0.0f,0.0f,0.0f); glVertex3f(0.0f,0.0f,axisLength); glEnd(); glPopMatrix(); } void reshape(GLsizei width,GLsizei height){ if(height == 0) height = 1; glViewport(50,50,width,height); curScreenWidth = width; curScreenHeight = height; glutPostRedisplay(); //std::cerr<<"current width/height : "<<width<<","<<height<<"\n"; } void displayOrtho(){ GLfloat aspect = curScreenWidth/curScreenHeight; glMatrixMode(GL_PROJECTION); 32 glLoadIdentity(); GLfloat w = orthoWidth / zoom; GLfloat h = orthoHeight / zoom; if(curScreenWidth < curScreenHeight) glOrtho(-w,w,-h/aspect,h/aspect,orthoNear/zoom,orthoFar/zoom); else glOrtho(-w*aspect,w*aspect,-h,h,orthoNear/zoom,orthoFar/zoom); //look at bspline center gluLookAt(camPosition.getPoint_x(),camPosition.getPoint_y(),camPosition.getPoint_z(), bsplineCenter.getPoint_x(),bsplineCenter.getPoint_y(),bsplineCenter.getPoint_z(),0.0,1.0 ,0.0); } void display(){ displayOrtho(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(bsplineCenter.getPoint_x(),bsplineCenter.getPoint_y(),bsplineCenter.getPoi nt_z()); // draw axis at center of bspline glRotatef(objectRotation.getPoint_x(),1.0f,0.0f,0.0f); glRotatef(objectRotation.getPoint_y(),0.0f,1.0f,0.0f); glRotatef(objectRotation.getPoint_z(),0.0f,0.0f,1.0f); glTranslatef(-bsplineCenter.getPoint_x(),-bsplineCenter.getPoint_y(),bsplineCenter.getPoint_z()); // draw axis at center of bspline drawAxis(); drawBspline(); glTranslatef(-bsplineCenter.getPoint_x(),-bsplineCenter.getPoint_y(),bsplineCenter.getPoint_z()); // draw axis at center of bspline glutSwapBuffers(); } void rotateObject(double x,double y, double z){ 33 double rotX = objectRotation.getPoint_x()+x; double rotY = objectRotation.getPoint_y()+y; double rotZ = objectRotation.getPoint_z()+z; if(fabs(rotX) > 360.0) rotX += (rotX < 0)? 360.0 : -360.0; if(fabs(rotY) > 360.0) rotY += (rotY < 0)? 360.0 : -360.0; if(fabs(rotZ) > 360.0) rotZ += (rotZ < 0)? 360.0 : -360.0; objectRotation = Point3D(rotX,rotY,rotZ); //std::cout<<"Current rotation : "<<objectRotation<<"\n"; } void mouse(int button,int state,int x,int y){ if(button == 3) { zoom += zoomSpeed; zoom = (zoom > maxZoom) ? maxZoom : zoom; glutPostRedisplay(); } else if(button == 4) { //std::cout<<"ZOOM OUT\n"; zoom -= zoomSpeed; zoom = (zoom < minZoom) ? minZoom : zoom; glutPostRedisplay(); } //std::cerr<<"zoom factor :"<<zoom<<"\n"; } void keyboard(unsigned char key,int x,int y){ switch(key) { case 'a' : rotateObject(0.0,1.0*rotateSpeed,0.0); break; case 'd' : rotateObject(0.0,-1.0*rotateSpeed,0.0); break; case 'w' : rotateObject(1.0*rotateSpeed,0.0,0.0); break; case 's' : rotateObject(-1.0*rotateSpeed,0.0,0.0); 34 break; default : break; } glutPostRedisplay(); } void drawBsplineOpenGL(int argc, char *argv[]){ glutInit(&argc,argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); glutInitWindowPosition(100,100); glutInitWindowSize(DEFAULT_WIDTH,DEFAULT_HEIGHT); glutCreateWindow("B-spline calculator"); glutReshapeFunc(reshape); glutDisplayFunc(display); glutMouseFunc(mouse); glutKeyboardFunc(keyboard); init(); glutMainLoop(); } void calculatePoint(){ double minAgent = splineData.getKnots()[splineData.getMinAgentIndex()]; double maxAgent = splineData.getKnots()[splineData.getMaxAgentIndex()]; double t; do { std::cout<<"Nhap t("<<minAgent<<"<=t<"<<maxAgent<<") : "; std::cin>>t; }while((t<minAgent) || (t>=maxAgent)); std::cout<<"P("<<t<<") = "<< calcDeBoor(splineData,t)<<"\n"; } int choseAction() { int t = 0; std::cout<<"\n"; std::cout<<"Chon cong viec can thuc hien :\n"; std::cout<<"1.Tinh gia tri cua B-spline tai mot diem\n"; std::cout<<"2.Ve duong cong B-spline\n"; std::cout<<"3.Thoat chuong trinh \n"; std::cin>>t; if((t < 1) || (t > 3)) return -1; return t; 35 } int main(int argc, char *argv[]){ initBspline(); int t = 0; do { t = choseAction(); switch(t) { case -1 : std::cout<<"Khong hop le, moi nhap lai"; break; case 1 : calculatePoint(); break; case 2 : drawBsplineOpenGL(argc,argv); break; default : break; } }while(t != 3); return 0; } 36