Lập trình đồ họa trên Java 2D & 3D

Lời nói đầu Sự phát triển của khoa học, kĩ thuật, nghệ thuật, kinh doanh, và công nghệ luôn luôn phụ thuộc vào khả năng truyền đạt thông tin của chúng ta, hoặc thông qua các bit dữ liệu lưu trữ trong microchip hoặc thông qua giao tiếp bằng tiếng nói. Câu châm ngôn từ xa xưa “một hình ảnh có giá trị hơn cả vạn lời" hay "trăm nghe không bằng một thấy" cho thấy ý nghĩa rất lớn của hình ảnh trong việc chuyển tải thông tin. Hình ảnh bao giờ cũng được cảm nhận nhanh và dễ dàng hơn, đặc biệt là trong

doc303 trang | Chia sẻ: huyen82 | Lượt xem: 6937 | Lượt tải: 3download
Tóm tắt tài liệu Lập trình đồ họa trên Java 2D & 3D, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
trường hợp bất đồng về ngôn ngữ. Do đó không có gì ngạc nhiên khi mà ngay từ khi xuất hiện máy tính, các nhà nghiên cứu đã cố gắng sử dụng nó để phát sinh các ảnh trên màn hình. Trong suốt gần 50 năm phát triển của máy tính, khả năng phát sinh hình ảnh bằng máy tính của chúng ta đã đạt tới mức mà bây giờ hầu như tất cả các máy tính đều có khả năng đồ họa.Đồ họa máy tính là một trong những lĩnh vực lí thú nhất và phát triển nhanh nhất của tin học. Ngay từ khi xuất hiện, đồ họa máy tính đã có sức lôi cuốn mãnh liệt, cuốn hút rất nhiều người ở nhiều lĩnh vực khác nhau như khoa học, nghệ thuật, kinh doanh, quản lí, ... Tính hấp dẫn và đa dạng của đồ họa máy tính có thể được minh họa rất trực quan thông qua việc khảo sát các ứng dụng của nó. Ngày nay, đồ họa máy tính được sử dụng trong rất nhiều lĩnh vực khác nhau như công nghiệp, thương mại, quản lí, giáo dục, giải trí, … Số lượng các chương trình đồ họa ứng dụng thật khổng lồ và phát triển liên tục.Trong lĩnh vực công nghiệp,CAD(computer-aided design) đã được sử dụng hầu hết trong việc thiết kế các cao ốc, ô tô, máy bay, tàu thủy, tàu vũ trụ, máy tính,…Trong lĩnh vực giải trí,nghệ thuật, đồ họa máy tính giúp ta tạo ra các chương trình trò chơi,các kĩ xảo điện ảnh cho các nhà làm phim,hay ngay chính giao diện đồ họa đã làm nâng cao khả năng giao tiếp giữa người và máy tính. Để có thể làm được những ứng dụng đồ họa hữu ích cho cuộc sống,trước hết chúng ta cần phải có một nền tảng vững chắc về lập trình đồ họa.Có rất nhiều ngôn ngữ hỗ trợ lập trình đồ họa máy tính,mỗi ngôn ngữ được xây dưng trên những thư viện đồ họa riêng,có những thế mạnh riêng.Và khi nói về lập trình đồ họa,chúng ta không thể không nói đến ngôn ngữ Java,một ngôn ngữ rất mạnh trong lĩnh vực này.Với mục đích nghiên cứu,tìm hiểu và mang đến cho những ai muốn tìm hiều về lập trình đồ họa một tài liệu thiết thực,nhóm chúng em đã chọn đề tài làm tutorial về lập trình đồ họa trên nền tảng ngôn ngữ lâp trình Java,dựa trên các tài liệu training trên mạng của hãng Sun.Vì là lần đầu làm tài liệu tham khảo nên chúng em không tránh khỏi sai sót.Chúng em mong thầy xem xét và góp ý cho tài liệu này.Chúng em chân thành cảm ơn. Phần 1 Lập trình đồ họa với Java 2D Chương 1 Tổng quan về Java 2D API Java 2D™ API tăng cường về khả năng đồ hoạ, văn bản và ảnh của Abstract Windowing Toolkit (AWT), giúp phát triển về giao diện người sủ dụng và ứng dụng về JAVA trong một số lĩnh vực mới.Cùng với khả năng mạnh về đồ hoạ ,phông chữ và ảnh trong các API, thì Java 2D API còn hỗ trợ những thuộc tính và thành phần màu sắc đã được nâng cao, và thành công trong việc biểu diễn các bề mặt hình học tuỳ ý và một kiểu tô trát chuẩn cho máy in và thiết bị hiển thị. Java 2D API cũng cho phép tạo ra các thư viện đồ hoạ mở rộng,như các thư viện của CAD-CAM và các thư viện tạo hiệu ứng đặc biệt cho ảnh và đồ hoạ, cũng như việc tạo ra các bộ lọc đọc/viết file ảnh và đồ hoạ.Khi được sử dụng kết hợp với Java Media Framework Java Media APIs khác ,thì Java 2D APIs có thể được sử dụng để tạo ra và hiển thị hiện thực ảo và các dạng đa phương tiện khác. Java Animation và Java Media Framework APIs dưa trên Java 2D API để hỗ trợ việc tô trát(rendering). 1.1 Enhanced Graphics, Text, and imaging Nếu các phiên bản trước của AWT chỉ cung cấp các gói tô trát(rendering) đơn giản chỉ phù hợp cho việc rendering các trang HTML đơn giản,mà không đáp ứng đủ cho các dạng đồ hoạ,văn bản và ảnh phức tạp. Thì Java 2D™ API cung cấp gói tô trát đẩy đủ các tính năng bằng cách mở rộng AWT để hỗ trợ cho đồ hoạ và các thao tác rendering. Ví dụ như thông qua các lớp đồ hoạ cho phép vẽ hình chữ nhật,hình ôval,và các đa giác. Đồ hoạ 2D tăng cường về khái niệm của phép tô trát hình học bằng cách cung cấp một cơ chế cho phép rendering ảo của bất kì bề mặt hình học nào.Tương tư như vậy với Java 2D API bạn có thể vẽ các dạng đường với bất kì độ rộng và dạng hình học nào. Dạng hình học được cung cấp thông qua các phần thực thi implementations của Shape interface trong Graphic class, như hình chữ nhật 2D và Elip 2D. Các đường cong và hình cung cũng thuộc phần implementations của Shape. Các kiểu vẽ và tô cũng được cung cấp thông qua phần thực thi implementations của giao tiếp Paint và Stroke interfaces, ví dụ BasicStroke, GradientPaint, TexturePaint,và Color. AffineTransform định nghĩa các phép biến đổi trong toạ độ 2 chiều, gồm có phép lấy tỉ lệ,phép chuyển đổi toạ độ ,phép quay và phép xén cắt. Khung nhìn được định nghĩa bởi các phương thức giống nhau của lớp Shape mà chúng được dùng để định nghĩa các khung nhìn chung,ví dụ như Rectangle2D và GeneralPath. Thành phần màu sắc được cung cấp bởi các phương thức của lớp Composite, ví dụ AlphaComposite. Một kiểu Font chữ thì được định nghĩa bởi các collection của Glyphs. 1.2 Rendering Model Kiểu tô trát đồ họa đơn giản không có gì thay đổi khi thêm vào Java 2D™ APIs. Để tô trát đồ họa thì phải thiết lập các thông số về đồ họa và gọi phương thức của đối tượng Graphics. Trong Java 2D API, lớp Graphics2D mở rộng lớp Graphics để hỗ trợ thêm nhiều thuộc tính đồ họa và cung cấp thêm các phương thức cho quá trình tô trát. The Java 2D API tự động cân chỉnh những sai khác trong các thiết bị tô trát và cung cấp một kiểu tô trát thống nhất cho các dạng thiết bị khác nhau. Tại tầng ứng dụng,quá trình tô trát là giống nhau cho dù thiết bị cuối đó là màn hình hay máy in. Với Java 2 SDK, version 1.3 , Java 2D API còn hỗ trợp cho các môi trường đa màn hình (multi-screen environments). 1.2.1 Coordinate Systems Java 2D API bao hàm hai hệ tọa độ: Không gian người sủ dụng là hệ tọa độ logic và độc lập với thiết bị. Các ứng dụng thường sủ dụng hệ tọa độ này,tất cả các dạng hình học được tô trát bằng Java 2D đều được xác định trong hệ tọa độ này. Không gian thiết bị là hệ tọa độ phụ thuộc thiết bị,ứng với từng loại thiết bị cuối mà có hệ tạo độ khác nhau. Bằng ứng dụng máy ảo một cửa sổ hiển thị có thể mở rộng thêm nhiều thiết bị hiển thị vậy lý tạo nên môi trường đa màn hiển thị, hệ tọa độ của thiết bị này được sủ dụng làm hệ tọa độ của máy ảo sẽ được lấy làm hệ tọa độ cho tất cả các màn hiể thị còn lại. Hệ thống Java 2D sẽ tự động thực hiện những phép chuyển đổi cần thiết giữa không gian người sử dụng và không gian thiết bị . Mặc dù hệ tọa độ cho màn hình rất khác đối với hệ tọa độ cho máy in nhưng sự khác biệt này trong suốt đối với các ứng dụng. 1.2.1.1 User Space Hệ tọa độ này được chỉ ra trong hình 1-1. (0,0) x y Figure 1-1 User Space Coordinate System Không gian người sử dụng biểu diễn một cách trừu tượng đồng nhất cho các hệ tạo độ của tất cả các thiết bị có liên quan.Còn không gian thiết bị cho một thiết bị cụ thể có thể có cùng gốc tọa độ và hướng của các trục hoặc là có thể không.Ngoài ra các tọa độ của không gian người sử dụng có thể chuyển đổi một cách tự động vào không gian thiết bị phù hợp mỗi khi một đối tượng đồ họa được tô trát,thường thì các chương trình driver của các thiết bị thực hiện điều này. 1.2.1.2 Device Space Java 2D API định nghĩa 3 mức thông tin cấu hình để hỗ trợ cho việc chuyển đổi từ không gian người sử dụng sang không gian thiết bị. Thông tin này được chứa trong 3 lớp : • GraphicsEnvironment • GraphicsDevice • GraphicsConfiguration Hai trong số chúng sẽ biểu diễn tất cả các thông tin cần thiết cho việc xác định thiết bi tô trát và cho việc chuyển đổi tọa độ từ không gian người sử dụng sang không gian thiết bị. Ứng dụng có thể truy cập thông tin này nhưng không nhất thiết phải thực hiện bất kì phép chuyển đổi nào giũa hai hệ tọa độchuyển . Lớp GraphicsEnvironment mô tả tập các thiết bị mà đựợc chấp nhận bởi các ứng dụng Java trong một môi trường cụ thể.Các thiết bị sử dụng cho quá trình tô trát gồm có màn hình , má in và các bộ đệm ảnh.Lớp GraphicsEnvironment cũng bao gồm tập các font phù hợp trong môi trường đó. Lớp GraphicsDevice mô tả thiết bị tô trát như màn hình hoặc máy in.Mỗi cấu hình phù hợp của thiết bị được biểu diễn bởi lớp GraphicsConfiguration. Ví dụ,một thiết bị hiển thị SVGA có thể thực hiện trong một số chế độ màu như : 640x480x16 colors, 640x480x256 colors, and 800x600x256 colors. Màn hình SVGA được biểu diễn bởi một đối tượng GraphicsDevice và ứng với mỗi chế độ màu của màn hình SVGA sẽ được biểu diễn bởi một đối tượng GraphicsConfiguration.Một đối tượng kiểu GraphicsEnvironment có thể bao gồm 1 hay nhiều đối tượng GraphicsDevices. Ngược lại ,mỗi đối tượng GraphicsDevice có thể có 1 hay nhiều đối tượng kiểu GraphicsConfigurations. 1.2.2 Transforms Java 2D API có một chế độ chuyển tọa độ thống nhất. Tất cả các phép chuyển tọa độ,bao gồm các phép chuyển từ không gian người sử dụng đến không gian của thiết bị,đều được biểu diễn bởi đối tượng kiểu AffineTransform .AffineTransform định nghĩa các luật cho những thao tác trên các hệ tọa độ bằng các ma trận(các phép biến đổi ảnh bằng các bộ lọc). Cũng có thể thêm đối tượng AffineTransform cho các dạng đồ họa bằng các phép xoay ,lấy tỉ lệ ,chuyển đổi hoặc cắt tọa độ cho một mặt hình học ,văn bản hay ảnh khi chúng được tô trát.Các phép biến đổi được thêm vào này sẽ được ứng dụng cho các đối tượng đồ họa được tô trát trong trường hợp đó phép bién đổi này được thực hiện khi chuyển từ tọa độ không gian người sử dụng sang không gian của thiết bị. 1.2.3 Fonts Một xâu thường được hiểu là tập các kí tự tao thành.Khi một xâu được vẽ, hình dạng của nó sẽ được xác định bởi font mà chúng ta chọn.Tuy nhiên, hình dạng mà font sử dụng để hiển thị lên màn hình xâu đó không phải lúc nào cũng giống với hình dáng của mỗi kí tự riêng biệt. ví dụ sự kết hợp của 2 hay nhiều kí tự thường được thay thế bởi một hình dạng kí hiêu nào đó được gọi là ligature. Các hình dạng mà một phông chữ sử dụng để biểu diễn các kí tự trong một xâu được gọi là glyphs. Một font có thể biểu diễn một kí tự như chữ thường hoặc chữ hoa bằng cách sử dụng nhiều glyphs, hoặc biểu diễn các liên kết kí tự như fi chỉ biểu diễn bởi 1 glyph. Trong Java 2D API, một glyph chỉ đơn giản là một dạng (Shape) mà có thể đựoc thao tác và tô trát một cách giống nhau như bất kì với các dạng khác Shape. Một font có thể được xem như tập các glyph. Một font đơn có thể có rất nhiều kiểu khác nhau như kểi chữ đậm ,vừa,nghiêng ,gôtích…tất cả các kiểu chữ này trong một font có cùng thiết kế in và có thể ví như chúng là các thành viên trong cùng một gia đình. Hay nói cách khác ,một nhóm các glyphs với các kiểu riêng biệt tạo nên một dạng font,nhóm các dạng font tao nên môt họ font ,họ các font tạo nên một tập font và tập này có sẵn trong một đối tượng GraphicsEnvironment cụ thể nào đó. Trong Java 2D API, các font được xác định bởi một tên mà mô tả một dạng font riêng biệt nào đó,ví dụ Helvetica Bold. Nhưng điều này lại khác với fần mềm JDK 1.1 ,các font được mô tả bằng các tên lôgíc mà ánh xạ trên các dạng font khác nhau fụ thuộc liệu các dạng font đó có sắn trong môi trường đang xét không.Như để tương thích cho điều đó ; the Java 2D API hỗ trợ việc xác định các font bằng tên lôgíc cũng như bằng tên dạng font. Sử dụng Java 2D API, có thể soạn thảo và tô rát các xâu bao gồm nhiều font thuộc các họ font,hình dạng,kich thước, và thâm chí cả ngôn ngữ khác nhau. Các dạng của văn bản được lưu riêng biệt một cách lôgíc với sự sắp xếp các văn bản.Các đối tượng Font được sử dụng để mô tả các hình dạng hiển thị của font, và thông tin sắp xếp được lưu trong đối tượng TextLayout và TextAttributeSet . Việc lưu giứ các font và thông tin sắp xếp riêng biệt nhau làm dễ dàng hơn cho việc sử dụng cùng font chữ nhưng khác về cấu hình sắp xếp. 1.2.4 Images Ảnh được tạo nên từ tập các pixel . Một điểm ảnh hay một pixel sẽ định nghĩa thể hiện của một ảnh tại vùng quan sát của màn hình . Một mảng hai chiều của điểm ảnh được gọi là một raster. Thể hiện của một điểm ảnh có thể được định nghĩa một cách trực tiếp hoặc như là một chỉ số trong bảng màu dành cho ảnh. Trong ảnh màu đa màu (hơn 256 màu) các điểm ảnh thường trực tiếp biểu diễn luôn màu sắc và các đặc trưng khác cho mỗi vùng hiển thị của ảnh. Những ảnh như vậy sẽ có kích thước lớn hơn ảnh màu mà hiển thị bằng chỉ số(indexed-color images), nhưng chúng nhìn sẽ thật hơn . Trong ảnh màu hiển thị bằng chỉ số (indexed-color image), những màu sắc của ảnh sẽ bị giới hạn bởi bảng màu , và thường số lượng màu trong bảng màu sẽ ít hơn sovới ảnh thật. Tuy nhiên các ảnh khi được lưu như tập các chỉ số màu sẽ làm cho kích thước ảnh nhỏ đi.Định dạng này thường được dùng cho những ảnh có 256 màu. Ảnh trong Java 2D API có hai thành phần chính • Dữ liệu ảnh thô(các điểm ảnh) • Những thông tin cần thiết cho quá trình chuyển đổi các điểm ảnh Các luật cho việc chuyển đổi các điểm ảnh được lưu bởi đối tượng ColorModel. Đối với một điểm ảnh để hiển thị cần phải đi kèm với một kiểu màu sắc. Một dải màu là một thành phần của không gian màu sắc dành cho ảnh.Ví dụ các thành phần màu đỏ là các daỉ màu trong ảnh RGB . Một điểm ảnh trong ảnh dùng màu trực tiếp có thể được hiểu như một tập . Gói java.awt.image bao gồm một số phương thức ColorModel cho các biểu diễn thành phần điểm ảnh. Một đối tượng ColorSpace chứa các luật để sao cho tập các giá trị màu tương ứng với một màu sắc nhất định .Các phương thức của ColorSpace trong java.awt.color sẽ biẻu diễn các không gian màu sắc thông dụng, gồm có không gian màu RGB và gray scale. Lưu ý rằng một không gian màu không phải là một tập các màu sắc mà tập này định nghĩ các luật để có thể chuyển đổi các giá trị màu thành các màu tương ứng thông dịch. Việc chia không gian màu sắc thành các chế độ màu sẽ tạo nên sự linh hoạt hơn trong việc biểu diễn và chuyển đổi từ phép biểu diễn màu này sang một phếp biểu diẽn màu khác. 1.2.5 Fills and Strokes Với Java 2D API, có thể tô các hình băng cách sử dụng các kiểu but khác nhau và các kiểu tô khác nhau. Vì các chữ xét cho cùng cũng được biểu diễn bằng tập các glyph, nên các xâu kí tự cũng có thể được vẽ và tô. Các kiểu bút được định nghĩa băng các đối tượng kiểu Stroke.Có thể xác định độ rộng cũng như các nét cho các đường thẳng và đường cong. Các kiểu tô được định nghĩa bởi các phương thức thuộc đối tượng Paint. Lớp Color có sắn trong các phiên bản trước của AWT đây là một dạng đơn giản của đối tượng Paint được sử dụng để định nghĩa tô màu trơn (solid color). Java 2D API cung cấp thêm hai phương thức mới cho Paint là TexturePaint và GradientPaint. TexturePaint định nghĩa kiểu tô dùng mẩu ảnh đơn giản(simple image fragment ) mà được lặp như nhau. GradientPaint định nghĩa một kiểu tô màu loang(gradient) giữa hai màu. Trong Java 2D, rendering một đường nét của một hình và tô hình đó bằng một kiểu nào đó được thực hiên hai thao tác riêng biệt: • Sử dụng một trong các thuật toán draw để render các đường nét của hình sử dụng các kiểu bút được xác định bởi các thuộc tính của Stroke và kiểu tô được xác định bởi thuộc tính Paint. • Sử dụng phương thức fill để tô các vùng trong của hình với kiểu tô đã dược xác định. Khi một chuỗi kí tự được render,các thuộc tính hiện tại của Paint đựơc áp dụng cho các glyphs tạo nên chuỗi đó.Lưu ý thực chất đối tượng drawString sẽ tô các glyphs mà được render. Để vẽ các nét của các glyphs trong chuỗi kí tự cần phải chọn các đường nét và render chúng như đối với các hình sử dụng phương thức draw . 1.2.6 Composites Khi render một đối tượng mà chồng lên một đối tượng đã tồn tai trước đó cần phải xác định làm thế nào để kết hợp màu của các đối tuợng. Java 2D API có các luật cho việc kết hợp màu trong trường hợp này trong đối tượng Composite. Tuy nhiên có một số vấn đề trong cách tiếp cận này: • Sẽ thật khó trong trường hợp nếu màu Red và màu Blue được thêm vào hây không được thêm vào. • Việc kết hợp lôgíc sẽ không được hỗ trợ trong trường hợp cácmàu thuộc các không gian màu khác nhau. • Sự kết hợp sẽ không có ý nghĩa trong trường hợp màu được biểu diễn bằng các gia trị màu thì khi đó sự kết hợp hai điểm ảnh là sự kết hợp của hai giá trị màu. Java 2D API tránh các loĩi này bằng cách thực hiện các luật pha trộn alpha mà sẽ đưa vào các thông tin về kiểu biểu diễn điểm ảnh mỗi khi thực hiện kết hợp màu. Một đối tượng AlphaComposite bao gồm các kiểu về màu sắc của các màu sắc của nguồn và đích. Backward Compatibility and Platform independence Như đã nói,Java 2D™ API có sự tương thích đối với phần mềm JDK 1.1 . Và Java 2D™ API là một kiến trúc chính vì vậy mà các ứng dụng của nó có tính độc lập về môi trường 1.3.1 Backward Compatibility Để tương thích với các tính năng đã tồn tại trong đồ họa JDK .Java 2D API tăng cường thêm một số tính năng cho AWT bằng cách thêm các phương thức mới vào các lớp đã tồn tại,mở rộng thêm các lớp đã tồn tại và đồng thời thêm các lớp mới và các interface mới mà không ảnh hưởng tới các API hợp lệ. Ví dụ,Có rất nhiều tính năng củaJava 2D API được bắt nguồn từ việc mở rộng các thuộc tính về đồ họa trong Graphics2D. Để cung cấp các thuộc tính mở rộng về đồ họa trong đó bao gồm cả yếu tố tương thích với các phần mềm đồ họa khác, Graphics2D mở rộng các lớp Graphics trong JDK1.1. • paint • paintAll • update • print • printAll • getGraphics JDK 1.1 applet thông dịch ngữ cảnh đồ họa mà được đưa vào như là một thể hiện của lớp Graphics. Để có thể truy nhập những chức năng được thực thi trong đối tượng Graphics2D, thì một applet tương thích với Java 2D API sẽ đưa ngữ cảnh đồ họa tới đối tượng Graphics2D: public void Paint (Graphics g) { Graphics2D g2 = (Graphics2D) g; ... ... g2.setTransform (t); } Trong môt vài trường hợp , hay đúng hơn là mở rộng một lớp thừa kế, Java 2D API khái quat hóa nó. Có hai kĩ thuật đã được sử dụng để khái quat hóa các lớp kế thừa: • Một hay nhiều lớp cha được chèn vào biểu đồ phân cấp của lớp , và các lớp thừa kế được cập nhật để mở rộng các lớp cha mới. Kĩ thuật này được sử dụng để thêm các phương thức và các dữ liệu thể hiện cho lớp kế thừa. • Một hay nhiều triển khai các interface được thêm vào lớp kế thừa . Kĩ thuật này được sử dụng để thêm các phương thức ảo tổng quát(general abstract methods) cho lớp kế thừa . Ví dụ, Java 2D API khái quát hóa lớp AWT Rectangle bằng cách sử dụng cả hai kĩ thuật Sự phân cấp cho hình chữ nhật giống như: Trong phần mềm JDK 1.1 , Rectangle là một đối tuợng mở rộng đơn giản. Bây giờ mở rộng các lớp Rectangle2D mới và triển khai cả hai interface Shape và Serializable. Hai lớp cha được thêm vào phân cấp của lớp Rectangle là RectangularShape và Rectangle2D. Các Applet được viết cho phần mềm JDK 1.1 không truy cập được các lớp mới và các phần triển khai của các interface,nhưng điều này không ảnh hưởng bởi vì lớp Rectangle vẫn bao gồm các phương thức và các thành phần có trong các phiên bản trước. 1.3.2 Platform independence Có khả năng pháp triển các ứng dụng độc lập với môi trường. Java 2D API không đảm đương về không gian màu sắc hay là chế độ màu của thiết bị rendering và Java 2D API có bất kì định dạng ảnh cụ thể nào. Và chỉ độc lập về font khi các font đó đã được xây dựng sẵn(được cung cấp như là một phần của phần mềm JDK ),hay là khi chúng được gởi tạo bằng lập trình. Java 2D API không hỗ trợ các font xây dựng sẵn và các font được tạo nên nhờ chương trình tạo font nhưng nó lại hỗ trợ việc định nghĩa các font thông qua tập các glyph của nó. Mỗi glyph lại có thể được định nghĩa bởi Shape mà bao gồm các đoạn đường thẳng và các đường cong. Có rất nhiều kiểu font với nhiều hình dang và kich thước được bắt nguồn từ tập các glyph đơn lẻ. 1.4 The Java 2D™ API Packages Các lớp Java 2D API được tổ chức trong các package sau: • java.awt • java.awt.geom • java.awt.font • java.awt.color • java.awt.image • java.awt.image.renderable • java.awt.print Package java.awt bao gồm các lớp Java 2D API đã đề cập ở trên và các interface đã tồn tại trước đó và các interface fát triển thêm. (Rõ dàng là không phải tất cả các lớp trong java.awt là các lớp của Java 2D ) Package java.awt.geom bao gồm các classe và interface có liên quan đến việc định nghĩa các dạng hình học nguyên thủy: Có nhiều dạng hình học nguyên thủy ứng với các phần implementation. Float và Double Điều này cho phép các implementation có thể ở hai mức chính xác là giá trị single và double. Gói java.awt.font chứa các lớp và các giao diện được sử dụng cho việc bố trí văn bản và định nghĩa các kiểu font: Gói java.awt.color chứa các lớp và các giao diện cho việc định nghĩa các không gian màu và các mẫu màu: Các gói java.awt.image và java.awt.image.renderable chứa các lớp và các giao diện cho việc định nghĩa và tạo bóng của ảnh. Package java.awt.image tồn tại trong các version trước đó của AWT. Java 2D API tăng cường thêm các lớp ảnh kế thừa từ AWT sau đây: • ColorModel • DirectColorModel • indexColorModel Các lớp chế độ màu sắc này vẫn tồn tại trong package java.awt.image package cho sự tương thích .Để đảm bảo tính thống nhất,các lớp cho chế độ màu mới cũng có trong package java.awt.image . Package java.awt.print bao gồm các lớp và các interface cho phép in tất cả các dạng văn bản ,đồ họa và ảnh dựa trên công nghệ Java 2D. Chương 2: Rendering with Graphics2D Graphics2D mở rộng java.awt.Graphics để tạo ra điều khiển tinh vi hơn về biểu diễn các hình ,văn bản và ảnh .Quá trình tô trát Java 2D™ được điều khiển thông qua đối tượng Graphics2D và các thuộc tính trạng thái của nó. Các thuộc tính tạng thái Graphics2D , như là các kiểu đường thẳng và các phép biến đổi,được áp dụng cho các đối tượng đồ họa khi chúng được tô trát. Tập các thuộc tính trạng thái kết hợp với Graphics2D được tham chiếu tới như là Graphics2DContext. Để tô trát văn bản ,hình và ảnh,cần thiết lập Graphics2D context và sau đó gọi một trong các phương thức tô trát Graphics2D như draw hay fill. 2.1 Các lớp và giao diện. Bảng cho sau đây sẽ liêt kê các giao diện và lớp trong việc kết hợp với ngữ cảnh Graphics2D context,bao gồm các lớp biểu diễn các thuộc tính trạng thái. Hầu hết các lớp này là một phần của package java.awt. Các giao diện và mô tả chúng: Giao diện Mô tả Composite Định nghĩa các phương thức cho việc kết hợp một đối tượng vẽ gốc với cùng đồ họa được đánh dấu. Được thực thi bởi phương thức AlphaComposite. CompositeContext Định nghĩa một môi trương tối ưu và độc lập cho việc thực hiện các phép kết hợp .Việc thực hiện các luật kết hợp tùy chọn được thực hiện bơit người lập trình. Paint Mở rộng: Transparency Định nghĩa các màu sắc cho việc tô hoặc vẽ. Được thực thi bởi đối tượng Color,Gradient-Paint và TexturePaint. PaintContext Định nghĩa một môi trương tối ưu và độc lập cho việc vẽ.Các thao tác vẽ được tùy chọn bởi người lập trình. Stroke Tạo ra một đối tượng Shape mà các đường viền của nó đựoc tạo bóng.Được thực thi bởi phương thức BasicStroke. Các lớp và mô tả sơ lược về chúng: Lớp Mô tả AffineTransform (java.awt.geom) Miêu tả một phép biến đổi 2D affine ,mà thực hiện việc ánh xạ tuyến tính từ tọa độ 2D này đến tọa độ 2D khác. AlphaComposite Thực thi : Composite Thực hiện các luật kết hợp các giá trị alpha cơ bản cho các đối tượng Shape,Text và Image. BasicStroke Thực thi :Stroke Định nghĩa kiểu bút vẽ( “pen style”) được áp dụng cho các đường bao của đối tượng Shape. Color Thực thi: Paint Định nghĩa một kiểu tô màu đồng nhất cho đối tượng Shape. GradientPaint Thực thi: Paint Định nghĩa một mẫu tô màu loang tuyến tính cho đối tượng Shape.Mẫu này sẽ thay đổi từ màu C1 (tại điểm P1) tới màu C2(tai điểm P2). Graphics2D Mở rộng: Graphics Lớp cơ sở cho quá trình tạo bóng trong không gian 2D.Mở rộng lớp gốc java.awt.Graphics TexturePaint Thực thi: Paint Định nghĩa một kiểu tô theo mẫu cho đối tượng Shape.Mẫu tô được tạo ra từu đối tượng BufferedImage. 2.2 Rendering Concepts Để tô trát một đối tượng đồ họa sử dụng Java 2D™ API, cần thiết lập ngữ cảnh Graphics2DContext và sử dụng một trong các phương thức tô trát Graphics2D đối với đối tượng. Có thể thay đổi các thuộc tính trạng thái mà tạo nên ngữ cảnh Graphics2D như : • Thay đổi độ rộng của nét bút • Thay đổi cho các nét bút kết hợp với nhau như thế nào. • Thiết lập một khung cắt để giới hạn vùng được tô trát. • Chuyển đổi tọa độ ,xoay,lấy tỉ lệ hoặc cắt cá đối tượng khi chúng được render. • Định nghĩa các màu và các kiểu để tô các hình. • Xác định các đối tượng đa đồ họa(multiple graphics objects) cần phải được tạo ra như thế nào. Graphics2D định nghĩa một số phương thức để thêm và thay đổi các thuộc tính trong ngữ cảnh đồ họa . Hầu hết những phương thức này là những đối tượng biểu diễn các thuộc tính riêng biệt như đối tựơng Paint hay Stroke . Ngữ cảnh Graphics2D cất giữ các tham chiếu tới các đối tương thuộc tính . Nếu biến đổi một đối tượng thuộc tính mà là một phần của ngữ cảnh Graphics2D ,thì cần phải gọi phương thức thiết lập phù hợp để khai báo ngữ cảnh.Thay đổi một đối tượng trong quá trình thao tác render sẽ gây nên một số biểu hiện bất thương thường và không ổn định . 2.2.1 Rendering Process Khi một đối tượng đồ họa được render, thông tin về hình học ,ảnh và thuộc tính được kết hợp để tính toán những giá trị điểm ảnh cần phải thay đổi trên màn hiển thị. Quá trình render cho một đối tượng Shape có thể được chia thành 4 bước : 1. Nếu đối tượng Shape được taọ nét ,đối tượng Stroke quy vào ngữ cảnh Graphics2D được sử dụng để tạo nên một đối tượng Shape mới bao quanh những đường nét đó . 2. Tọa độ của đường thuộc đối tượng Shape được biến đổi từ không gian người sử dụng sang không gian của thiết bị theo các thuộc tính biến đổi trong ngữ cảng đồ họa Graphics2D . 3. Đường (path)của đối tượng Shape được cắt bỏ bằng cách sử dụng các thuộc tính cắt trong ngữ cảnh Graphics2D. 4. Phần còn lại của đối tượng Shape được tô băng cách sử dụng thuộc tính của đối tượng Paint và Composite thuộc tính trong ngữ cảnh của đối tượng Graphics2D. Tạo bóng (Rendering) văn bản tương đương với việc tạo bóng các đối tượng thuộc kiểu Shape, khi đó văn bản được tạo bóng với từng glyph và mỗi glyph là một đối tượng thuộc kiểu Shape. Chỉ có điều khác là Java 2D API phải xác định đối tượng Font nào sử dụng cho văn bản và lấy kiểu glyph tương ứng từ đối tượng Font trước khi tạo bóng. Đối với ảnh thì có sự khác biệt , sự chuyển đổi và thực hiện các phép cắt bỏ với hộp xác định biên ảnh( image’s bounding box). Thông tin về màu sắc lấy từ chính ảnh đó và nguồn alpha(alpha channel) được sử dụng kết hợp với thuộc tính hiện tại của Composite khi các điểm ảnh được hợp trên bề mặt tạo bóng. 2.2.2 Controlling Rendering Quality Java 2D API đưa ra các lựa chọn cho phép người sử dụng tạo bóng nhanh hay tạo bóng với chất lượng cao. Những tham chiếu của người su dụng được xác định như là các chỉ dẫn thông qua thuộc tính của đối tượng RenderingHints trong ngữ cảnh của đối tượng Graphics2D . Không phải tất cả các môi trường đều hỗ trợ việc thay đổi chế độ tạo bóng vì vậy xác định các chỉ dẫn cho quá trình tạo bóng sẽ không được đảm bảo chúng sẽ được sử dụng hay không . Lớp RenderingHints hỗ trợ cá kiểu chỉ dẫn sau đây: • Alpha interpolation - có thể thiết lập tùy chọn default, quality, speed. • Antialiasing - có thể thiết lập tùy chọn default :on hoặc off. • Color Rendering - có thể thiết lập tùy chọn default, quality, hoặc speed. • Dithering - có thể thiết lập tùy chọn default:disable, enable. • Fractional Metrics - có thể thiết lập tùy chọn default, on, hoặc off. • Interpolation- có thể thiết lập tùy chọn nearest-neighbor, bilinear, hoặc bicubic. • Rendering - có thể thiết lập tùy chọn default, quality, hoặc speed. • Text antialiasing - có thể thiết lập tùy chọn default: on hoặc off. Để thiết lập hoặc thay đổi các thuộc tính RenderingHints trong ngữ cảnh của đối tượng Graphics2D thì gọi phương thức setRenderingHints. Khi một chỉ dẫn được thiết lập măc định ,môi trường tạo bóng mặc định sẽ được sử dụng . Kĩ thuật làm trơn(Antialiasing) Khi các thực thể cơ sở được tạo bóng trên thiết bị hiển thị ,các biên(cạnh) của chúng có thể không trơn hay nhám là do thuộc tính aliasing. Các đường cung và đường chéo có dạng nhám bởi vì chúng được xấp xỉ bởi các điểm ảnh mà gần nhất về hìn dạng với chúng . Đây là điều mà chúng ta có thể nhận thấy với các thiết bị trước đây với các cạnh nhám xuất hiện trái ngược hoàn toàn với các cạnh trơn của đườgn nằm ngang hay thẳng đứng. Antialiasing là một kĩ thuật được sử dụng để tạo bóng các đối tượng với các cạnh trơn hơn. Thay vì sử dụng các điểm ảnh gẩn giống với các đường chéo và đường cong ,tăng mật độ các điểm ảnh bao quanh tới các vùng được tạo bóng. Điều này làm cho cách cạnh trơn hơn và trải rộng sự chuyển tiếp bật/tắt với các pixel đa điểm(multi pixel) . Tuy nhiên kĩ thuật antialiasing đòi hỏi nhiều về tài nguyên máy tính và làm tăng thời gian tạo bóng. 2.2.3 Stroke Attributes Tạo nét một đối tượng thuộc kiểu Shape như đối tượng GeneralPath tương đươnng với việc sử dụng một bút lôgíc theo các đoạn của đối tượng GeneralPath. Thuộc tính của Graphics2DStroke định nghĩa các tính chất của nét bút vẽ. Đối tượng BasicStroke được sử dụng để định nghĩa các thuộc tính đường nét cho một ngữ cảnh của Graphics2D. BasicStroke đinh nghĩa các thuộc tính như độ rộng cuả nét vẽ ,mẫu tô… Để thiết lập hay thay đổi các thuộc tính Stroke trong ngữ cảnh của Graphics2D thì gọi phương thức setStroke. Như ví du,ảnh đầu tiên trong hình 2-3 sử dụng miter join-style,và hình thứ hai sử dụng kiểu round join-style, a round endcap style, and a dashing pattern. Các phương thức tạo bóng Graphics2D có sử dụng thuộc tính của Stroke để vẽ như drawArc, drawLine, drawOval, drawPolygon, drawPolyline, drawRect, và drawRoundRect.Khi một trong các phương thức được gọi thì các đường nét của đối tượng Shape đã xác định sẽ được tậo bóng. Thuộc tính Stroke định nghĩa các tính chất của đường thẳng và các thuộc tính của Paint định nghĩa màu sắc hay mẫu của nét bút. Ví dụ phương thức draw(myRectangle) được gọi: 1. Thuộc tính của Stroke sẽ quy định cho đường nét của hình chữ nhật . 2. Các đường nét này sẽ được biến đổi thành một đối tượng kiểu Shape . 3. Đối tượng Paint is được áp dụngcác điểm ảnh thuộc miền giới hạn bởi đường bao của đối tựơng Shape. Quá trình xử lý này được minh hạo trong hình 2-4: 2.2.4 Fill Attributes Thuộc tính tô màu trong ngữ cảnh Graphics2D được biểu diễn bởi đối tượng Paint. Có thể thêm một đối tượng vào ngữ cảnh của._. đối tượng Graphics2D (Graphics2D context) bằng cách gọi phương thức setPaint. Khi một đối tượng Shape hay là các glyph được vẽ (bởi các phương thức Graphics2D.draw, Graphics2D.drawString), Đối tượng Paint này sẽ được áp dụng tới tất cả các pixel nằm trong đối tượng Shape mà biểu diễn các đường nét bao quanh đối tượng . Khi một đối tượng Shape được tô màu (Graphics2D.fill), đối tượng Paint được áp dụng tới tất cả các điểm ảnh nằm trong đường bao của đối tượng Shape. Các kiểu tô màu đồng nhất đơn giản (solid color) được khởi tạo bởi phương thức setColor.Color là phần triển khai (implementation) đơn giản nhất của giao tiếp Paint (Paint interface). Để tô các hình với các kiểu tô phức tạp hơn như là tô loang(gradient) và tô dệt ( texture) bằng cách sử dụng các lớp trong Java 2D Paint là GradientPaint và TexturePaint. Các lớp này sẽ loại bỏ các công việc tiêu tốn thời gian để tạo các kiểu tô phức tạp bằng cách sử dụng các kiểu tô mầu đồng nhất đơn giản . Khi gọi phưong thức fill để tạo bóng một đối tượng Shape,thì hệ thống sẽ: 1. Xác định các điểm ảnh nào để thể hiện đối tượng đó. 2. Xác định màu cho mỗi điểm ảnh từ đối tựợng Paint. 3. Chuyển các màu này tới giá trị điểm ảnh tương ứng cho các thiết bị hiển thị. 4. Ghi các điểm ảnh ra thiết bị hiển thị đó. Quá trình xử lý theo phiên Để tổ chức hợp lý quá trình xử lý các điểm ảnh , Java 2D API tổ chức chúng theo từng phiên. Mỗi phiên có thể là tập các điểm ảnh kề nhau trên một đường quét hay là một khối (block)các điểm ảnh. Quá trình xử lý theo phiên được thực hiện theo 2 bước: 1. Phương thức createContext của đối tượng paint được gọi để tạo một đối tượng PaintContext. Đối tượng PaintContext này sẽ lưu thông tin ngữ cảnh về thao tác tạo bóng hiện thời và những thông tin cần thiết để tạo ra các màu. Phương thức createContext method is passed the bounding boxes of thegraphics object being filled in user space and in device space,đối tượng ColorModel là nơi để tạo các màu sắc,và thực hiện quá trình chuyển đổi để ánh xạ từ không gian người sử dụng vào không gian của thiết bị. Đối tượng ColorModel xem như một chỉ dẫn không phải tất cả các đối tượng Paint có thể hỗ trợ một đối tượng ColorModel bất kỳ. 2. Phương thức getColorModel được gọi để nhận giá trị của đối tượng ColorModel cho màu vẽ từ đối tượng PaintContext. Phương thức getRaster sau đó được gọi lặp lại nhiều lần để nhận giá trị của đối tựợng Raster mà bao gồm dữ liệu màu thật cho mỗi phiên. Thông tin này passed to the next stage in the rendering pipeline, mà vẽ các màu đã được tạo bằng cách sử dụng đối tựợng Composite hiện thời . 2.2.5 Clipping Paths Một khung nhìn sẽ xác định phần của đối tượng Shape hay Image cần được tạo bóng . Khi một khung nhìn là một phần của ngữ cảnh của đối tượng Graphics2D, thì chỉ những phần của đối tượng Shape hay image nằm trong khung nhìn đó mới được tạo bóng. Để thêm khung nhìn cho ngữ cảnh của Graphics2D thì goị phương thức setClip. Bất kì đối tượng Shape cũng có thể được sử dụng để định nghĩa một khung nhìn. Để thành đổi khung nhìn cần phải sử dụng phương thức setClip để xác định một khung nhìn mới hay là gọi phương thức clip để thay đổi khung nhìn cho phần giao nhau giữa khung nhìn cũ và một đối tượng Shape. 2.2.6 Transformations Ngữ cảnh của đối tượng Graphics2D bao gồm một phép bién đổi mà được sử dụng để biến đổi những đối tượng từ không gian người sử dụng vào không gian thiết bị trong quá trình tạo bóng. Để thực hiện các phép biến đổi khác như phép quay hay lấy tỉ lệ thì cần phải thêm các phép biến đổi khác vào ngữ cảnh của đối tượng.Các phép biến đổi được thêm vào này sẽ trở thành một phần của ống biến đổi (pipeline of transformations) mà được ápdụng trong suôt qua trình tạo bóng. Graphics2D cung cấp một vài cách khác để thay đổi phép biến đổi trong ngữ cảnh của đối tượng Graphics2D . Cách đơn giản nhất là gọi một trong các phương thức biến đổi trong đối tượng Graphics2D như : rotate, scale, shear, hay translate. Xác định các tính chất của phép biến đổi để áp dụng trong suốt qúa trình tạo bóng và Graphics2D tự động tạo ra các thay đổi phù hợp. Cũng có thể nối một phép biến đổi AffineTransform với phép biến đổi Graphics2D hiện tại. Phép biến đổi AffineTransform thực hiện các phép biến đổi cho đường như phép dịch,tỉ lệ ,quay và cắt bỏ với tập các hình cơ bản. Khi một phép biến đổi được nối vào phép biến đổi đã tồn tại trước đó thì phép biến đổi sau cùng sẽ được xác định như là phép biến đổi được thực hiện đầu tiên. Để tiếp nối một phép biến đổi với phép biến đổi hiện tại , you pass an AffineTransform to Graphics2D.transform. Lớp Graphics2D cũng bao gồm phương thức setTransform , nhưng phương thức này không bao giờ được sử dụng để nối với các phép biến đổi tọa độ khác của phép biến đổi đang tồn tại. Phương thức setTransform sẽ ghi đè lên phép biến đổi hiện thời của đối tượng Graphics2D, nhưng phương thức này cần cho môt số mục đích khác như: • Áp dụng phép biến đổi tỉ lệ điều chỉnh cho máy in. • Vẽ một đối tượng JComponent non-zero translation from its parent’s origin • Phóng to một thành phần để dễ dàng quan sát. • Các tình huống khác mà người cung cấp đối tượng Graphics2D muốn chuyển đổi cho quá trình tạo bóng hiệu quả . Phương thức setTransform method được xem như để thiết lập đối tượng Graphics2D trở lai phép biến đổi ban đầu sau quá trình tạo bóng đồ họa ,văn bản và ảnh đã được chuyển đổi: AffineTransform aT = g2d.getTransform(); g2d.transform(...); g2d.draw(...); g2d.setTransform(aT); Graphics2D cũng cung câp một phiên bản về drawimage mà trong đó đối tượng AffineTransform Được xem như một tham số . Điều này cho phép áp dụng một phép biến đổi cho đối tượng ảnh khi nó được vẽ mà không cần chỉnh sửa đường ống cuả phép biến đổi. Ảnh này được được vẽ như khi kết nối phép biến đôit này với phép biến đôit hiện tại trong ngữ cảnh của Graphics2D Các phép biến đổi quan hệ(Affine Transforms) Java 2D API cung cấp một lớp biến đổi là AffineTransform. AffineTransforms được sử dụng để biến đổi văn bản ,hình và các ảnh khi chúng được tạo bóng. Cũng có thể ứng dụng các phép biến đổi cho đối tượng Font để tạo ra các dẫn xuất font mới(new font derivations). Một phép biến đổi đồng nhất (affine transformation) thực hiện một phép biến đổi tuyến tính trên tập đồ hạo cơ bản. Nó luôn biến các đường thẳng thành các đường thẳng và các đường thẳng song song thành các đường thẳng song song,tuy nhiên khoảng cách giữa các điểm và các góc của các đường thẳng không song song có thể thay đổi. Các phép biến đổi có thể kết hợp để tạo ra đường ống các phép biến đổi một cách hiệu quả nhằm áp dụng cho một đối tượng nào đó. Sự kết hợp này xem như là sự móc nối. Khi một phép biến đổi được kết nối với một phép biến đổi đang tồn tại như với AffineTransform.concatenate, thì phép biến đổi cuối cùng được xác định là phép biến đổi đầu tiên được áp dụng . Một phép biến đổi cũng có thể được móc nối trước với một phép biến đổi đang tồn tại . Trong trường hợp này thì phép biến đổi cuối cùng sẽ được thực hiện cuối cùng . Phép móc nối trước được được áp dụng để thực hiện các phép biến đổi có liên quan với không gian thiết bị thay vì không gian người sử dụng. Ví dụ có thể sử dụng phương thức AffineTransform.preConcatenate để thực hiện phép chuyển đổi có liên quan tới không gian các điểm ảnh.. 2.2.6.1 Constructing an AffineTransform AffineTransform cung cấp một tập các phương thức cho việc xây dựng các đối tượng AffineTransform. • getTranslateinstance • getRotateinstance • getScaleinstance • getShearinstance Để sử dụng các phương thức này cần phải xác định các tính chất của phép biến đổi mà cần tạo ra và lớp AffineTransform sẽ tạo ra các ma trận chuyển đổi phù hợp. Cũng có thể xây dựng phép biến đổi đòng nhất một cách trực tiếp mà không cần thông qua lớp AffineTransform bằng cách xác định các thành phần của phép biến đổi đó. 2.2.7 Composite Attributes Khi hai đối tượng đồ họa trồng lên nhau thì điều cần thiết là phải xác định những màu nào sẽ được tạo bóng cho các điểm ảnh chồng lên nhau. Ví dụ nếu một hình chữ nhật màu đỏ và một hình chữ nhật màu xanh chồng lên nhau thì các điểm ảnh đó sẽ có màu đỏ hoặc màu xanh hay là sự kết hợp của cả hai màu. Màu của những điểm ảnh trong vùng chồng nhau đó sẽ xác định hình chữ nhật nào nằm trên và bị che khuất như thế nào. Quá trình xử lý để xác định màu nào tạo bóng cho các điểm ảnh sẽ được chia sẽ cho các đối tượng chồng (overlaping objects)được gọi compositing. Có hai interface tạo các kiểu kết hợp cơ bản trong Java 2D là : Composite và CompositeContext. Để xác định các kiểu kết hợp sẽ được sử dụng thì cần phải thêm vào một đối tượng AlphaComposite cho ngữ cảnh của đối tượng Graphics2D bằng cách gọi phương thức setComposite. AlphaComposite,và một triển khai(implementation) của interface Composite , có hỗ trợ một số kiểu kết hợp khác nhau . Mỗi đối tượng của lớp này là một luật kết hợp để mô tả sự pha trộn một màu mới với màu đang tồn tại.. Một trong những luật kết hợp trong lớp AlphaComposite là SRC_OVER, luật này chỉ ra màu mới (màu nguồn) sẽ được pha trộn với màu đang tồn tại (màu đích ) như thế nào. 2.2.7.1 Managing Transparency Giá trị alpha của một màu là giá trị đo đọ trong suôt của màu đó ,nó chỉ ra (theo phần trăm) bao nhiêu phần trăm màu trước khi tô được hiển thị ra khi các màu chồng lên nhau. Các màu tối (co giá tri alpha=1.0) thì các màu tô đè lên chúng sẽ không được hiển thị, trong khi đó các màu trong suốt (có giá trị alpha=0.0) cho phép các màu tô đè lên nó được hiển thị. Khi văn bản và hình được tạo bóng thì giá trị alpha sẽ bắt nguồn từ thuộc tính của đối tượng Paint trong ngữ cảnh của đối tượng Graphics2D . Khi các hình và văn bản được làm trơn thì giá trị alpha từ thuộc tính Paint (trong ngữ cảnh của Graphics2D) được kết hợp với thông tin về các điểm ảnh bị che từ “rasterized path”. Các ảnh lưu thông tin về giá trị alpha của chính nó . Khi tạo một đối tượng AlphaComposite , có thể xác định được giá trị alpha được thêm vào . Khi thêm đối tượng AlphaComposite này tới ngữ cảnh của Graphics2D ,giá trị alpha thêm vào này sẽ làm tăng giá trị trong suốt của bất kì đối tượng đồ họa nào khi chúng được tạo bóng - giá trị alpha của mỗi đối tượng đồ họa được nhân lên bởi giá trị alpha của đối tượng AlphaComposite. 2.2.7.2 Transparency and images Các ảnh có thể lưu thông tin về độ trong suốt cho mỗi điểm ảnh của nó. Thông tin này đựoc gọi là kênh alpha( alpha channel,) ,nó được sử dụng kêts hợp với đối tượng Composite trong ngữ cảnh của Graphics2D để pha trộn màu của ảnh đó các đồ họa đang tồn tại. 2.3 Thiết lập Graphics2Context Để cấu hình ngữ cảnh Graphics2D cho quá trình tạo bóng phải sử dụng các phương thức khởi tạo cho đối tượng Graphics2D để xác định các thuộc tính như RenderingHints, Stroke, Paint,Clipping path, Composite, and Transform. 2.3.1 Setting Rendering Hints Đối tượng RenderingHints đóng gói tất các tham chiếu để xác định một đối tượng sẽ được tạo bóng như thế nào . Để tạo các chỉ dẫn cho quá trình tạo bóng trong ngữ cảnh Graphics2D ,thì cần tạo một đối tượng RenderingHints và chuyển nó vào lớp Graphics2D.setRendering Tạo chỉ dẫn cho quá trình tạo bóng sẽ không đảm bảo việc sẽ sử dụng một thuật toán tạo bóng cụ thể: không phải môi trường cũng hỗ trợ chế độ tạo bóng . Trong ví dụ cho sau đây thì cho phép sử dụng kĩ thuật làm trơn (antialiasing) và tạo ra các tham chiếu để xác định chất lượng cho quá trình tạo bóng: qualityHints = new RenderingHints(RenderingHints.KEY_ANTiALiASiNG, RenderingHints.VALUE_ANTiALiAS_ON); qualityHints.put(RenderingHints.KEY_RENDERiNG, RenderingHints.VALUE_RENDER_QUALiTY); g2.setRenderingHints(qualityHints); 2.3.2 Specifying Stroke Attributes Một đối tượng BasicStroke sẽ định nghĩa các tính chất đuwoc áp dụng cho đường viền bao quanh một đối tượng Shape, gồm có độ rộng và kiểu đường (dashing pattern),để làm thế nào các đoạn thẳng được kết hợp với nhau. Để khởi tạo các thuộc tính về đường nét trong ngữ cảnh Graphics2D ,thì khởi tạo một đối tượng BasicStroke và chuyền nó vào phương thức setStroke 2.3.2.1 Setting the Stroke Width Để khởi tạo độ rộng đường nét thì khởi tạo đối tượng BasicStroke với độ rộng mong muốn và sau đó gọi phương thức setStroke Trong ví dụ cho sau đây ,độ rộng đường nét được khởi tạo với 12 điểm và giá trị mặc định được sử dụng cho việc bố trí kiểu jont và kiểu endcap. wideStroke = new BasicStroke(12.0f); g2.setStroke(wideStroke); 2.3.2.2 Specifying Join and Endcap Styles Để khởi tạo cho các kiểu join và endcap ,cần tạo một đối tượng BasicStroke với các thuộc tính mong muốn . Trong ví dụ cho sau đây,độ rộng đường nét được khởi tạo với 12 điểm và các kiểu join và endcap được sử dụng thay cho các gia tri mặc định: roundStroke = new BasicStroke(4.0f, BasicStroke.CAP_ROUND, BasicStroke.JOiN_ROUND); g2.setStroke(roundStroke); 2.3.2.3 Setting the Dashing Pattern Các kiểu đường phức tạp có thể dễ dàng được định nghĩa với một đối tượng BasicStroke. Khi tạo một đối tượng BasicStroke ,phải xác định 2 tham số để kiểm soát kiểu đường: • dash - là một mảng biểu diễn kiểu đường. Các phần tử xen kẽ của mảng biểu diễn kích thước nét gạch và khoảng cách giữa các nét gạch . Phần tử 0 biểu diễn nét gạch đầu tiên, phần tử thứ 1 biểu diễn khoảng trắng đầu tiên. • dash_phase - là một offset định nghĩa nơi bắt đầu của mẫu nét gạch. Trong ví dụ sau đây, hai mẫu nét gạch được áp dụng cho một đường thẳng. Trong mẫu thứ nhất ,kích thước của các nét gạch và khoảng trống giữa chúng là không thay đổi. Mẫu thứ hai thì phức tạp hơn ,sử dụng mảng sáu phần tử để định nghĩa float dash1[] = {10.0f}; BasicStroke bs = new BasicStroke(5.0f, BasicStroke.CAP_BUTT, BasicStroke.JOiN_MiTER, 10.0f, dash1, 0.0f); g2.setStroke(bs); Line2D line = new Line2D.Float(20.0f, 10.0f, 100.0f, 10.0f); g2.draw(line); float[] dash2 = {6.0f, 4.0f, 2.0f, 4.0f, 2.0f, 4.0f}; bs = new BasicStroke(5.0f, BasicStroke.CAP_BUTT, BasicStroke.JOiN_MiTER, 10.0f, dash2, 0.0f); g2.setStroke(bs); g2.draw(line) Cả hai mẫu đều sử dụng offset(địa chỉ bắt đầu của hai mẫu) 0 khi đó các nét gạch đầu tiên sẽ ổ vị trí đầu của mẫu. Hai mẫu này được minh họa trong hình2-7 2.3.3 Specifying Fill Attributes Thuộc tính cảu đối tượng Paint trong ngữ cảnh Graphics2D xác định các màu tô hay các mẫu (kiểu )tô khi một đối tượng văn bản và Shape được tạo bóng. 2.3.3.1 Filling a Shape with a Gradient Lớp GradientPaint cung cấp một cách đơn giản để tô một hình bằng kiểu tô loang . Khi khởi tạo đối tượng GradientPaint, cần xác định vị trí đầu tiên cùng với màu tô và vị trí cuối cùng một màu tô. Kiểu tô này sẽ thay đổi tỉ lệ từ màu này sang màu khác dọc theo đường nối của hai vị trí đã chọn, minh họa trong hình 2-8. Trong ngôi sao thứ ba,cả hai điểm đều nằm trong cùng một hình.Tất cả các điểm dọc theo đường tô loang mở rộng qua điểm P1 sẽ nhận được màu ban đầu và các điểm dọc theo đường tô loang được mở rộng ngoài điểm P2 sẽ nhận màu kết thúc. Các bước cần thực hiện để tô loang theo hai màu: 1. Khởi tạo đối tượng GradientPaint . 2. Gọi phương thức Graphics2D.setPaint. 3. Khởi tạo đối tượng Shape. Trong ví dụ sau đây, một hình chữ nhật được tô với mẫu dệt đơn giản từ một đối tượng buffered image. GradientPaint gp = new GradientPaint(50.0f, 50.0f, Color.blue 50.0f, 250.0f, Color.green); g2.setPaint(gp); g2.fillRect(50, 50, 200, 200); 2.3.3.2 Filling a Shape with a Texture Lớp TexturePaint cung cấp một cách đơn giản để tô hình bằng mẫu lặp. Khi tạo một đối tượng Bufferedimage để sử dụng như là một mẫu tô. Cần chuyển hàm khởi tạo một hình chữ nhật để định nghĩa tần số lặp cho mẫu ,được chi ra trong hình 2-9. Để tô hình theo mẫu dệt: 1. Tạo đối tượng TexturePaint 2. Gọi phương thức Graphics2D.setPaint. 3. Tạo đối tượng Shape. 4. Gọi phương thức Graphics2D.fill(shape). Trong ví dụ sau đây, một hình chữ nhật được tô với mẫu dệt đơn giản từ một đối tượng bufferedimage. // Create a buffered image texture patch of size //5x5 Bufferedimage bi = new Bufferedimage(5, 5, Bufferedimage.TYPE_iNT_RGB); Graphics2D big = bi.createGraphics(); big.setColor(Color.green); big.fillRect(0,0,5,5); big.setColor(Color.lightGray); big.fillOval(0,0,5,5); // Create a texture paint from the buffered image Rectangle r = new Rectangle(0,0,5,5); TexturePaint tp = new TexturePaint(bi,r,TexturePaint.NEAREST_NEiGHBOR); // Add the texture paint to the graphics context. g2.setPaint(tp); // Create and render a rectangle filled with the texture. g2.fillRect(0,0,200,200); } 2.3.4 Setting the Clipping Path Để định nghĩa một khung nhìn: Khởi tạo một đối tượng Shape mà biểu diễn vùng muốn tạo bóng. Gọi phương thức Graphics2D.setClip để sử dụng đối tuợng shape như là một khung nhìn cho ngữ cảnh Graphics2D . Để thu hẹp khung nhìn: Khởi tạo đối tượng Shape mà giao với khung hnìn hiện tại. Gọi phương thức clip để thay đổi khung nhìn cho phần giao nhau của khung nhìn hiện tại và đối tượng Shape mới. Trong ví dụ cho sau đây, một khung nhìn được tạo ra từ một elip và sau đó được thay đổi băng cách gọi phương thức clip. public void paint(Graphics g) { Graphics2D g2 = (Graphics2D) g; // The width and height of the canvas int w = getSize().width; int h = getSize().height; // Create an ellipse and use it as the clipping path Ellipse2D e = new Ellipse2D.Float(w/4.0f,h/4.0f, w/2.0f,h/2.0f); g2.setClip(e); // Fill the canvas. Only the area within the clip is rendered g2.setColor(Color.cyan); g2.fillRect(0,0,w,h); // Change the clipping path, setting it to the intersection of // the current clip and a new rectangle. Rectangle r = new Rectangle(w/4+10,h/4+10,w/2-20,h/2-20); g2.clip(r); // Fill the canvas. Only the area within the new clip // is rendered g2.setColor(Color.magenta); g2.fillRect(0,0,w,h); 2.3.5 Setting the Graphics2D Transform Để biến đổi đối tượng Shape,xâu văn bản, hay ảnh cần phải thêm vào một phép biến đổi mới AffineTransform cho đường ống phép biến đổi tronh ngữ cảnh của Graphics2D trước khi tạo bóng . phép biến đổi được áp dụng khi đối tượng đồ họa được tạo bóng . Ví dụ để vẽ một hình chữ nhật nghiêng 45 độ: Lấy một phép biến đổi Graphics2D trước khi thực hiện bất kì phép biến đổi nào . Luôn gọi phương thức getTransform trong đối tượng Graphics2D trước khi thêm vào một phép biến đổi cho ngữ cảnh đồ họa bởi vì ngữ cảnh đồ họa có thể đã tồn tại một phép biến đổi cho một lí do khác ,định vị Swing và các thành phần trong một cửa sổ. Lấy phép quay bằng cách gọi AffineTransform. getRotateinstance. Gọi phương thức Graphics2D.transform để thêm vào các phép biến đổi mới cho đường ống biến đổi. Không được sử dụng phương thức setTransform để thêm một phép biến đổi tọa độ mới,bởi vì setTransform sẽ ghi đè lên phép biến đổi hiện tại trong ngữ cảnh đồ họa 4.Tạo đối tượng Rectangle2D.Float . Gọi phươn thức Graphics2D.draw để tạo bóng hình chữ nhật. Sau khi tạo bóng cho hình chữ nhật đã được biến đổi , thì reset phép biến đổi của Graphics2D trở về phép biến đổi ban đầu mà đã lưu trong bước 1 bằng cách gọi phương thức setTransform cho phép biến đổi ban đầu . Trong ví dụ sau đây, một thể hiện của AffineTransform được sử dụng để quay hình chữ nhật đi 45 độ khi nó được tạo bóng. AffineTransform aT = g2.getTransform(); Rectangle2D rect = new Rectangle2D.Float(1.0,1.0,2.0,3.0); AffineTransform rotate45 = AffineTransform.getRotateinstance(Math.Pi/4.0,0.0,0.0) g2.transform(rotate45); g2.draw(rect); g2.setTransform(aT); Còn trong ví dụ này,một đối tượng AffineTransform được sử dụng để quay các câu text xung quang một điểm trung tâm. // Define the rendering transform AffineTransform at = new AffineTransform(); // Apply a translation transform to make room for the // rotated text. at.setToTranslation(400.0, 400.0); g2.transform(at); // Create a rotation transform to rotate the text at.setToRotation(Math.Pi / 2.0); // Render four copies of the string “Java” at 90 degree angles for (int i = 0; i < 4; i++) { g2.drawString(“Java”, 0.0f, 0.0f); g2.transform(at); } Có thể biến đổi ảnh trong cách tương tự -phép biến đổi trong ngữ cảnh Graphics2D được áp dụng trong quá trình tạo bóng mà không cần quan tâm đến kiểu đối tượng đồ hạo đang được tạo bóng Để áp dụng phép biến đổi cho ảnh mà không cần thay đổi phép biến đổi trong ngữ cảnh Graphics2D , thì chuyển AffineTransform sang drawimage: AffineTransform rotate45 = AffineTransform.getRotateinstance(Math.Pi/4.0,0.0,0.0) g2.drawimage(myimage, rotate45); Các phép biến đổi cũng được áp dụng cho đối tượng Font để tạo ra một kiểu. 2.3.6 Specifying a Composition Style Một đối tượng AlphaComposite chứa các luật kết hợp để xác định các màu sẽ được tạo bóng như thế nào khi đối tượng nàu đè lên đối tượng khác . Để xác định các kiểu kết hợp cho ngữ cảnh Graphics2D , cần tạo một đối tượng AlphaComposite và chuyền nó vào phương thức setComposite. Kiểu thông dụng nhất là SRC_OVER. 2.3.6.1 Using the Source Over Compositing Rule Luật kết hợp SRC_OVER sẽ kết hợp các điểm ảnh nguồn (source pixel) với các điểm ảnh đích (destination pixel). Ví dụ ,nếu tạo bóng một hình chữ nhật màu xanh nước biển (blue) và sau đó tạo bóng một hình chữ nhật màu đỏ mà đè lên một phần của hình chữ nhật kia,thì vùng giao nhau đó sẽ có màu đỏ. Hay nói cách khác ,đối tương mà được tạo bóng cuối cùng sẽ xuất hiện ở trên cùng . Để sử dụng luật kết hợp SRC_OVER : Tạo một đối tượng AlphaComposite bằng cách gọi phương thức getinstance và xác định luật SRC_OVER . AlphaComposite ac =AlphaComposite.getinstance(AlphaComposite.SRC_OVER); Gọi phương thức setComposite để thêm đối tuợng AlphaComposite cho ngữ cảnh của Graphics2D. g2.setComposite(ac); Mỗi khi đối tượng kết hợp này được thiết lập ,thì đối tượng đè lên được tạo bóng bằng cách sử dụng luật kết hợp đã xác định. 2.3.6.2 Increasing the Transparency of Composited Objects AlphaComposite cho phép xác định một giá trị hằng số alpha thêm vào mà được nhân với giá trị alpha của các điểm ảnh nguồn để tăng độ trong suốt. Ví dụ ,để khởi tạo một đối tượng AlphaComposite mà tạo bóng đối tượng nguồn có độ trong suốt là 50% ,xác định một giá trị alpha là .5: AlphaComposite ac =AlphaComposite.getinstance(AlphaComposite.SRC_OVER, .5f); Trong ví dụ cho sau đây, a source over alpha composite object is created with an alpha of .5 and added to the graphics context, causing subsequent shapes to be rendered 50% transparent. public void paint(Graphics g) { Graphics2D g2 = (Graphics2D) g; g2.setColor(Color.red); g2.translate(100,50); // radians=degree * pie / 180 g2.rotate((45*java.lang.Math.Pi)/180); g2.fillRect(0,0,100,100); g2.setTransform(new AffineTransform()); // set to identity // Create a new alpha composite AlphaComposite ac = AlphaComposite.getinstance(AlphaComposite.SRC_OVER,0.5f); g2.setComposite(ac); g2.setColor(Color.green); g2.fillRect(50,0,100,100); g2.setColor(Color.blue); g2.fillRect(125,75,100,100); g2.setColor(Color.yellow); g2.fillRect(50,125,100,100); g2.setColor(Color.pink); g2.fillRect(-25,75,100,100); } 2.4 Rendering Graphics Primitives Graphics2D cung cấp các phương thưc tạo bóng cho các cơ sở đồ họa như Shapes, Text, và images: • draw—tạo nét cho đường viền của đối tựợng Shape bằng cách sử dụng các đối tượng Stroke và Paint trong ngữ cảnh Graphics2D . • fill—tô một đối tượng Shape bằng cách sử dụng đối tượng Paint trong ngữ cảnh Graphics2D . • drawString—tạo bóng các xâu văn bản đã dược xác định bằng cách sư dụng đối tượng Paint trong ngữ cảnh Graphics2D . • drawimage—tạo bóng các ảnh đã được xác định 2.4.1 Drawing a Shape Đường biên của bất kì đối tượng Shape cũng được tạo bóng bằng phương thức Graphics2D.draw. Phương thức vẽ từ các phiên bản trước cũng được hỗ trợ : drawLine, drawRect, drawRoundRect, drawOval, drawArc,drawPolyline, drawPolygon, draw3DRect. Khi một đối tượng Shape được vẽ , thì đường biên của nó sẽ được tạo nét bằng đối tượng Stroke trong ngữ cảnh Graphics2D Bằng việc thiét lập đối tượng BasicStroke phù hợp trong ngữ cảnh Graphics2D ,thò có thể vẽ đường thẳng với độ rộng và mẫu nét gạch bất kì . Đối tượng BasicStroke cũng định nghĩa các thuộc tính endcap và join của đường thẳng . Để tạo bóng cho các đường biên của đối tượng shape: 1. Khởi tạo cho đối tượng BasicStroke 2. Gọi phương thức Graphics2D.setStroke 3. Khởi tạo cho đối tượng Shape. 4. Gọi phương thức Graphics2D.draw(shape). Trong ví dụ sau,đối tượng GeneralPath được sử dụng để định nghĩa một ngôi sao và một đối tượng BasicStroke được thêm vào ngữ cảnh Graphics2D để định nghĩa cho các cạnh của ngôi sao với các thuộc tính join . public void paint(Graphics g) { Graphics2D g2 = (Graphics2D) g; // create and set the stroke g2.setStroke(new BasicStroke(4.0f)); // Create a star using a general path object GeneralPath p = new GeneralPath(GeneralPath.NON_ZERO); p.moveTo(- 100.0f, - 25.0f); p.lineTo(+ 100.0f, - 25.0f); p.lineTo(- 50.0f, + 100.0f); p.lineTo(+ 0.0f, - 100.0f); p.lineTo(+ 50.0f, + 100.0f); p.closePath(); // translate origin towards center of canvas g2.translate(100.0f, 100.0f); // render the star's path g2.draw(p); } 2.4.2 Filling a Shape Phương thức Graphics2D.fill có thể được sử dụng để tô bất kì đối tượng Shape. Khi một đối tượng Shape được tô, thì phần bị giới hạn bởi đương bao của đối tượng sẽ được tạo bóng với ngữ cảnh Graphics2D của thuộc tính Paint attribute—như Color, TexturePaint, or GradientPaint. Phương thức tô từ các phiên bản trước đó của phần mềm JDK cũng được hỗ trợ: fillRect, fill3DRect, fillRoundRect, fillOval, fillArc, fillPolygon,clearRect. Để tô một đối tượng Shape: 1. Xác định màu tô và kiểu tô trong ngữ cảnh đồ họa bằng cách sử dụng Graphics2D.setColor hay Graphics2D.setPaint. 1. Khởi tạo đối tượng Shape. 2. Gọi phương thức Graphics2D.fill để tạo bóng đối tựơng Shape. Trong ví dụ sau đây ,phương thức setColor được gọi để định nghĩa màu tô là green fill cho một đối tượng Rectangle2D. public void paint(Graphics g) { Graphics2D g2 = (Graphics2D) g; g2.setPaint(Color.green); Rectangle2D r2 = new Rectangle2D.Float(25,25,150,150); g2.fill(r2); } 2.4.3 Rendering Text Để tạo bóng văn bản ,cần gọi Graphics2D.drawString, sau đó chuyển vào xâu mà bạn muốn tạo bóng 2.4.4 Rendering images Để tạo bóng văn bản ,cần gọi Graphics2D.drawimage, sau đó chuyển vào xâu mà bạn muốn tạo bóng . 2.5 Defining Custom Composition Rules Có thể tạo một kiểu mới hoàn toàn của phép kết hợp băng cách thực thi các interface Composite và CompositeContext . Một đối tượng Composite cung cấp một đối tượng CompositeContext mà thực chất là lưu trạng thái và thưc hiện công việc kết hợp. Nhiều đối tượng CompositeContext có thể được tạo từ một đối tượng Composite để lưu các trạng thái riêng biệt trong môi trường đa luồng (multithreaded environment.) 2.6 Rendering in a Multi-Screen Environment TrongJavaTM 2 SDK, version 1.3, Java 2DTM API hỗ trợ những cấu hình đa màn hiển thi khác nhau mà có thể được cấu hình bởi một môi trường mà : • Có hai hoặc nhiều màn hình độc lập • Có hai hoặc nhiều màn hình nơi chỉ có một màn hình chính và các màn hiển thị khác sẽ copy những hình ảnh xuát hiện trên màn hình chính • Hai hoặc nhiều màn hình mà có dạng như một máy ảo và nó cho phép gọi các thiết bị ảo. Java 2D API cho phép tạo các đối tượng Frame, JFrame, Window, hoặc Jwindow .Với một đối tượng GraphicsConfiguration để xác định các thiết bị hiển thị cho quá trình tạo bóng. Trong cả ba cấu hình,mỗi thiết bị hiển thị được biểu diễn bởi một đối tượng GraphicsDevice.Một đối tượng GraphicsDevice có nhiều đối tượng GraphicsConfiguration kết hợp với nó . Khi hai hoặc nhiều màn hình được sử dụng để tạo một thiết bị ảo ,hệ tọa độ ảo (mà tồn tại độc lập với thiết bị hiển thị vật lý )được sử dụng để biểu diễn thiết bị ảo đó . Các biên cảu mỗi đối tượng GraphicsConfiguration trong cấu hình đa màn hiển thị có quan hệ với hệ tọa độ ảo . Một màn hiển thị trong môi trường này được xác định như là màn hiển thị chính và tạ vị trí (0, 0) của hệ tọa độ ảo . Dựa trên vị trí của màn hiển thị chính ,thiết bị ảo có thể có tọa độ âm ,như trong hình 2-10: Để xác định nếu là môi trường thiết bị ảo nơi mà một đối tượng Window hay Frame có thể xuất hiện trên hai hay nhiều màn hình vật lý ,thì gọi phương thức getBounds trên mỗi đối tượng GraphicsConfiguration trong hệ thống và kiểm tra nếu gốc tọa độ khác vị trí (0, 0). Phương thức getBounds của đối tượng GraphicsConfiguration trả về một đối tượng hình Rectangle trong hệ tọa độ ảo .Vì vậy ,nếu một gốc tọa độ không phải vị trí (0, 0), thì môi trường đó chính là môi trường ảo. Trong môi trường thiết bị ảo ,các tọa độ của các đối tượng GraphicsConfiguration có quan hệ với hệ tọa độ ảo . Vì vậy, phải sử dụng tọa độ ảo khi gọi phương thức setLocation của đối tượng Frame hay Window. Cho ví dụ,Đoạn mã này sẽ lấy đường bao của một đối tượng GraphicsConfiguration và sử dụng các đường bao để thiết lập vị trí của đối tượng Frame tại tọa độ (10, 10) trong hệ tọa độ của màn hiển thị vật lý . Frame f = new Frame(GraphicsConfiguration gc); Rectangle bounds = gc.getBounds(); f.setLocation(10 + bounds.x, 10 + bounds.y); Nếu các đường bao của đối tượng GraphicsConfiguration không được tính đến , thì đối tượng Frame được hiển thị tại vị trí (10, 10) trên mà hình vật lý chính , điều này có thể khác với mà hình vật lý của đối tượng GraphicsConfiguration đã được xác định . Phương thức getBounds có thể được sử dụng để xác định các đường biên cảu thiết bị ảo. Gọi phương thức getBounds trên mỗi đối tượng GraphicsConfiguration trong hệ thống . Để xác dịnh các đường bao cảu thiết bị ảo , tính toán sự kết hợp của các đường bao. Kĩ thuật này được sử dụng trong ví dụ sau. Rectangle virtualBounds = new Rectangle(); GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice[] gs = ge.getScreenDevices(); for (int j = 0; j < gs.length; j++) { GraphicsDevice gd = gs[j]; GraphicsConfiguration[] gc = gd.getConfigurations(); for (int i = 0; i < gc.length; i++) { virtualBounds = virtualBounds.union(gc[i].getBounds()); } } Applet sau sẽ tạo một đối tượng JFrame với mỗi đối tượng GraphicsConfiguration của mỗi đối tượng GraphicsDevice trong đối tượng Graphic._. tga.addChild(new Sphere(.40f, 0, 4, sphereAppearB)); // show a level 0 object at a distance for comparison t3d.set(new Vector3f(-5.0f, 0.0f, -35.0f)); TransformGroup tgb = new TransformGroup(t3d); objRoot.addChild(tgb); tgb.addChild(new Sphere(.40f, 0, 25, sphereAppearA)); // a white background is better for printing images in tutorial Background background = new Background(); background.setColor(1.0f, 1.0f, 1.0f); background.setApplicationBounds(new BoundingSphere()); objRoot.addChild(background); // Let Java 3D perform optimizations on this scene graph. objRoot.compile(); return objRoot; } // end of CreateSceneGraph method of DistanceLODApp Hình 5-19 biểu diễn biểu đồ đồ thị khung cảnh được tạo ra trong ví dụ trên. Chú ý rằng, đối tượng Switch đích vừa là con của đối tượng TransformGroup vừa được tham chiếu đến bởi đối tượng DistanceLOD. Cả hai quan hệ này đều cần được xác lập. Hình 5-19. Một phần biểu đồ đồ thị khung cảnh cho chương trình DistanceLODApp Hình 5-20 là hai cảnh được sinh từ chương trình DistanceLODApp. Mỗi ảnh này có 2 khối cầu tĩnh và một khối cầu dịch chuyển (trong hình bên phải, khối cầu bên trái nhất đã bị che khuất). Khối cầu dịch chuyển được biểu diễn bằng một đối tượng DistanceLOD với 4 khối cầu có độ phức tạp hình học khác nhau. Khối cầu nhỏ màu xanh là khối cầu chi tiết nhất được sử dụng bởi đối tượng DistanceLOD ở khoảng cách cực đại. Khối cầu lớn màu đỏ là khối cầu kém chi tiết nhất của đối tượng DistanceLOD và ở khoảng cách cực tiểu. Hai khối cầu này được hiển thị để làm mốc so sánh. Trong chương trình này, đối tượng DistanceLOD được biểu diễn bởi các khối cầu có màu sắc khác nhau để minh họa quá trình chuyển đổi. Một đối tượng nội suy vị trí PositionInterpolator được sử dụng để dịch chuyển đối tượng DistanceLOD theo hướng vuông góc với màn hình. Khi đối tượng DistanceLOD dịch chuyển ra xa người quan sát, nó sẽ chuyển đối tượng trực quan để hiển thị. Nếu không sử dụng các khối cầu có màu sắc khác nhau sẽ rất khó khăn để nhận biết khi nào thì đối tượng trực quan được chuyển. Hình 5-20. Hai cảnh được sinh ra từ DistanceLODApp. Giao diện lập trình ứng dụng DistanceLOD API DistanceLOD định nghĩa một nút đối tượng hành vi LOD dựa khoảng cách, hoạt động trên một nút nhóm Switch để lựa chọn một trong số các con của nút Switch đó dựa vào khoảng cách của nút đối tượng LOD so với người quan sát. Một mảng n giá trị khoảng cách đơn điệu tăng, được xác định sao cho thành phần đầu tiên distances[0] tương ứng với mức chi tiết cao nhất và thành phần cuối cùng distances[n-1] tương ứng với mức chi tiết thấp nhất. Dựa vào khoảng cách thực từ người quan sát đến nút DistanceLOD, n giá trị khoảng cách [0, n-1] lựa chọn n+1 mức chi tiết [0, n]. Nếu gọi khoảng cách từ người quan sát đến nút LOD thì phương trình để xác định mức chi tiết (con của nút Switch) được lựa chọn là: nếu d <= distances[0] i nếu distances[i-1] < d <= distances[i] n nếu d > distances[n-1] Chú ý rằng cả vị trí và mảng các giá trị khoảng cách đều được xác định trong hệ tọa độ địa phương của nút hiện thời. Các phương thức khởi tạo: DistanceLOD() Khởi tạo và khởi gán cho nút DistanceLOD giá trị mặc định. DistanceLOD(float[] distances) Khởi tạo và gán giá trị ban đầu cho nút DistanceLOD với mảng các giá trị khoảng cách xác định và vị trí mặc định (0, 0, 0). DistanceLOD(float[] distances, Point3f position) Khởi tạo và khởi gán cho nút DistanceLOD với mảng các giá trị khoảng cách và vị trí xác định. Một số phương thức cung cấp bởi DistanceLOD: int numDistances() Trả về số lượng các ngưỡng khoảng cách. void setDistance(int whichDistance, double distance) Thiết lập ngưỡng khoảng cách LOD xác định. void setPosition(Point3f position) Thiết lập vị trí của nút LOD . Morph Các lớp nội suy thay đổi các thuộc tính trực quan khác nhau trong thế giới ảo. Tuy nhiên, không có lớp nội suy nào cung cấp các khả năng thay đổi hình dạng hình học của các đối tượng trực quan. Khả năng này do lớp Morph cung cấp. Đối tượng Morph tạo hình dạng cho một đối tượng trực quan thông qua việc nội suy từ một tập các đối tượng GeometryArray. Theo đặc điểm này, lớp Morph giống với các lớp nội suy. Tuy nhiên, Morph không phải là lớp nội suy, thậm chí, nó còn không kế thừa lớp hành vi Behavior. Lớp Morph mở rộng từ lớp Node. Trong chương 4, chúng ta đã làm quen với phương thức processStimulus của đối tượng Behavior để thực hiện các thay đổi đối với đồ thị khung cảnh thực hay đối với các đối tượng trong đồ thị khung cảnh thực. Do không có lớp hành vi chuyên biệt sử dụng với đối tượng Morph , các ứng dụng Morph phải tự xây dựng lớp hành vi riêng. Lớp Morph được coi như một lớp hoạt ảnh hay lớp tương tác phụ thuộc vào sự mô phỏng hành vi tương tác với đối tượng Morph . Đối tượng Morph có thể được sử dụng để biến kim tự tháp thành hình khối hộp, mèo thành chó, hoặc biến đổi bất kì hình dạng hình học nào thành một dạng hình học khác. Hạn chế duy nhất là các đối tượng hình học sử dụng cho việc nội suy phải cùng thuộc một lớp và là lớp con của lớp GeometryArray, với cùng số lượng các đỉnh. Hạn chế về số lượng đỉnh không phải là hạn chế lớn của Morph , trong đĩa chương trình kèm theo, bạn đọc có thể thấy chương trình Pyramid2Cube.java, biến một kìm tự tháp thành hình hộp. Đối tượng Morph cũng có thể được sử dụng để tạo hoạt ảnh cho các đối tượng trực quan (ví dụ: làm cho một người đi lại được, hay làm cho bàn tay có thể cầm nắm được vật dụng…). Chương trình ví dụ tạo hoạt ảnh cho bàn tay, Morphing.java cũng có thể tìm thấy trong đĩa chương trình kèm theo. Chương trình ví dụ thứ 3, làm cho một dạng hình que đi lại được sẽ được trình bày chi tiết trong phần sau. Sử dụng đối tượng Morph Để hiểu được cách sử dụng đối tượng Morph , trước hết, cần phải biết các thức đối tượng Morph làm việc. Morph không quá phức tạp. Đối tượng Morph lưu trữ một mảng các đối tượng GeometryArray với mỗi đối tượng định nghĩa đặc tả hình học hoàn chỉnh cho một đối tượng trực quan. Các đối tượng GeometryArray có thể được coi như là các khung ảnh chính trong hoạt ảnh, hay chính xác hơn, là các hằng số trong phương trình tính toán để tạo ra một đối tượng GeometryArray mới. Ngoài mảng các đối tượng GeometryArray, một đối tượng Morph còn có một mảng các trọng số, đây chính là các biến số trong phương trình. Sử dụng các đối tượng GeometryArray cùng với các trọng số, đối tượng Morph khởi tạo một đối tượng mảng hình học sử dụng giá trị trọng số trung bình của tọa độ, màu sắc, …cung cấp bởi đối tượng GeometryArray. Thay đổi các trọng số sẽ làm thay đổi hình dạng hình học. tạo một mảng các đối tượng GeometryArray tạo một đối tượng Morph với khả năng ALLOW_WEIGHTS_WRITE lắp ghép đồ thị khung cảnh, bao gồm cả việc thêm các con cho đối tượng đích Switch Hình 5-21. Công thức sử dụng đối tượng Morph Như đã trình bày, sử dụng đối tượng Morph không khó, tuy nhiên, các bước này đúng cho cả hoạt ảnh và tương tác. Hoạt ảnh và tương tác được cung cấp thông qua một đối tượng hành vi. Do đó, sử dụng đối tượng Morph thường có nghĩa là viết một lớp hành vi. Cách viết lớp hành vi đã được trình bày trong phần 4.2.1, nên ở đây không nói chi tiết. Tất nhiên, Morph hoàn toàn có thể sử dụng mà không cần đối tượng hành vi, nhưng nó sẽ không thể tạo ra được hoạt ảnh. Ví dụ sử dụng Morph Chương trình Morph này sử dụng một đối tượng hành vi tùy chỉnh để sinh hoạt ảnh. Bước đầu tiên trong quá trình phát triển sẽ là xây dựng lớp hành vi này. Trong đối tượng hành vi được sử dụng để tạo hoạt ảnh cho đối tượng Morph , phương thức processStimlus thay đổi trọng số của đối tượng Morph . Trong ví dụ này processStimulus thiết lập các giá trị trọng số dựa vào giá trị Alpha của một đối tượng Alpha. Hoạt động này xảy ra tại mỗi khung ảnh được tô trát khi điều kiện kích hoạt đã được thỏa mãn. Đoạn mã sau là đoạn mã xây dựng đối tượng hành vi tùy biến trong chương trình MorphApp. public class MorphBehavior extends Behavior{ private Morph targetMorph; private Alpha alpha; // the following two members are here for effciency (no memory burn) private double[] weights = {0, 0, 0, 0}; private WakeupCondition trigger = new WakeupOnElapsedFrames(0); // create MorphBehavior MorphBehavior(Morph targetMorph, Alpha alpha){ this.targetMorph = targetMorph; this.alpha = alpha; } public void initialize(){ // set initial wakeup condition this.wakeupOn(trigger); } public void processStimulus(Enumeration criteria){ // don't need to decode event since there is only one trigger // do what is necessary weights[0] = 0; weights[1] = 0; weights[2] = 0; weights[3] = 0; float alphaValue = 4f * alpha.value() - 0.00001f; int alphaIndex = (int) alphaValue; weights[alphaIndex] = (double) alphaValue - (double)alphaIndex; if(alphaIndex < 3) weights[alphaIndex + 1] = 1.0 - weights[alphaIndex]; else weights[0] = 1.0 - weights[alphaIndex]; targetMorph.setWeights(weights); // set next wakeup condition this.wakeupOn(trigger); } } // end of class MorphBehavior Lớp MorphBehavior tạo khung hoạt ảnh chính bằng cách sử dụng các đối tượng GeometryArray hai lần tại một thời điểm theo một quá trình có tính tuần hoàn. Lớp này thích hợp cho tất cả các hoạt ảnh sử dụng 4 khung ảnh chính và có thể thay đổi dễ dàng để khớp với số lượng khung ảnh chính khác nhau. Với lớp hành vi tùy chỉnh vừa viết, mọi việc còn lại chỉ là tạo khung ảnh chính sử dụng cho hoạt ảnh. Hình 5-22 biểu diễn các hình vẽ tay được sử dụng làm khung ảnh chính cho chương trình ví dụ. Muốn xây dựng các khung ảnh đẹp hơn, có thể sử dụng các gói 3D. Các hình có màu đen trông giống như là hai khung hình chính, mỗi cái được lặp lại một lần, nhưng thực tế, chúng là bốn khung hình chính riêng biệt. Sự khác nhau là ở thứ tự các đỉnh được xác định. Đoạn mã sau được trích từ phương thức createSceneGraph của chương trình MorphApp.java với các số đánh dấu các bước được chỉ ra trong hình 5-21. Trong phương thức này, một đối tượng MorphBehavior, một đối tượng Alpha và một đối tượng Morph được tạo ra, rồi được gộp vào đồ thị khung cảnh. Các đối tượng khung ảnh chính GeometryArray được tạo ra bởi một số phương thức khác. Toàn bộ chương trình có thể tìm thấy trong đĩa chương trình kèm theo. Hình 5-22. Các hình khung ảnh chính của MorphApp với đường quỹ đạo của một đỉnh (đường mờ) public BranchGroup createSceneGraph() { // Create the root of the branch graph BranchGroup objRoot = new BranchGroup(); Transform3D t3d = new Transform3D(); t3d.set(new Vector3f(0f, -0.5f, 0f)); TransformGroup translate = new TransformGroup(t3d); // create GeometryArray[] (array of GeometryArray objects) GeometryArray[] geomArray = new GeometryArray[4]; geomArray[0] = createGeomArray0(); geomArray[1] = createGeomArray1(); geomArray[2] = createGeomArray2(); geomArray[3] = createGeomArray3(); // create morph object Morph morphObj = new Morph(geomArray); morphObj.setCapability(Morph.ALLOW_WEIGHTS_WRITE); // create alpha object Alpha alpha = new Alpha(-1, 1, 0, 0, 2000, 100, 0, 0, 0, 0); // create morph driving behavior MorphBehavior morphBehav = new MorphBehavior(morphObj, alpha); morphBehav.setSchedulingBounds(new BoundingSphere()); //assemble scene graph objRoot.addChild(translate); translate.addChild(morphObj); objRoot.addChild(morphBehav); Background background = new Background(); background.setColor(1f, 1f, 1f); background.setApplicationBounds(new BoundingSphere()); objRoot.addChild(background); // Let Java 3D perform optimizations on this scene graph. objRoot.compile(); return objRoot; } // end of CreateSceneGraph method of MorphApp Một chú ý thú vị rút ra từ ví dụ trên là nhiều hoạt ảnh khác nhau có thể được tạo ra, sử dụng chính các khung hình chính được xây dựng ở trên, với các lớp hành vi khác nhau. Hình 5-23 biểu diễn một cảnh sinh ra bởi Morph3DApp. Trong chương trình này, 3 lớp hành vi khác nhau tạo hoạt ảnh dựa trên một số hoặc tất cả đối tượng GeometryArray của MorphApp. Chúng được gọi lần lượt (từ trái qua phải) là “In Place”, “Tango”, và “Broken”. Hình 5-23. Một cảnh trong chương trình Morph3App Giao diện lập trình ứng dụng Morph API Các phương thức khởi tạo: Morph (GeometryArray[] geometryArrays) Khởi tạo và khởi gán giá trị cho đối tượng Morph với một mảng đối tượng GeometryArray xác định và một đối tượng null thuộc kiểu Appearance. Morph (GeometryArray[] geometryArrays, Appearance appearance) Khởi tạo và khởi gán giá trị cho đối tượng Morph với một mảng đối tượng GeometryArray và một đối tượng Appearance xác định. Một số phương thức khác cung cấp bởi Morph : void setAppearance(Appearance appearance) Thiết lập thành phần hiển thị bề ngoài cho nút Morph . void setGeometryArrays(GeometryArray[] geometryArrays) Thiết lập thành phần geometryArrays cho nút Morph . void setAppearanceOverrideEnable(boolean flag) Thiết lập cờ để có nút lá AlternateAppearance làm bề ngoài hiển thị cho nút Morph . void setWeights(double[] weights) Thiết lập vector trọng số Morph cho nút Morph hiện thời. Các khả năng có thể thiết lập cho Morph : ALLOW_APPEARANCE_READ | WRITE Xác định rằng nút hiện thời cho phép đọc (ghi) thông tin appearance của nó. ALLOW_GEOMETRY_ARRAY_READ | WRITE Xác định rằng nút hiện thời cho phép đọc (ghi) thông tin dạng hình học của nó. ALLOW_WEIGHTS_READ | WRITE Xác định rằng nút hiện thời cho phép đọc (ghi) vector trọng số Morph của nó. Giao diện GeometryUpdater Trong các phần trên, hoạt ảnh được tạo ra chủ yếu bằng cách di chuyển các khối hình hình học, chứ không thay đổi hay tạo ra hình mới. Ngoại trừ Morph tạo ra hình nội suy từ các hình cho trước. Java 3D API giới thiệu giao diện GeometryUpdater, giao diện này cùng với BY_REFERENCE geometry (chương 2) cho phép thay đổi hình dạng hình học trong thời gian thực thi chương trình. Với giao diện GeometryUpdater, người lập trình ứng dụng có thể tạo ra bất cứ loại hoạt ảnh nào phụ thuộc vào việc thay đổi thông tin về dạng hình học, kể cả các hoạt ảnh sử dụng kĩ thuật Billboard , level of detail và Morph . Giao diện GeometryUpdater có tính mềm dẻo cao, cho phép người lập trình tạo được nhiều hiệu ứng hơn so với các kĩ thuật trên. Các ứng dụng có thể sử dụng GeometryUpdater bao gồm các kĩ thuật hoạt ảnh chuẩn như kiến tạo các hệ động, các hệ phân tử; sinh bóng mờ tự động hay các hiệu ứng đặc biệt như chớp… Do GeometryUpdater cho phép truy cập đến dữ liệu từng đỉnh của đối tượng hình học nên khả năng tạo hoạt ảnh là không có giới hạn. Mặc dù có thể hiệu chỉnh dữ liệu đối tượng hình học BY_REFERENCE mà không cần sử dụng đối tượng GeometryUpdater, nhưng cách làm này cho kết quả hoặc là không thể dự đoán được hoặc là không ổn định. Sử dụng GeometryUpdater Để sử dụng được đối tượng GeometryUpdater cho các ứng dụng hình học động, trước hết cần tạo đối tượng hình học BY_REFERENCE với các khả năng thích hợp, tạo một lớp GeometryUpdater và lấy ra một đối tượng thuộc lớp đó, rồi tạo một lớp hành vi tùy biến và cũng lấy ra một đối tượng thuộc lớp đó. Công việc này không quá phức tạp như nhìn nhận ban đầu. Công việc tạo đối tượng hình học BY_REFERENCE không làm gì nhiều hơn ngoài việc tạo ra một đối tượng hình học khác. Đối tượng GeometryUpdater có nhiệm vụ hiệu chỉnh đối tượng hình học khi được gọi. Đối tượng hành vi lập lịch gọi đối tượng GeometryUpdater trong ứng dụng. Chúng ta xem xét qua hai phương thức quan trọng của giao diện GeometryUpdater. Hai phương thức này có cùng tên là updateData(). Phương thức updateData() thứ nhất phải được cài đặt chi tiết trong lớp định nghĩa GeometryUpdater trong ứng dụng. Phương thức updateData() thứ hai là phương thức của GeometryArray, phương thức này sẽ gọi phương thức được cài đặt trong lớp GeometryUpdater. void updateData(Geometry geometry) Cập nhật dữ liệu hình học có thể truy cập bởi tham chiếu. void updateData(GeometryUpdater updater) Phương thức này gọi phương thức updateData của đối tượng GeometryUpdater xác định để đồng bộ hóa cập nhật dữ liệu đối tượng hình học được tham chiếu bởi đối tượng GeometryArray hiện thời. Chương trình ví dụ hệ thống phân tử đài phun nước sử dụng GeometryUpdater Các hệ thống phân tử thông thường được sử dụng để mô hình nước, khói, pháo hoa và các hiện tượng giống dạng lỏng khác. Trong một hệ thống phân tử, có hai tham số thiết kế cơ bản: các phân tử sẽ có dạng như thế nào, vị trí và hướng của nó sẽ được cập nhật ra sao. Các phân tử thông thường được biểu diễn dưới dạng điểm hoặc đường, tuy nhiên, các dạng hình học khác cũng có thể được sử dụng. Việc cập nhật chuyển động có thể mô phỏng được hành vi tự nhiên của các đối tượng (cụ thể hơn là mô phỏng các định luật vật lý) hay các chuyển động mong muốn khác. Thông thường, một vài phần của mã chương trình sẽ có thành phần ngẫu nhiên để tránh cho các phân tử hoạt động hoàn toàn giống nhau. Trong chương trình ví dụ, các hạt nước được biểu diễn dưới dạng các đoạn thẳng nhỏ với chuyển động tuân theo các quy luật vật lý (cụ thể là chúng được gia tốc bởi trọng trường). Mỗi đoạn thẳng được xác định bởi 2 điểm. Hình 5-24 là một chuỗi các ảnh thu được từ chương trình ví dụ ParticleApp. Ảnh phía bên trái là đài nước trước khi các phân tử nước được khởi tạo. Ảnh ở giữa là khi cột “nước” được khởi tạo trong đài phun. Trong ảnh này, có tất cả khoảng 300 phần tử hoạt động. Hình ngoài cùng bên phải là ảnh đài phun khi đã hoạt động một khoảng thời gian. Trong hình này có khoảng 500 phần tử nước hoạt động. Hình 5-24. Chuỗi hình ảnh thu từ chương trình ParticleApp Trong chương trình ParticleApp.java, cả đài phun được quay quanh trục để thể hiện tính 3 chiều tự nhiên của hoạt ảnh. Lớp hành vi tuỳ biến Behavior và lớp GeometryUpdater là các lớp trong của lớp Fountain. Có thể dùng một vài cách khác để thiết kế hoạt ảnh như trên. Tuy nhiên, sử dụng các lớp này như là lớp trong sẽ biến Fountain trở thành một đối tượng đồ hoạ hoạt ảnh hoàn chỉnh. Hơn nữa, do cả hai lớp Behavior và Geometry đều chỉ được sử dụng cho riêng ứng dụng này, nên chẳng có lí do gì để cho phép chúng được sử dụng bởi các lớp khác bên ngoài Fountain. Các đoạn mã sau tương ứng định nghĩa các đối tượng hình học, hành vi và geometryUpdater cho ứng dụng ParticleApp. Ba đoạn mã này là thành phần chính tạo nên hoạt ảnh trong ParticleApp. Đoạn mã đầu tiên đưa ra định nghĩa cho lớp Fountain. Cụ thể, nó định nghĩa một vài trường và tạo ra đối tượng hình học được dùng để biểu diễn nước. Ba trường (hay 3 thuộc tính) được định nghĩa gần thẻ . Trường waterLines và baseElevation được sử dụng trong một vài phương thức khác nhau nên chúng được khai báo là các thuộc tính của lớp Fountain. waterLines là một tham chiếu đến đối tượng hình học LineArray, đối tượng hình học của phần tử nước. baseElevation là giá trị toạ độ y của chân đài phun. Trường thuộc tính thứ 3 giữ một tham chiếu đến đối tượng WaterUpdater được tạo ra tại đây với mục đích là không làm tràn bộ nhớ. Phương thức thứ nhất của lớp Fountain là createWaterGeometry(). Phương thức này có định nghĩa một biến nguyên N , N là số lượng các phần tử nước (số các đoạn thẳng của LineArray) được dùng để biểu diễn nước. Giá trị 1400 được gán cho N trong đoạn mã không có gì đặc biệt ngoại trừ việc nó làm cho hoạt ảnh trông có vẻ hợp lý hơn. Gần như bất cứ giá trị nào cũng có thể gán cho N. Càng nhiều phần tử được sử dụng để tạo hoạt ảnh thì thời gian để xây dựng một khung hình hiển thị càng lớn. Trong phương thức createWaterGeometry(), đối tượng LineArray được tạo ra trên dòng mã gán nhãn . Số lượng các đỉnh là N*2, bởi mỗi phần tử (mỗi đoạn thẳng) cần một đỉnh bắt đầu và một đỉnh kết thúc. Chú ý rằng định dạng đỉnh bao gồm cả BY_REFERENCE. Các phần tử nước được tạo hoạt ảnh bằng cách thay đổi giá trị toạ độ đỉnh cho các đoạn thẳng tương ứng. Việc này chỉ có thể thực hiện được nếu khả năng thích hợp được thiết lập. Dòng mã dán nhãn đầu tiên thiết lập khả năng cho phép ghi dữ liệu đỉnh. Khả năng này cần phải được thiết lập cho bất cứ ứng dụng nào sử dụng GeometryUpdater. Trong hầu hết các ứng dụng sử dụng GeometryUpdater, khả năng đọc cũng cần phải được thiết lập. Ứng dụng này đòi hỏi như vậy. Dòng mã dán nhãn thứ 2 có nhiệm vụ thiết lập khả năng đọc dữ liệu giá trị đỉnh. Phụ thuộc ứng dụng và cách đối tượng GeometryUpdater được thiết kế, thông tin dạng hình học nhất định ngoài dữ liệu đỉnh có thể cần để thiết lập đối tượng Geometry. Ví dụ, nếu đối tượng GeometryUpdater không “biết” được số đỉnh được sử dụng thì giá trị này phải được đọc từ đối tượng Geometry đã được truyền cho nó. Tất nhiên, thông tin này chỉ có thể đọc được nếu khả năng thích hợp được thiết lập. Dòng mã tiếp sau hai dòng mã gán nhãn chịu trách nhiệm thiết lập khả năng đọc số lượng đỉnh. Các dòng mã còn lại trong đoạn mã sau khởi tạo toạ độ cho N đỉnh. Mỗi đỉnh được khởi tạo toạ độ (0, baseElevation, 0), do đó ban đầu, không có phần tử nào được nhìn thấy. public class Fountain extends BranchGroup { protected LineArray waterLines = null; protected float baseElevation = -0.45f; protected GeometryUpdater geometryUpdater = new WaterUpdater(); Geometry createWaterGeometry() { int N = 1400; // number of 'drops' waterLines = new LineArray(N * 2, LineArray.COORDINATES | LineArray.BY_REFERENCE); waterLines.setCapability(GeometryArray.ALLOW_REF_DATA_WRITE); waterLines.setCapability(GeometryArray.ALLOW_REF_DATA_READ); waterLines.setCapability(GeometryArray.ALLOW_COUNT_READ); float[] coordinates = new float[N * 3 * 2]; int p; for (p = 0; p < N; p += 2) { // for each particle coordinates[p * 3 + 0] = 0.0f; coordinates[p * 3 + 1] = baseElevation; coordinates[p * 3 + 2] = 0.0f; coordinates[p * 3 + 3] = 0.0f; coordinates[p * 3 + 4] = baseElevation; coordinates[p * 3 + 5] = 0.0f; } waterLines.setCoordRefFloat(coordinates); // the following statements would be redundant // waterLines.setInitialCoordIndex(0); // waterLines.setValidVertexCount(N*2); return waterLines; } abstract class UpdateWaterBehavior extends Behavior { WakeupOnElapsedFrames w = null; public UpdateWaterBehavior() { w = new WakeupOnElapsedFrames(0); } public void initialize() { wakeupOn(w); } public void processStimulus(Enumeration critiria) { waterLines.updateData(geometryUpdater); wakeupOn(w); } // end processStimulus } // end class UpdateWaterBehavior public class WaterUpdater implements GeometryUpdater { Random random; public WaterUpdater() { random = new Random(); } public void updateData(Geometry geometry) { GeometryArray geometryArray = (GeometryArray) geometry; float[] coords = geometryArray.getCoordRefFloat(); int N = geometryArray.getValidVertexCount(); int i; for (i = 0; i < N; i += 2) { // for each particle if (coords[i * 3 + 1] > baseElevation) { // update active // particles coords[i * 3 + 0] += coords[i * 3 + 0] - coords[i * 3 + 3]; // x1 coords[i * 3 + 1] += coords[i * 3 + 1] - coords[i * 3 + 4] - 0.01f; // y1 coords[i * 3 + 2] += coords[i * 3 + 2] - coords[i * 3 + 5]; // z1 coords[i * 3 + 3] = (coords[i * 3 + 0] + coords[i * 3 + 3]) / 2; // x2 coords[i * 3 + 4] = (coords[i * 3 + 1] + coords[i * 3 + 4] + 0.01f) / 2;// y2 coords[i * 3 + 5] = (coords[i * 3 + 2] + coords[i * 3 + 5]) / 2; // z2 if (coords[i * 3 + 1] < baseElevation) { // if particle // below base coords[i * 3 + 0] = 0.0f; // x1 coords[i * 3 + 1] = baseElevation; // y1 coords[i * 3 + 2] = 0.0f; // z1 coords[i * 3 + 3] = 0.0f; // x2 coords[i * 3 + 4] = baseElevation; // y2 coords[i * 3 + 5] = 0.0f; // z2 } } else { // an inactive particle if (random.nextFloat() > 0.8) { // randomly start a drop coords[i * 3 + 0] = 0.03f * (random.nextFloat() - 0.5f); // x1 coords[i * 3 + 1] = 0.14f * random.nextFloat() + baseElevation; // y1 coords[i * 3 + 2] = 0.03f * (random.nextFloat() - 0.5f); // z1 } // end if } // end if-else } // end for loop } } } Đoạn mã sau định nghĩa lớp UpdateWaterBehavior, một lớp mở rộng từ lớp Behavior. Đây là đoạn mã dễ nhất trong ứng dụng GeometryUpdater. Lớp Behavior điều khiển hoạt ảnh bằng cách gọi phương thức updateGeometry của đối tượng hình học được tạo hoạt ảnh khi phương thức processStimulus của nó được gọi. Định nghĩa lớp UpdateWaterBehavior bao gồm trường w – một tham chiếu đến đối tượng WakeupOnElasedFrames - được sử dụng để kích hoạt đối tượng hành vi. Đối tượng WakeupOnElasedFrames được tạo trong phương thức khởi tạo của UpdateWaterBehavior bắt đầu từ dòng mã được dán nhãn . Phương thức initialize() của lớp UpdateWaterBehavior, bắt đầu từ dòng mã , thiết lập điều kiện đánh thức ban đầu cho đối tượng hành vi. Phương thức processStimulus(), bắt đầu từ dòng mã gán nhãn , định nghĩa các hành động của đối tượng hành vi đáp ứng lại các sự kiện đánh thức nó. Trong trường hợp này, phương thức updateData() được gọi và truyền tham số geometryUpdater cho waterLines. abstract class UpdateWaterBehavior extends Behavior { WakeupOnElapsedFrames w = null; public UpdateWaterBehavior() { w = new WakeupOnElapsedFrames(0); } public void initialize() { wakeupOn(w); } public void processStimulus(Enumeration critiria) { waterLines.updateData(geometryUpdater); wakeupOn(w); } // end processStimulus } // end class UpdateWaterBehavior Đoạn mã sau định nghĩa lớp GeometryUpdater. GeometryUpdater dịch chuyển các phần tử nước bằng cách thay đổi dữ liệu tọa độ của chúng. Lớp GeometryUpdater trong ứng dụng này có một hàm khởi tạo và một phương thức. Trong hàm khởi tạo, nhãn , một đối tượng Random được tạo ra để sử dụng trong phương thức thường duy nhất của lớp, phương thức updateData(). Phương thức updateData(), nhãn , tạo hoạt ảnh cho các phần tử nước. Thông thường, không phải tất cả các phần tử đều hoạt động tại cùng một thời điểm. Phần tử nào không hoạt động sẽ có tọa độ y trùng với tọa độ y của chân đài phun (baseElevation). Nếu một phần tử có tọa độ y bằng với baseElevation, nó được coi là không hoạt động và vì thế, phần tử này không di chuyển. Ban đầu, tất cả các phần tử nước đều không hoạt động. Xét hệ phần tử này một thời gian ngắn sau khi khởi động, khi này, đã có một vài phần tử hoạt động, số còn lại thì chưa. Mỗi lần updateData() được gọi, tiến trình hoạt ảnh sẽ thu thập thông tin liên quan về đối tượng hình học để cập nhật. Trên dòng mã , tham số Geometry được ép kiểu thành GeometryArray. Trên dòng mã , một tham chiếu đến dữ liệu tọa độ đỉnh được lấy ra. Dòng thu thập số lượng đỉnh. Chú ý rằng, ứng dụng này có thể chạy hiệu quả hơn bằng cách tính toán các thông tin này một lần rồi lưu trữ nó trong các trường của đối tượng. Tuy nhiên, hiệu quả đạt được cũng chỉ hạn chế và làm cho đoạn mã không sử dụng lại được. Lớp Geometry này có thể được sử dụng cho đài phun với các kích thước khác nhau. Khi đã có những bước chuẩn bị thích hợp, phương thức updateData() xét mỗi phần tử tại một thời điểm nào đó trong vòng lặp . Với mỗi phần tử hoạt động (xác định bằng cách so sánh tọa độ y của nó với baseElevation), chương trình sẽ tính toán chuyển động cong dạng parabol của nó. Tọa độ đỉnh đầu tiên của một phần tử được gán các giá trị thích hợp để mô hình chuyển động, sau đó, tọa độ cũ của đỉnh thứ nhất sẽ được gán cho đỉnh thứ hai. Câu lệnh if trên dòng kiểm tra xem phần tử đã vượt qua baseElevation hay chưa. Nếu điều kiện này thỏa mãn, phần tử đó sẽ ngừng hoạt động bởi chương trình cập nhật giá trị tọa độ cả hai đỉnh của nó về giá trị ban đầu, giá trị xác định phần tử không hoạt động. Một phần các phần tử không hoạt động, phần else của điều kiện trên dòng , được ngẫu nhiên khởi tạo bởi điều kiện trên dòng . Trong ví dụ này, một lượng trung bình khoảng 20% các phần tử không hoạt động sẽ được khởi tạo. public class WaterUpdater implements GeometryUpdater { Random random; public WaterUpdater() { random = new Random(); } public void updateData(Geometry geometry) { GeometryArray geometryArray = (GeometryArray) geometry; float[] coords = geometryArray.getCoordRefFloat(); int N = geometryArray.getValidVertexCount(); int i; for (i = 0; i < N; i += 2) { // for each particle if (coords[i * 3 + 1] > baseElevation) { // update active // particles coords[i * 3 + 0] += coords[i * 3 + 0] - coords[i * 3 + 3]; // x1 coords[i * 3 + 1] += coords[i * 3 + 1] - coords[i * 3 + 4] - 0.01f; // y1 coords[i * 3 + 2] += coords[i * 3 + 2] - coords[i * 3 + 5]; // z1 coords[i * 3 + 3] = (coords[i * 3 + 0] + coords[i * 3 + 3]) / 2; // x2 coords[i * 3 + 4] = (coords[i * 3 + 1] + coords[i * 3 + 4] + 0.01f) / 2;// y2 coords[i * 3 + 5] = (coords[i * 3 + 2] + coords[i * 3 + 5]) / 2; // z2 if (coords[i * 3 + 1] < baseElevation) { // if particle // below base coords[i * 3 + 0] = 0.0f; // x1 coords[i * 3 + 1] = baseElevation; // y1 coords[i * 3 + 2] = 0.0f; // z1 coords[i * 3 + 3] = 0.0f; // x2 coords[i * 3 + 4] = baseElevation; // y2 coords[i * 3 + 5] = 0.0f; // z2 } } else { // an inactive particle if (random.nextFloat() > 0.8) { // randomly start a drop coords[i * 3 + 0] = 0.03f * (random.nextFloat() - 0.5f); // x1 coords[i * 3 + 1] = 0.14f * random.nextFloat() + baseElevation; // y1 coords[i * 3 + 2] = 0.03f * (random.nextFloat() - 0.5f); // z1 } // end if } // end if-else } // end for loop } } Cột nước trong hoạt ảnh trông sẽ đẹp hơn nếu các đoạn thẳng biểu diễn mỗi phần tử loại bỏ được răng cưa. Việc này có thể thực hiện được bằng cách thêm đối tượng LineAttributes với setLineAntialiasedEnable(true) vào thành phần Appearance của phần tử nước. Tuy nhiên, lưu ý rằng, việc làm này có thể phải trả giá bởi tốc độ xử lý hoạt ảnh. Tài liệu tham khảo Trong quá trình làm cuốn tutorial về lập trình đồ họa trong Java 2D & 3D chúng em có tham khảo các tài liệu sau: 1. Slide bài giảng Kĩ thuật đồ họa và hiện thực ảo của ThS.Lê Tấn Hùng. 2. The Java Tutorial, 2nd Volume. Available online at: 3. The 2D Text Tutorial. Available online from the Java Developer Connection: 3. The Java 2D Sample Programs. Available online at: 4. The Java 2D Demo. Available from the Java 2D website: 5. The Java 3D document at: ._.

Các file đính kèm theo tài liệu này:

  • docDAN011.doc