Property binding with the points of a polygon in JavaFX

963 views Asked by At

In the following example I bound a grey rectangle with a pane in height and width. The blue polygon should fit proportionally to the rectangle. This means that the borders of the rectangle and the polygon should have the same size, when I resize the window.

I guess I can achieve this with the .addListener method (via widthProperty and heightProperty) of the rectangle. But I did not succeed.

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class PolygonFunktion extends Application {

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

    @Override
    public void start(Stage stage) throws Exception {

        Pane pane = new Pane();
        
        //Rectangle
        Rectangle rec = new Rectangle();
        rec.setFill(Color.GRAY);
        
        rec.layoutXProperty().set(20);
        rec.layoutYProperty().set(20);
        rec.widthProperty().bind(pane.widthProperty().subtract(rec.layoutXProperty().multiply(2)));
        rec.heightProperty().bind(pane.heightProperty().subtract(rec.layoutYProperty().multiply(2)));

        //Polygon
        Polygon poly = new Polygon();
        Double[] xy = {0.,0.,  0.,-10., 10.,-10., 10.,-20., 20.,-20., 20.,-30., 30.,-30., 30.,0. };
        poly.getPoints().addAll(xy);
        poly.setFill(Color.BLUE);
        
        poly.layoutXProperty().bind(rec.layoutXProperty());
        poly.layoutYProperty().bind(rec.layoutYProperty().add(rec.heightProperty()));
                
        rec.widthProperty().addListener( (o,oP,nP) -> {
            //... ?
        });
        
        rec.heightProperty().addListener( (o,oP,nP) -> {
            //... ?         
        }); 
        
        pane.getChildren().add(rec);
        pane.getChildren().add(poly);

        stage.setScene(new Scene(pane, 300, 300));
        stage.show();
    }
}

enter image description here

I do not want to use scale methods, because the edge of the polygon would be scaled as well.

2

There are 2 answers

0
James_D On BEST ANSWER

You can just update the points in the listener. Note that since each listener is doing the same thing, you only need one. Something like:

final int numBars = 3 ;
ChangeListener<Number> recSizeListener = (obs, oldValue, newValue) -> {
    double width = rec.getWidth();
    double height = rec.getHeight();
    Double[] points = new Double[(numBars+1)*4];
    for (int i = 0 ; i <= numBars ; i++) {
        // x values 
        points[i*4] = points[i*4+2] = i*width/numBars ;
        // y values
        points[i*4+1] = -i*height/numBars ;
        points[i*4+3] = -(i+1)*height/numBars ;
    }
    // fix last point:
    points[numBars*4+3] = height ;
    poly.getPoints().setAll(points);
};
rec.widthProperty().addListener(recSizeListener);
rec.heightProperty().addListner(recSizeListener);

Instead of using bindings and listeners, however, it's probably better here to use a custom Pane and override the layoutChildren() method:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class PolygonFunktion extends Application {

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

    @Override
    public void start(Stage stage) throws Exception {

        Rectangle rec = new Rectangle();
        rec.setFill(Color.GRAY);
        Polygon poly = new Polygon();
        poly.setFill(Color.BLUE);
        
        final int numBars = 3 ;

        Pane pane = new Pane(rec, poly) {

            final double margin = 20 ;

            @Override
            protected void layoutChildren() {
                double width = getWidth();
                double height = getHeight();
                Double[] points = new Double[(numBars+1)*4];
                rec.setX(margin);
                rec.setY(margin);
                double recWidth = width-2*margin ;
                double recHeight = height-2*margin ;
                rec.setWidth(recWidth);
                rec.setHeight(recHeight);

                for (int bar = 0 ; bar <= numBars ; bar++) {
                    points[4*bar] = points[4*bar+2] = margin + bar*recWidth/numBars ;
                    points[4*bar+1] = recHeight + margin - bar*recHeight/numBars ;
                    points[4*bar+3] = recHeight + margin - (bar+1)*recHeight/numBars ;
                }
                points[4*numBars + 3] = recHeight + margin ;
                
                poly.getPoints().setAll(points);
            }
        };

        stage.setScene(new Scene(pane, 300, 300));
        stage.show();
    }
}

This will ensure the calculations are only made once, each time the pane is laid out.

0
ralle On

Okay, I have a solution that may be unattractive, but it works.

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class PolygonFunktion extends Application {

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

    @Override
    public void start(Stage stage) throws Exception {

        Pane pane = new Pane();
        
        //Rectangle
        Rectangle rec = new Rectangle();
        rec.setFill(Color.GRAY);
        
        rec.layoutXProperty().set(20);
        rec.layoutYProperty().set(20);
        rec.widthProperty().bind(pane.widthProperty().subtract(rec.layoutXProperty().multiply(2)));
        rec.heightProperty().bind(pane.heightProperty().subtract(rec.layoutYProperty().multiply(2)));

        //Polygon
        Polygon poly = new Polygon();
        Double[] xy = {0.,0.,  0.,-10., 10.,-10., 10.,-20., 20.,-20., 20.,-30., 30.,-30., 30.,0. };
        
        poly.getPoints().addAll(xy);
        poly.setFill(Color.BLUE);
        
        poly.layoutXProperty().bind(rec.layoutXProperty());
        poly.layoutYProperty().bind(rec.layoutYProperty().add(rec.heightProperty()));
        
        
        double maxWidth = poly.getLayoutBounds().getWidth(); 
        double maxHeight =  poly.getLayoutBounds().getHeight();

        
        rec.widthProperty().addListener( (o,oP,nP) -> { 
            
            for(int i=0;  i<poly.getPoints().size(); i+=2) { 
                double polfac = xy[i] / maxWidth;               
                poly.getPoints() .set(i, polfac*nP.doubleValue());
            }  
        });
        
        rec.heightProperty().addListener( (o,oP,nP) -> { 
            
            for(int i=1;  i<poly.getPoints().size(); i+=2) { 
                double polfac = xy[i] / maxHeight;
                poly.getPoints() .set(i, polfac*nP.doubleValue());
            }  
        }); 
        
        pane.getChildren().add(rec);
        pane.getChildren().add(poly);

        stage.setScene(new Scene(pane, 300, 300));
        stage.show();
    }
}