Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaFX: Apply perspective transformation on a node given a perspective transformed pane

I made a small demo to exemplify the problem I am having. A walkthrough on its use:

1 - Click on the screen four times in order to create the grid.

2 - Click on the button at the bottom to give the created grid the perspective effect.

3 - Click on the screen again to draw a circle.

enter image description here

My problem is that I don't know what transformation I should apply on the Circle node in order to have the same visual effect as the grid at the exact location where the user clicked.

The demo at this point only draws a circle with no transformations.

The desired output is as it follows (I cheated here using an Ellipse instead of Circle - this cannot be my solution because the user inputs the four coordinates of the grid):

enter image description here

Here's the code for the demo.

Main.java

package application;

import java.io.IOException;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Scene;


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(getClass().getResource("View.fxml"));
            Scene scene = new Scene(loader.load(), 600, 600);
            primaryStage.setScene(scene);
            primaryStage.setTitle("Perspective Transformation");
            primaryStage.show();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

View.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>

<VBox fx:id="vBox" maxHeight="600.0" maxWidth="600.0" minHeight="600.0" minWidth="600.0" prefHeight="600.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller">
   <children>
      <AnchorPane fx:id="anchorPane" maxHeight="575.0" minHeight="575.0" prefHeight="575.0" prefWidth="600.0" />
      <Button fx:id="button" alignment="CENTER" mnemonicParsing="false" prefHeight="25.0" prefWidth="600.0" text="Perspective Transformation" />
   </children>
</VBox>

Controller.java

package application;

import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.effect.PerspectiveTransform;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;

public class Controller {
    @FXML
    VBox vBox;
    @FXML
    AnchorPane anchorPane;
    @FXML
    Button button;

    Line line_1, line_2, line_3, line_4, middle_1, middle_2;

    private int numberOfClicks = 0;
    private int W = 10;
    //Store coordinates of the corresponding clicks
    private double cx1, cy1,
                   cx2, cy2,
                   cx3, cy3,
                   cx4, cy4,
                   cx5, cy5;

