Thứ Ba, 2 tháng 6, 2015

[JavaFX 8] Phần 8 (tt) : Hiệu ứng - Effect

Phần 8 (tt) : Hiệu ứng - Effect

11. PerspectiveTransform :

PerspectiveTransform là hiệu ứng thường được sử dụng để tạo ra sự quay input theo hướng của trục z. Nó làm biến dạng input bằng cách di chuyển các điểm góc đến vị trí được chỉ ra và định vị lại các pixel theo các đường thẳng được nối từ các góc.

a.Các thuộc tính :

kiểu
Tên thuộc tính
Giá trị mặc định
Miêu tả
Effect
input
null
input của perspectiveTransform
DoubleProperty
llx
0
tọa độ x của góc trái thấp nhất của input.
DoubleProperty
lly
0
tọa độ y của góc trái thấp nhất của input.
DoubleProperty
ulx
0
tọa độ x của góc trái cao nhất của input.
DoubleProperty
uly
0
tọa độ y của góc trái cao nhất của input.
DoubleProperty
lrx
0
tọa độ x của góc phải thấp nhất của input.
DoubleProperty
lry
0
tọa độ y của góc phải thấp nhất của input.
DoubleProperty
urx
0
tọa độ x của góc phải cao nhất của input.
DoubleProperty
ury
0
tọa độ y của góc phải cao nhất của input.

b. Cách sử dụng :

// Tạo đối tượng PerspectiveTransform với giá trị thuộc tính mặc định
PerspectiveTransform perspectiveTrasform = new PerspectiveTransform();
// Tạo đối tượng PerspectiveTransform với :
//  - tọa độ góc trái cao nhất (20,20)
//  - tọa độ góc phải cao nhất (100,40)
//  - tọa độ góc trái thấp nhất (20,300)
//  - tọa độ góc phải thấp nhất (100,240)
PerspectiveTransform perspectiveTrasform =
new PerspectiveTransform(20,20, 100,40, 20,300, 100, 240);

c. Một số phương thức của PerspectiveTransform:

kiểu trả về
Phương thức
Miêu tả
void
setInput(Effect  value)
Cung cấp input cho PerspectiveTransform effect.
Effect
getInput ()
Trả về input dành cho PerspectiveTransform .
void
setUlx(double value)
Thiết lập giá trị thuộc tính ulx
double
getUlx()
Trả về giá trị thuộc tính ulx.
void
setUly(double value)
Thiết lập giá trị thuộc tính uly
double
getUly()
Trả về giá trị thuộc tính uly.
void
setUrx(double value)
Thiết lập giá trị thuộc tính urx
double
getUrx()
Trả về giá trị thuộc tính urx
void
setUry(double value)
Thiết lập giá trị thuộc tính ury
double
getUry()
Trả về giá trị thuộc tính ury
void
setLlx(double value)
Thiết lập giá trị thuộc tính llx
double
getLlx()
Trả về giá trị thuộc tính llx
void
setLly(double value)
Thiết lập giá trị thuộc tính lly
double
getLly()
Trả về giá trị thuộc tính lly
void
setLrx(double value)
Thiết lập giá trị thuộc tính lrx
double
getLrx()
Trả về giá trị thuộc tính lrx
void
setLry(double value)
Thiết lập giá trị thuộc tính lry
double
getLry()
Trả về giá trị thuộc tính lry

d. Ví dụ :

Viết chương trình sử dụng Slider thể hiện giá trị góc quay khi quay input quanh 1 trục nhưng hình minh họa

