Search code examples
javajavafxresizeshapesborderpane

JavaFX center an resizable arc in a borderpane


I have a question. I am creating an application where an Arc length changes and I want that arc inside a borderpane, but when i change the length of the Arc it will get centered, so it doesn't look like a circle that's getting filled. Actually what i need is an arc and calculate it's position (the center of the borderpane) by it's maximum length (360). Can someone help me with this problem? Thank you very much.


Solution

  • Create a Group. Place a rectangle in the group which is the size of a full circle (e.g. the rectangle's height and width is set to the circle's diameter), then add the arc to the group, with the arc layout position set to the circle's radius. Place the Group in a StackPane so that the fixed size Group will be centered within a resizable Region. Place the StackPane in the center of your BorderPane. Set the minimum size of the StackPane to zero so that it can be sized smaller than the Group, keeping the Group centered and clipped within it's visible bounds. Add some controls to the bottom of the BorderPane so that you can control the length of the arc dynamically.

    arcimage

    import javafx.application.Application;
    import javafx.geometry.Insets;
    import javafx.scene.*;
    import javafx.scene.control.Slider;
    import javafx.scene.layout.*;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.*;
    import javafx.stage.Stage;
    
    public class ArcControl extends Application {
        @Override
        public void start(Stage stage) throws Exception {
            CenteredArc centeredArc = new CenteredArc();
            ArcControls arcControls = new ArcControls(centeredArc.getArc());
    
            BorderPane layout = new BorderPane();
            layout.setCenter(centeredArc.getArcPane());
            layout.setBottom(arcControls.getControlPane());
    
            stage.setScene(new Scene(layout));
            stage.show();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    
    class CenteredArc {
        private static final double INSET = 10;
    
        private static final double ARC_RADIUS = 100;
        private static final double INITIAL_ARC_LENGTH  = 60;
        private static final double ARC_STROKE_WIDTH = 10;
        private static final double ARC_REGION_SIZE =
                ARC_RADIUS * 2 + ARC_STROKE_WIDTH + INSET * 2;
    
        private final Arc arc;
        private final Pane arcPane;
    
        public CenteredArc() {
            // Create the arc.
            arc = new Arc(
                    ARC_REGION_SIZE / 2, ARC_REGION_SIZE / 2,
                    ARC_RADIUS, ARC_RADIUS,
                    0,
                    INITIAL_ARC_LENGTH
            );
            arc.setStrokeWidth(10);
            arc.setStrokeLineCap(StrokeLineCap.ROUND);
            arc.setStroke(Color.FORESTGREEN);
            arc.setFill(Color.POWDERBLUE);
    
            // Create a background fill on which the arc will be centered.
            // The paint of the background fill can be set to Color.TRANSPARENT
            // if you don't want the fill to be seen.
            final double fillSize = ARC_RADIUS * 2 + arc.getStrokeWidth() + INSET * 2;
            Rectangle fill = new Rectangle(fillSize, fillSize, Color.PINK);
    
            // Place the fill and the arc in the group.
            // The Group will be a fixed sized matching the fill size.
            Group centeredArcGroup = new Group(fill, arc);
    
            // place the arc group in a StackPane so it is centered in a resizable region.
            arcPane = new StackPane(centeredArcGroup);
            arcPane.setPadding(new Insets(INSET));
            arcPane.setMinSize(0, 0);
            arcPane.setStyle("-fx-background-color: papayawhip;");
        }
    
        public Arc getArc() {
            return arc;
        }
    
        public Pane getArcPane() {
            return arcPane;
        }
    }
    
    // helper class which can use a slider to control an arc.
    class ArcControls {
        private static final double INSET = 10;
    
        private final Slider slider;
        private final VBox controlPane;
    
        public ArcControls(Arc arc) {
            slider = new Slider(0, 360, arc.getLength());
            controlPane = new VBox(
                    slider
            );
            controlPane.setPadding(
                    new Insets(INSET)
            );
            controlPane.setStyle(
                    "-fx-background-color: palegreen;"
            );
    
            arc.lengthProperty().bind(slider.valueProperty());
        }
    
        public Slider getSlider() {
            return slider;
        }
    
        public VBox getControlPane() {
            return controlPane;
        }
    }