Bassusour
Bassusour

Reputation: 175

Rotate a polygon around its center using Rotate

I want to track my points in a Polygon class after transformations. My problem with using the Rotate class from JavaFX, is to get the points position after the rotation, by doing something like this in the Piece class:

double x = this.getPoints().get(0) + this.getLocalToParentTransform().getTx()
double y = this.getPoints().get(1) + this.getLocalToParentTransform().getTy()

It works using the Translate class, but when I rotate, the coordinates will rotate itself with the rotation with a weird pivot point, and not follow the polygon at all, unless it is rotated 360 degrees. Piece is a subclass of Polygon.

    private double originalX;
    private double originalY;
    private double centerX;
    private double centerY;

    private void initializePiece(Piece piece, Pane pane, int i) {
        piece.setOnMousePressed(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                originalX = event.getSceneX();
                originalY = event.getSceneY();
                centerX = piece.getCenterX();
                centerY = piece.getCenterY();
            }
        });

        piece.setOnMouseDragged(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                if (event.getButton() == MouseButton.PRIMARY) {
                    // Position of piece wrt. picked up position
                    double deltaX = event.getSceneX() - originalX;
                    double deltaY = event.getSceneY() - originalY;

                    Translate translate = new Translate();
                    translate.setX(deltaX);
                    translate.setY(deltaY);
                    piece.getTransforms().addAll(translate);

                    originalX = event.getSceneX();
                    originalY = event.getSceneY();
                }

                if (event.getButton() == MouseButton.SECONDARY) {
                    double deltaY = event.getSceneY() - originalY;
                    Rotate rotation = new Rotate(deltaY, centerX, centerY);
                    piece.getTransforms().add(rotation);
                    originalY = event.getSceneY();
                }
            }
        });
    }

And this is a snippet from the Piececlass.

    public double getCenterX() {
        double avg = 0;
        for (int i = 0; i < this.getPoints().size(); i += 2) {
            avg += this.getPoints().get(i) + this.getLocalToParentTransform().getTx();
        }
        avg = avg / (this.getPoints().size() / 2);
        
        return avg;
    }

    public double getCenterY() {
        double avg = 0;
        for (int i = 1; i < this.getPoints().size(); i += 2) {
            avg += this.getPoints().get(i) + this.getLocalToParentTransform().getTy();
        }
        avg = avg / (this.getPoints().size() / 2);
        return avg;
    }

Upvotes: 0

Views: 468

Answers (1)

James_D
James_D

Reputation: 209245

All transformations are applied in local coordinates, so the pivot point should be given in the local coordinate system of the polygon, which is "unaware" of the transformations applied to it. I.e. you should just compute the center of the polygon in its own coordinate system, not its parent's coordinate system.

The mouse event's getX() and getY() methods will give the coordinates in the coordinate system of the event's source node, so using these makes the computations relatively easy. Here's a simple example (I calculated the current angle between the mouse press and the center of the polygon, and then the change in angle on dragging, instead of just using the y-coordinate; this feels more natural.)

import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;

public class DraggingTransforms extends Application {
    
    public static void main(String[] args) {
        Application.launch(args);
    }
    
    private double pressX ;
    private double pressY ;
    private double angle ;

    @Override
    public void start(Stage stage) throws Exception {
        Polygon poly = new Polygon(10, 10, 50, 10, 50, 0, 70, 20, 50, 40, 50, 30, 10, 30, 10, 10);
        poly.setFill(Color.web("#00b140"));
        Pane root = new Pane(poly);
        
        poly.setOnMousePressed(e -> {
            pressX = e.getX();
            pressY = e.getY();
            angle = computeAngle(poly, e);
        });
        
        poly.setOnMouseDragged(e -> {
            if (e.getButton() == MouseButton.PRIMARY) {
                poly.getTransforms().add(new Translate(e.getX() - pressX, e.getY()-pressY));
            } else {
                double delta = computeAngle(poly, e) - angle;
                Rotate rotation = new Rotate(delta, computeCenterX(poly), computeCenterY(poly));
                poly.getTransforms().add(rotation);
            }
        });
        
        Scene scene = new Scene(root, 800, 800);
        stage.setScene(scene);
        stage.show();
    }

    private double computeAngle(Polygon poly, MouseEvent e) {
        return new Point2D(computeCenterX(poly), computeCenterY(poly))
                   .angle(new Point2D(e.getX(), e.getY()));
    }
    
    private double computeCenter(int offset, Polygon poly) {
        double total = 0 ;
        for (int i = offset ; i < poly.getPoints().size(); i+=2) {
            total += poly.getPoints().get(i);
        }
        return total / (poly.getPoints().size() / 2);
    }
    
    private double computeCenterX(Polygon poly) {
        return computeCenter(0, poly);
    }
    private double computeCenterY(Polygon poly) {
        return computeCenter(1, poly);
    }

}

Note that if you do need the coordinates of a point in the shape transformed to the parent's coordinate system, all you need to do is, for example,

poly.getLocalToParentTransform()
     .transform(
          poly.getPoints().get(0), 
          poly.getPoints().get(1)
     )

Upvotes: 3

Related Questions