Đầu tiên, chúng ta phải cung cấp tọa độ trục quay I1,  I2 và gốc quay α.  Dựa vào công thức lượng giác, chúng ta xác định tọa độ A’, B’, C’, D’.
xa’ = xi1 - (xi1 – xa) *cosα;
ya’ = yi1 - (xi1 – xa) * sinα;
xb’ = xi1 + (xi1 - xb) *cosα;
yb’ = yi1 - (xi1 – xb) * sinα;
xc’ = xi2 - (xi1 – xc) *cosα;
yc’ = yi2 + (xi1 – xc) * sinα;
xd’ = xi2 + (xi1 – xd) *cosα;
yd’ = yi2 - (xi1 – xd) * sinα;
Code :
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.effect.PerspectiveTransform;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import javafx.scene.paint.ImagePattern;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class examplePerspectiveTransform1 extends Application {

   // tọa độ I1,I2
   double xi1, yi1, xi2, yi2;
   //góc xoay
   double angle;

   PerspectiveTransform perspectiveTrasform;

   public void start(Stage primaryStage) {

       Rectangle rectangle = new Rectangle(100, 50, 250, 200);
       Image image = new Image("graphics/background.jpg");
       rectangle.setFill(new ImagePattern(image));

       // thiết lập tọa độ trục xoay ban đầu
       xi1 = xi2 = rectangle.getX() + 100;
       yi1 = rectangle.getY();
       yi2 = rectangle.getY() + rectangle.getHeight();

       //Slider thể hiện giá trị góc xoay
       Label labelAngle = new Label("Góc xoay : ");
       labelAngle.setLayoutX(0);
       labelAngle.setLayoutY(300);

       Slider sliderAngle = new Slider(0, 180, 10);
       sliderAngle.setLayoutX(100);
       sliderAngle.setLayoutY(300);
       sliderAngle.setShowTickLabels(true);
       sliderAngle.setShowTickMarks(true);
       sliderAngle.setPrefWidth(300);
       // Sự kiện khi thay đổi giá trị góc quay
       sliderAngle.valueProperty().addListener(new ChangeListener<Number>() {

           @Override
           public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
               // thiết lập lại hiệu ứng PerspectiveTransform khi góc quay thay đổi
               angle = Math.toRadians(newValue.doubleValue());
               // tọa độ Ulx, Uly
               double x1 = xi1 - (xi1 - rectangle.getX()) * Math.cos(angle);
               double y1 = yi1 - (xi1 - rectangle.getX()) * Math.sin(angle);

               // tọa độ Urx, Ury
               double x2 = xi1 +
Math.abs(xi1 - (rectangle.getX() + rectangle.getWidth())) * Math.cos(angle);
               double y2 =  yi1 –
Math.abs(xi1 - (rectangle.getX() + rectangle.getWidth())) * Math.sin(angle);

               // tọa độ Llx, Lly
               double x3 = xi2 - Math.abs(xi2 - (rectangle.getX())) * Math.cos(angle);
               double y3 = yi2 + Math.abs(xi2 - (rectangle.getX())) * Math.sin(angle);

               // tọa độ Lrx, Lry
               double x4 =  xi2 +
Math.abs(xi2 - (rectangle.getX() + rectangle.getWidth())) * Math.cos(angle);
               double y4 =  yi2 –
Math.abs(xi2 - (rectangle.getX() + rectangle.getWidth())) * Math.sin(angle);

               perspectiveTrasform.setUlx(x1);
               perspectiveTrasform.setUly(y1);
               perspectiveTrasform.setUrx(x2);
               perspectiveTrasform.setUry(y2);
               perspectiveTrasform.setLlx(x3);
               perspectiveTrasform.setLly(y3);
               perspectiveTrasform.setLrx(x4);
               perspectiveTrasform.setLry(y4);
           }

       });

       //Slider thể hiện giá trị tọa độ trục xoay I1,I2
       Label labelPivot = new Label("Tọa độ trục xoay :");
       labelPivot.setLayoutX(0);
       labelPivot.setLayoutY(340);

       Slider sliderPivot =
           new Slider(rectangle.getX(), rectangle.getX() + rectangle.getWidth(), rectangle.getX());
       sliderPivot.setShowTickLabels(true);
       sliderPivot.setShowTickMarks(true);
       sliderPivot.setLayoutX(100);
       sliderPivot.setLayoutY(340);
       sliderPivot.setPrefWidth(300);
       // Sự kiện khi thay đổi tọa độ trục xoay
       sliderPivot.valueProperty().addListener(new ChangeListener<Number>() {

           @Override
           public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
               // thiết lập lại tọa độ I1, I2
               xi1 = xi2 = newValue.doubleValue();
           }

       });
       // thiết lập sự hiển thị đầu tiên
       angle = Math.toRadians(sliderAngle.getValue());
       // tọa độ Ulx, Uly
       double x1 = xi1 - (xi1 - rectangle.getX()) * Math.cos(angle);
       double y1 = yi1 - (xi1 - rectangle.getX()) * Math.sin(angle);

       // tọa độ Urx, Ury
       double x2 =
            xi1 + Math.abs(xi1 - (rectangle.getX() + rectangle.getWidth())) * Math.cos(angle);
       double y2 =
            yi1 - Math.abs(xi1 - (rectangle.getX() + rectangle.getWidth())) * Math.sin(angle);

       // tọa độ Llx, Lly
       double x3 = xi2 - Math.abs(xi2 - (rectangle.getX())) * Math.cos(angle);
       double y3 = yi2 + Math.abs(xi2 - (rectangle.getX())) * Math.sin(angle);

       // tọa độ Lrx, Lry
       double x4 =
             xi2 + Math.abs(xi2 - (rectangle.getX() + rectangle.getWidth())) * Math.cos(angle);
       double y4 =
             yi2 - Math.abs(xi2 - (rectangle.getX() + rectangle.getWidth())) * Math.sin(angle);

       // hiệu ứng PerspectiveTransform
       perspectiveTrasform = new PerspectiveTransform();
       perspectiveTrasform.setUlx(x1);
       perspectiveTrasform.setUly(y1);
       perspectiveTrasform.setUrx(x2);
       perspectiveTrasform.setUry(y2);
       perspectiveTrasform.setLlx(x3);
       perspectiveTrasform.setLly(y3);
       perspectiveTrasform.setLrx(x4);
       perspectiveTrasform.setLry(y4);
       rectangle.setEffect(perspectiveTrasform);

       Group g = new Group();
       g.setLayoutX(30);
       g.setLayoutY(30);
       g.getChildren().addAll(rectangle, sliderAngle, labelAngle, sliderPivot, labelPivot);

       Scene scene = new Scene(g, 450, 400);
    
       primaryStage.setTitle("Hello World!");
       primaryStage.setScene(scene);
       primaryStage.show();
   }

   public static void main(String[] args) {
       launch(args);
   }
}

