javafxresizepolygonpointsproperty-binding

Property binding with the points of a polygon in JavaFX


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.


Solution

  • 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.