    @FXML
    private void initialize() {
        button.setDisable(true);

        button.setOnAction((event) -> {

            //Create a standard grid
            Line standard_1 = new Line(0, 0, 0, 600);
            Line standard_2 = new Line(0, 600, 600, 600);
            Line standard_3 = new Line(600, 600, 600, 0);
            Line standard_4 = new Line(600, 0, 0, 0);
            //Middle ones
            Line standard_5 = new Line(300, 0, 300, 600);
            Line standard_6 = new Line(0, 300, 600, 300);

            standard_1.setStrokeWidth(W);
            standard_2.setStrokeWidth(W);
            standard_3.setStrokeWidth(W);
            standard_4.setStrokeWidth(W);
            standard_5.setStrokeWidth(W);
            standard_6.setStrokeWidth(W);

            anchorPane.getChildren().clear();

            Pane perspectivePane = new Pane();

            perspectivePane.getChildren().addAll(standard_1, standard_2, standard_3,
                                            standard_4, standard_5, standard_6);

            PerspectiveTransform pt = new PerspectiveTransform();

            pt.setUlx(cx1);
            pt.setUly(cy1);
            pt.setUrx(cx2);
            pt.setUry(cy2);
            pt.setLrx(cx3);
            pt.setLry(cy3);
            pt.setLlx(cx4);
            pt.setLly(cy4);

            perspectivePane.setEffect(pt);

            anchorPane.getChildren().add(perspectivePane);

        });

        anchorPane.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {

            @Override
            public void handle(MouseEvent event) {
                numberOfClicks++;

                if(numberOfClicks == 1){
                    cx1 = event.getX();
                    cy1 = event.getY();
                }
                else if(numberOfClicks == 2){
                    cx2 = event.getX();
                    cy2 = event.getY();
                    line_1 = new Line(cx1, cy1, cx2, cy2);
                    line_1.setStrokeWidth(W);
                    anchorPane.getChildren().add(line_1);
                }
                else if(numberOfClicks == 3){
                    cx3 = event.getX();
                    cy3 = event.getY();
                    line_2 = new Line(cx2, cy2, cx3, cy3);
                    line_2.setStrokeWidth(W);
                    anchorPane.getChildren().add(line_2);
                }
                else if(numberOfClicks == 4){
                    cx4 = event.getX();
                    cy4 = event.getY();
                    line_3 = new Line(cx3, cy3, cx4, cy4);
                    line_4 = new Line(cx4, cy4, cx1, cy1);
                    line_3.setStrokeWidth(W);
                    line_4.setStrokeWidth(W);
                    middle_1 = new Line((cx1+cx2)/2, (cy1+cy2)/2, (cx3+cx4)/2, (cy3+cy4)/2);
                    middle_2 = new Line((cx2+cx3)/2, (cy2+cy3)/2, (cx4+cx1)/2, (cy4+cy1)/2);
                    middle_1.setStrokeWidth(W);
                    middle_2.setStrokeWidth(W);
                    anchorPane.getChildren().addAll(line_3, line_4, middle_1, middle_2);
                    button.setDisable(false);
                }
                else if(numberOfClicks == 5){
                    cx5 = event.getX();
                    cy5 = event.getY();
                    Circle circle = new Circle();
                    circle.setCenterX(cx5);
                    circle.setCenterY(cy5);
                    circle.setRadius(30);
                    circle.setFill(Color.TRANSPARENT);
                    circle.setStroke(Color.BLACK);
                    circle.setStrokeWidth(W);

                    //PerspectiveTransform pt = new PerspectiveTransform();

                    //What transformation should I apply to the circle?

                    //circle.setEffect(pt);
                    anchorPane.getChildren().add(circle);
                }
                else {
                    anchorPane.getChildren().clear();
                    numberOfClicks = 0;
                }
            }
        });
    }
}
like image 653
ihavenoidea Avatar asked Feb 20 '18 19:02

ihavenoidea


1 Answers

I don't know for sure if this is what you wanted, but I used Group to accomplish this. I followed the code from here. Test this update. I will try explaining it tomorrow if it works.

import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Group;
import javafx.scene.control.Button;
import javafx.scene.effect.PerspectiveTransform;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;

/**
 *
 * @author blj0011
 */
public class FXMLDocumentController implements Initializable
{

    @FXML
    VBox vBox;
    @FXML
    AnchorPane anchorPane;
    @FXML
    Button button;

    Group group = new Group();
    Group group2 = new Group();

    PerspectiveTransform pt = new PerspectiveTransform();

    Line line_1, line_2, line_3, line_4, middle_1, middle_2;

    private int numberOfClicks = 0;
    private int W = 10;
    //Store coordinates of the corresponding clicks
    private double cx1, cy1,
            cx2, cy2,
            cx3, cy3,
            cx4, cy4,
            cx5, cy5;