12. DisplacementMap

DisplacementMap là hiệu ứng di chuyển các pixel trong input đặt vào vị trí khác trong output.

a.Các thuộc tính :

Kiểu
Tên thuộc tính
Giá trị mặc định
Miêu tả
Effect
Input
null
input của DisplacementMap
FloatMap
mapData
empty map
Xây dựng phương án xác định các input pixel sẽ được di chuyển vào output.
DoubleProperty
Offset
0.0
Khoảng cách cố định theo trục X được áp dụng đến tất cả các input pixel di chuyển vào output.
DoubleProperty
Offset
0.0
Khoảng cách cố định theo trục Y được áp dụng đến tất cả các input pixel được di chuyển vào output.
DoubleProperty
scaleX
1.0
Bội số được áp dụng đến mapData theo trục X
DoubleProperty
scaleY
1.0
Bội số được áp dụng đến mapData theo trục Y
BooleanProperty
Wrap
False
- Nếu wrap = false : các pixel nằm ngoài các cạnh biên không xuất hiện trong output.
- Nếu hệ số wrap = true, các pixel nằm ngoài đường biên được cuộn lại và xuất hiện trong output.

b. Cách sử dụng :

// Tạo đối tượng DisplacementMap với giá trị thuộc tính mặc định
DisplacementMap displacementMap = new DisplacementMap();
// Tạo đối tượng DisplacementMap cung cấp thuộc tính mapData
DisplacementMap displacementMap = new DisplacementMap(mapData);
// Tạo đối tượng DisplacementMap cung cấp mapData, offsetX, offsetY, scaleX, scaleY
DisplacementMap displacementMap =
new DisplacementMap(mapData, offsetX, offsetY, scaleX, scaleY);

