2

I have to draw a sinus curve in a javafx canvas dot after dot and connect these. But as you can change the width of the line to it sometimes gets cut in width because the line is near the border. So is there a possibility to like make a padding or a border to avoid this? It would be possible to like manipulate the coordinates but tbh I dont want to do that as I think there should be a better solution. Picture of the canvas

EDIT:

This is the code example to reproduce it in a javafx project

public class HelloApplication extends Application {

    @Override
    public void start(Stage stage) throws IOException {
        Canvas c = new Canvas(715,495);
        GraphicsContext gc = c.getGraphicsContext2D();
        VBox v = new VBox();
        Pane p = new Pane();
        p.getChildren().add(c);


        Button b1 = new Button("Draw Sinus at Canvas");
        b1.setOnAction(e -> drawSinus(c, gc));

        v.getChildren().addAll(b1, p);
        Scene sc = new Scene(v);
        stage.setScene(sc);
        stage.setTitle("Drawing Lines - Dynamically at Runtime");
        stage.show();
    }


    private void drawSinus(Canvas c, GraphicsContext gc) {
        double height = c.getHeight();
        double width = c.getWidth();
        double multiplier = (2 * Math.PI)/width;
        double x1 = 0;
        double y1 = height/2;
        double x2 = 1;
        double y2 = 0;
        int i = 0;
        gc.setStroke(Color.BLACK);
        gc.setLineWidth(10);

        while(i < width) {
            y2 = (height/2) - ((height/2) * Math.sin(x2 * multiplier));
            gc.strokeLine(x1,y1,x2,y2);
            x1 = x2;
            y1 = y2;
            x2++;
            i++;
        }
    }

