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.
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):
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;
}
}
});
}
}
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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With