c. Một số phương thức của DisplacementMap:

Kiểu trả về
Phương thức
Miêu tả
void
setInput(Effect  value)
Cung cấp input cho DisplacementMap effect.
effect
getInput ()
Trả về input dành cho DisplacementMap.
void
setMapData(double value)
Thiết lập giá trị thuộc tính mapData.
floatmap
getMapData()
Trả về giá trị thuộc tính mapData.
void
setOffsetX(double value)
Thiết lập giá trị thuộc tính offsetX
double
getOffsetX()
Trả về giá trị thuộc tính offsetX.
void
setOffsetY(double value)
Thiết lập giá trị thuộc tính offsetY
double
getOffsetY()
Trả về giá trị thuộc tính offsetY .
void
setScaleX(double value)
Thiết lập giá trị thuộc tính scaleX.
double
getScaleX()
Trả về giá trị thuộc tính scaleX.
void
setScaleY(double value)
Thiết lập giá trị thuộc tính scaleY.
double
getScaleY()
Trả về giá trị thuộc tính scaleY .
boolean
isWrap()
Trả về giá trị thuộc tính wrap.
void
setWrap(boolean value)
Thiết lập giá trị thuộc tính wrap.

d. FloatMap class :

FloatMap là một cấu trúc dữ liệu mà mỗi phần tử  trong nó được xem là 1 tọa độ điểm (x,y) và mỗi tọa độ chứa tối đa 4 giá trị kiểu float. Mỗi giá trị kiểu float của 1 tọa độ điểm được gọi là band. Do đó, mỗi tọa độ có 4 band và được đánh số thứ tự 0,1,2,3. Số phần tử trong FloatMap được xác định bởi width (chiều rộng) và height (chiều cao).
FloatMap thường được sử dụng cho pixel màu sắc. Bởi, pixel được xác định bằng tọa độ x,y và màu sắc được xác định bằng 4 thành phần red,green, blue, alpha.

Tạo đối tượng FloatMap:

// Tạo đối tượng FloatMap có widh và height mặc định
FloatMap fmap = new FloatMap();
// Tạo đối tượng FloatMap có widh = 10 và height 20
FloatMap fmap = new FloatMap(10,20);

Một số phương thức của FloatMap :

Kiểu trả về
Phương thức
Miêu tả
void
setWidth(int  value)
Thiết lập width
int
getWidth()
Trả về giá trị width.
void
setHeight(int  value)
Thiết lập height
int
getHeight()
Trả về giá trị height.
void
setSample(int x, int y, int band, float s)
Thiết lập giá trị s dành cho band nào tại vị trí x, y
void
setSample(int x, int y, float s0)
Thiết lập giá trị s0 dành cho band 0 tại vị trí x, y
void
setSample(int x, int y, float s0, float s1)
Thiết lập giá trị s0, s1 dành cho band 0 và 1 nào tại vị trí x, y
void
setSample(int x, int y, float s0, float s1, float s3, float s4)
Thiết lập giá trị s0, s1, s2, s3 dành cho band 0,1,2,3 nào tại vị trí x, y

e. Phương trình của DisplacementMap :