    public static void main(String[] args) {
        launch();
    }
}
Yubi
  • 57
  • 6
  • 1
    Create and post a [mre] demonstrating the issue. You might be able to solve this by setting a transform on the graphics context, or perhaps by setting appropriate stroke attributes (such as line cap). – James_D Sep 27 '22 at 16:36
  • @James_D I added the example – Yubi Sep 27 '22 at 17:03
  • That's not a [mre]. Can you create and post one? – James_D Sep 27 '22 at 17:04
  • Also, it looks like you are modifying the canvas from a background thread. This is not allowed (see [documentation](https://openjfx.io/javadoc/18/javafx.graphics/javafx/scene/canvas/GraphicsContext.html)). Use the animation API if you want to animate something. – James_D Sep 27 '22 at 17:06
  • Oh Sorry. I made a mistake reading the Task documentation saying something about FX Application Thread. Do you have a fix for this? I added the minimal reproducible example. Hope this is fine now. It should show the issue at the top and bottom very clear. Yes I know that I should use animation API for stuff like this but its my task in school to do it like that. – Yubi Sep 27 '22 at 17:38
  • If you have to use a background thread, you should wrap the calls to the `GraphicsContext` in `Platform.runLater(...)`. – James_D Sep 27 '22 at 18:19
  • And if you don't have to use a background, but you'd like the graph to be incrementally drawn in front of the user, then use the `javafx.animation` API. – Slaw Sep 27 '22 at 19:19
  • Unrelated to your question, but you might be interested in some similar plotting questions [Draw Cartesian Plane Graph with canvas in JavaFX](https://stackoverflow.com/questions/24005247/draw-cartesian-plane-graph-with-canvas-in-javafx/24008426#24008426) and [Making a zoomable coordinate system in JavaFX](https://stackoverflow.com/questions/68944897/making-a-zoomable-coordinate-system-in-javafx/68945835#68945835) – jewelsea Sep 27 '22 at 22:18

2 Answers2

3

One way to add some padding around the area in which you're drawing, without changing the coordinates you use, is to add a transform to the graphics context. Basically, you first scale the drawing area to make it smaller by ratios (width-2*padding)/width and (height-2*padding)/height (so the actual drawing area is reduced by a size 2*padding in each dimension). Then translate by padding in each dimension. This looks like:

public class HelloApplication extends Application {

    @Override
    public void start(Stage stage) throws IOException {
        Canvas c = new Canvas(715,495);
        GraphicsContext gc = c.getGraphicsContext2D();
        VBox v = new VBox();
        Pane p = new Pane();
        p.getChildren().add(c);


        Button b1 = new Button("Draw Sinus at Canvas");
        b1.setOnAction(e -> drawSinus(c, gc, 10));

        v.getChildren().addAll(b1, p);
        Scene sc = new Scene(v);
        stage.setScene(sc);
        stage.setTitle("Drawing Lines - Dynamically at Runtime");
        stage.show();
    }


    private void drawSinus(Canvas c, GraphicsContext gc, double padding) {


        double height = c.getHeight();
        double width = c.getWidth();

        Affine transform = new Affine(Transform.scale((width-2*padding)/width, (height-2*padding)/height));
        transform.appendTranslation(padding, padding);
        gc.setTransform(transform);

        double multiplier = (2 * Math.PI)/width;
        double x1 = 0;
        double y1 = height/2;
        double x2 = 1;
        double y2 = 0;
        int i = 0;
        gc.setStroke(Color.BLACK);
        gc.setLineWidth(10);

        while(i < width) {
            y2 = (height/2) - ((height/2) * Math.sin(x2 * multiplier));
            gc.strokeLine(x1,y1,x2,y2);
            x1 = x2;
            y1 = y2;
            x2++;
            i++;
        }

        // reset transform; may not be necessary depending on actual use case
        gc.setTransform(new Affine());
    }

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

You need to reset the transform if you want to, e.g., clear the entire canvas, including the padded area.

James_D
  • 201,275
  • 16
  • 291
  • 322
  • In the image in the OP, you have axes drawn as well. These would need to be drawn in the transformed coordinate system too, but clearing the canvas needs to happen in the untransformed coordinates. Not sure if this is what you're looking for. – James_D Sep 27 '22 at 18:30
  • Thanks mate. This works very good. But I don't understand what the `transform.appendTranslation(padding, padding)` does. So you already defined in the line above which ratio it should apply what does this do then? And concerning thread-safety. I read about `Platform.runLater();` Which parts of my code have to be wrapped in that? – Yubi Sep 28 '22 at 17:53
  • You need both to scale the canvas, so you are drawing on a smaller portion of it, and then to shift (translate) it so `(0,0)` is in the top left corner of the portion you want to draw on. So you need to apply a translation after the scale. – James_D Sep 28 '22 at 18:04
  • *”Which parts of my code have to be wrapped in `Platform.runLater()`?”*. Anything that modifies the UI. – James_D Sep 28 '22 at 18:05
  • So the `appendTranslation(padding, padding)` just moves 0,0 to padding,padding? And the line above is for scaling x and y to make it fit in the smaller portion? – Yubi Sep 29 '22 at 07:09
  • @Yubi Yes, that’s correct. – James_D Sep 29 '22 at 10:15
2

Place the canvas a StackPane and set padding on the StackPane.

Canvas canvas = new Canvas(715,495);
StackPane stackPane = new StackPane(canvas);
stackPane.setPadding(new Insets(5));

Adjust canvas and padding sizes as needed for your requirements.

jewelsea
  • 150,031
  • 14
  • 366
  • 406
  • > Adjust canvas and padding sizes as needed for your requirements. This didn't work for me. Or am I doing something wrong? I changed It to this: `Canvas c = new Canvas(715,495); GraphicsContext gc = c.getGraphicsContext2D(); VBox v = new VBox(); StackPane p = new StackPane(c); p.setPadding(new Insets(100));` But all I got was a padding between the Stackpane and the Canvas so the line still goes over the border and the window got bigger. [Image](https://i.stack.imgur.com/XLZ1n.png) – Yubi Sep 28 '22 at 17:11
  • Ok, I think I understand. The proposed solution as-is is ok for adding padding to a canvas but not exactly in the way for your requirements. – jewelsea Sep 28 '22 at 17:15
  • yea so it's my first question here and I always have problems in writing what I mean so not your fault. thanks anyway – Yubi Sep 28 '22 at 17:50