    @Override
    public void initialize(URL location, ResourceBundle resources)
    {
        button.setDisable(true);

        anchorPane.getChildren().addAll(group);

        button.setOnAction((event) -> {


            pt.setUlx(cx1);
            pt.setUly(cy1);
            pt.setUrx(cx2);
            pt.setUry(cy2);
            pt.setLrx(cx3);
            pt.setLry(cy3);
            pt.setLlx(cx4);
            pt.setLly(cy4);

            group.setEffect(pt);
        });

        anchorPane.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event) -> {
            numberOfClicks++;

            switch (numberOfClicks) {
                case 1:
                    cx1 = event.getX();
                    cy1 = event.getY();
                    System.out.println(cx1 + " : " + cy1);
                    break;
                case 2:
                    cx2 = event.getX();
                    cy2 = event.getY();
                    line_1 = new Line(cx1, cy1, cx2, cy2);
                    line_1.setStrokeWidth(W);
                    group.getChildren().add(line_1);
                    break;
                case 3:
                    cx3 = event.getX();
                    cy3 = event.getY();
                    line_2 = new Line(cx2, cy2, cx3, cy3);
                    line_2.setStrokeWidth(W);
                    group.getChildren().add(line_2);
                    break;
                case 4:


                    cx4 = event.getX();
                    cy4 = event.getY();



                    line_3 = new Line(cx3, cy3, cx4, cy4);
                    line_4 = new Line(cx4, cy4, cx1, cy1);
                    line_3.setStrokeWidth(W);
                    line_4.setStrokeWidth(W);
                    middle_1 = new Line((cx1 + cx2) / 2, (cy1 + cy2) / 2, (cx3 + cx4) / 2, (cy3 + cy4) / 2);
                    middle_2 = new Line((cx2 + cx3) / 2, (cy2 + cy3) / 2, (cx4 + cx1) / 2, (cy4 + cy1) / 2);
                    middle_1.setStrokeWidth(W);
                    middle_2.setStrokeWidth(W);
                    group.getChildren().addAll(line_3, line_4, middle_1, middle_2);
                    button.setDisable(false);
                    break;
                case 5:
                    List<Double> centerOfTransform = findCenterOfTransForm(pt);

                    cx5 = event.getX();
                    cy5 = event.getY();
                    List<Double> list = new ArrayList();
                    list.add(cx5);
                    list.add(cy5);
                    List<Double> changeInCenterXAndCenterY = findChangeInCenterXAndY(centerOfTransform, list);


                    PerspectiveTransform ptCircle = new PerspectiveTransform();
                    ptCircle.setUlx(cx1 + changeInCenterXAndCenterY.get(0));
                    ptCircle.setUly(cy1 + changeInCenterXAndCenterY.get(1));
                    ptCircle.setUrx(cx2 + changeInCenterXAndCenterY.get(0));
                    ptCircle.setUry(cy2 + changeInCenterXAndCenterY.get(1));
                    ptCircle.setLrx(cx3 + changeInCenterXAndCenterY.get(0));
                    ptCircle.setLry(cy3 + changeInCenterXAndCenterY.get(1));
                    ptCircle.setLlx(cx4 + changeInCenterXAndCenterY.get(0));
                    ptCircle.setLly(cy4 + changeInCenterXAndCenterY.get(1));




                    System.out.println("cx5: " + cx5 + "  cy5: " + cy5);
                    Circle circle = new Circle();

                    circle.setRadius(30);
                    circle.setFill(Color.TRANSPARENT);
                    circle.setStroke(Color.BLACK);
                    circle.setStrokeWidth(W);


                     circle.setCenterX(cx5);
                    circle.setCenterY(cy5);
                    circle.setEffect(ptCircle);
                    anchorPane.getChildren().add(circle);
                    System.out.println("centerx: " + circle.getTranslateX()+ "  centery: " + circle.getTranslateY());
                    break;
                default:
                    group.getChildren().clear();
                    numberOfClicks = 0;
                    break;
            }
        });
    }

     List<Double> findCenterOfTransForm(PerspectiveTransform pt)
    {
        List<Double> tempList = new ArrayList();
        tempList.add((pt.getUlx() + pt.getUrx() + pt.getLrx() + pt.getLlx()) / 4);
        tempList.add((pt.getUly() + pt.getUry() + pt.getLry() + pt.getLly()) / 4);

        return tempList;
    }

     List<Double> findChangeInCenterXAndY(List<Double> originalXAndY, List<Double> newXAndY)
     {
         List<Double> tempList = new ArrayList();
         tempList.add(newXAndY.get(0) - originalXAndY.get(0));
         tempList.add(newXAndY.get(1) - originalXAndY.get(1));

         return tempList;
     }


}
like image 145
Sedrick Avatar answered Oct 03 '22 11:10

Sedrick