DisplacementMap tính toán các pixel được lấy từ input dựa trên phương trình :
dst[x,y] = src[ x + (offsetX + scaleX*map[x,y][0]) * srcWidth,
y + (offsetY + scaleY*map[x,y][1]) * srcHeight ]
trong đó :
- dst[x,y] : là pixel có tọa độ x,y trong output.
- src[x1,y1] : là pixel có tọa độ x1, y1 trong input. Với :
x1 =  x + (offsetX + scaleX*map[x,y][0]) * srcWidth
y1 = y + (offsetY + scaleY*map[x,y][1]) * srcHeight
- offsetX, offsetY :  tương ứng thuộc tính offset và offset.
- scaleX, scaleY: tương ứng thuộc tính scaleX, scaleY.
- map[x.y] : tương ứng thuộc tính mapData. Trong đó, map[x,y][0] , map[x,y][1]  là giá trị kiểu float của band 0 và band 1 tại tọa độ x,y.
- srcWidthsrcHeight : là width và height của Input.
Ví dụ :
Giả sử offsetX = offsetY = 0, scaleX= scaleY = 1 , map[x,y][0] = 0.5, map[x,y][1] = -0.5, width = 2, height = 2. Phương trình trên có dạng như sau :
dst[x,y] = src[x + 1, y-1]
Để dễ hiểu, chúng tôi lập bảng thể hiện tọa độ pixel trong output được di chuyển từ pixel nào trong input :
Tọa độ pixel trong output
Tọa độ pixel trong input
0,0
1,-1
0,1
1,0
1,0
2,-1
1,1
2,0
Từ bảng trên, ta thấy :
- Pixel (1,-1) trong input được di chuyển vào pixel(0,0) của output. Nhưng trong input không tồn tại pixel (1,-1) bởi y < 0 nên tại pixel(0,0) của output không hiển thị gì cả.
- Pixel(1,0) trong input được di chuyển vào pixel(0,1) của output.
- Pixel (2,-1) trong input được di chuyển vào pixel(1,0) của output. Nhưng trong input không tồn tại pixel (2,-1) bởi y < 0 nên tại pixel(1,0) của output không hiển thị gì cả.
- Pixel (2,0) trong input được di chuyển vào pixel(1,1) của output. Nhưng trong input không tồn tại pixel (2,0) bởi x>=2 nên tại pixel(2,0) của output không hiển thị gì cả.
Kết quả, trong output chỉ hiển thị nội dung pixel(1,0) của input được di chuyển vào vị trí (0,1).

f. Thuộc tính offset :

Bạn muốn tất cả các pixel của input di chuyển 1 khoảng cách cố định so với các pixel trong output, bạn sử dụng thuộc tính offset. Thuộc tính offsetXoffsetY có giá trị [-1,1].
-  Di chuyển về bên trái : giá trị offsetX > 0
-  Di chuyển về bên phải : giá trị offsetX < 0
-  Di chuyển hướng lên  : giá trị offsetY > 0
-  Di chuyển hướng xuống : giá trị offsetY < 0
Ví dụ : tạo 1 text = “DisplacementMap Example” và di chuyển text về bên phải 30%
Vị trí ban đầu
Sử dụng hiệu ứng DisplacementMap di chuyển về bên phải 30%
Code :
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.effect.DisplacementMap;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class DisplacementmapExample extends Application {

   public static void main(String[] args) {
       Application.launch(args);
   }

   @Override
   public void start(Stage stage) {

       // tạo hiệu ứng Displacement
       DisplacementMap effect1 = new DisplacementMap();
       // tất cả các pixel trong input đều di chuyển 30% về bên phải
       effect1.setOffsetX(-0.3);

       Text t1 = new Text("DisplacementMap Example");
       t1.setFont(Font.font(36));
       t1.setEffect(effect1);

       HBox root = new HBox(t1);
       root.setStyle("-fx-padding: 10;"
               + "-fx-border-style: solid inside;"
               + "-fx-border-width: 2;"
               + "-fx-border-insets: 5;"
               + "-fx-border-radius: 5;"
               + "-fx-border-color: blue;");
       Scene scene = new Scene(root);
       scene.setFill(Color.CORAL);
       stage.setScene(scene);
       stage.show();
   }
}

 g. Thuộc tính mapData

Thuộc tính mapData là thực thể của FloatMap class, mỗi phần tử trong mapData chứa tọa độ pixel (x,y), mỗi tọa độ cần 2 band : band 0 chứa giá trị di chuyển theo chiều ngang, band 1 chứa giá trị duy chuyển theo chiều dọc, band 3 và band 4 không dùng đến.
Sử dụng thuộc tính mapData để tính giá trị di chuyển theo 1 công thức nào đó như ý muốn của bạn. Ví dụ, di chuyển theo hàm sin, hàm cos, phân nửa trên di chuyển bên trái, phân nửa dưới di chuyển bên phải,…
Ví dụ : Sử dụng hiệu ứng DisplacementMap để di chuyển ảnh theo hàm cos
Ảnh ban đầu
Ảnh sử dụng hiệu ứng DisplacementMap theo hàm cos trên trục y
Code :
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.effect.DisplacementMap;
import javafx.scene.effect.FloatMap;
import javafx.scene.image.Image;
import javafx.scene.paint.ImagePattern;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class exampleDisplacementMap extends Application {

   @Override
   public void start(Stage primaryStage) {

       Rectangle rectangle = new Rectangle(60, 20, 300, 200);
       Image image = new Image("graphics/background.jpg");
       rectangle.setFill(new ImagePattern(image));

       // tạo đối tượng floatMap có width và height
       // bằng width và height rectangle
       FloatMap floatMap = new FloatMap();
       floatMap.setWidth((int) rectangle.getWidth());
       floatMap.setHeight((int) rectangle.getHeight());

       // Vòng lặp for, thiết lập giá trị band 0 và band 1
       // cho mỗi tọa độ x,y
       for (int x = 0; x < rectangle.getWidth(); x++) {
           // v chứa giá trị của 1 hàm cos theo tọa độ x
           double v = Math.cos(x) / 50;
           for (int y = 0; y < rectangle.getHeight(); y++) {
               // thiết lập giá trị cho band 0 = 0.0f : không di chuyển theo chiều ngang
               // band 1 = v : di chuyển theo chiều dọc khoảng v
               floatMap.setSamples(x, y, 0.0f, (float) v);
           }
       }

       DisplacementMap displacementMap = new DisplacementMap();
       displacementMap.setMapData(floatMap);
       rectangle.setEffect(displacementMap);

       Group root = new Group();
       root.getChildren().add(rectangle);

       Scene scene = new Scene(root, 380, 300);

       primaryStage.setTitle("Hello World!");
       primaryStage.setScene(scene);
       primaryStage.show();
   }

   public static void main(String[] args) {
       launch(args);
   }
}

h. Thuộc tính Wrap :

- Nếu wrap = false : các pixel nằm ngoài các cạnh biên không xuất hiện trong output.
- Nếu hệ số wrap = true : các pixel nằm ngoài đường biên được cuộn lại và xuất hiện trong output.
Vị trí ban đầu

Sử dụng hiệu ứng DisplacementMap di chuyển về bên phải 30% với wrap = false

Sử dụng hiệu ứng DisplacementMap di chuyển về bên phải 30% với wrap = true

k. Thuộc tính scale :

Thuộc tính scaleXscaleY là bội số được dùng cho các giá trị của FloatMap. Bởi giá trị band 0 và band 1 của pixel trong mapData không được vượt quá [-1;1], nên chúng ta  phải sử dụng thuộc tính scale để tăng giá trị của band.
Ảnh ban đầu
Ảnh sử dụng hiệu ứng DisplacementMap theo hàm cos trên y với scaleY = 1
Ảnh sử dụng hiệu ứng DisplacementMap theo hàm cos trên y với scaleY = 3

Không có nhận xét nào:

Đăng nhận xét