0

I want draw and fill a path made up of 2 arcs and 2 lines with a specific color. I need to use the Canvas from JavaFX, because there is more i need to draw. The problem is, that the path which I created is not even drawn nor filled. What I want is this

enter image description here

but my code produces this

enter image description here

As you may notice this arc is thinner on the left and on the right side than it is in the middle. Just using a simple arc with a ceratin stroke width is unfortuneatly not an option for me.

This is my code where the commented part produces the second image. I already tried to use the draw path facilities and to fill it, but it is not working

import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.scene.shape.ArcType;
import javafx.stage.Stage;

public final class CanvasTest extends Application {

    private static final int WIDTH = 400;
    private static final int HEIGHT = 300;
    private static final int RADIUS = 250;

    private static final int BRICK_HEIGHT = 15;
    private static final int RADIUSHALF = RADIUS / 2;
    private static GraphicsContext gc;

    @Override
    public void start(Stage stage) {
        final Group root = new Group();
        final Scene scene = new Scene(root, WIDTH, HEIGHT);
        final Canvas can = new Canvas(WIDTH, HEIGHT);
        gc = can.getGraphicsContext2D();

        gc.setFill(Color.BLACK);
        gc.fillRect(0, 0, WIDTH, HEIGHT);

        drawArc(WIDTH / 2, HEIGHT / 2);

        root.getChildren().add(can);
        stage.setScene(scene);
        stage.show();
    }

    private void drawArc(final int posX, final int posY) {
        gc.setStroke(Color.WHITE);
        gc.setLineWidth(1);
        gc.setFill(Color.WHITE);

        final double newRadius = RADIUSHALF - BRICK_HEIGHT;
        final double yOffsetLowerArc = Math.cos(Math.toRadians(45)) * newRadius;
        final double xOffsetLowerArc = Math.sin(Math.toRadians(45)) * newRadius;
        final double newAngleLowerArc = Math.toDegrees(Math.atan2(xOffsetLowerArc, yOffsetLowerArc + BRICK_HEIGHT));
        final double xOffsetUpperArc = Math.cos(Math.toRadians(45)) * RADIUSHALF;
        final double yOffsetUpperArc = Math.sin(Math.toRadians(45)) * RADIUSHALF;
        final double yOffsetNewLowerArc = Math.cos(Math.toRadians(newAngleLowerArc)) * RADIUSHALF;
        final double xOffsetNewLowerArc = Math.sin(Math.toRadians(newAngleLowerArc)) * RADIUSHALF;

        // this code produces the un-filled custom arc
        // gc.strokeArc(posX - RADIUSHALF, posY - RADIUSHALF, RADIUS, RADIUS, 45, 90, ArcType.OPEN);
        // gc.strokeArc(posX - RADIUSHALF, posY - RADIUSHALF + BRICK_HEIGHT, RADIUS, RADIUS, 90 - newAngleLowerArc, 2 * newAngleLowerArc, ArcType.OPEN);
        // gc.strokeLine(posX - xOffsetNewLowerArc, posY + BRICK_HEIGHT - yOffsetNewLowerArc, posX - xOffsetUpperArc, posY - yOffsetUpperArc);
        // gc.strokeLine(posX + xOffsetNewLowerArc, posY + BRICK_HEIGHT - yOffsetNewLowerArc, posX + xOffsetUpperArc, posY - yOffsetUpperArc);

        gc.beginPath();
        gc.arc(posX - RADIUSHALF, posY - RADIUSHALF, RADIUS, RADIUS, 45, 90/*, ArcType.OPEN*/);
        gc.arc(posX - RADIUSHALF, posY - RADIUSHALF + BRICK_HEIGHT, RADIUS, RADIUS, 90 - newAngleLowerArc, 2 * newAngleLowerArc/*, ArcType.OPEN*/);
        gc.moveTo(posX + xOffsetNewLowerArc, posY + BRICK_HEIGHT - yOffsetNewLowerArc);
        gc.lineTo(posX + xOffsetUpperArc, posY - yOffsetUpperArc);
        gc.moveTo(posX - xOffsetNewLowerArc, posY + BRICK_HEIGHT - yOffsetNewLowerArc);
        gc.lineTo(posX - xOffsetUpperArc, posY - yOffsetUpperArc);
        gc.closePath();
        gc.fill();
    }

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

Maybe I also misunderstood the concept of paths in JavaFX, but for me my approch is sound :-) never the less it is not working. (I also read this "Working with Canvas")

Westranger
  • 1,308
  • 19
  • 29
  • There are some scene graph based solutions in the answers to: [Draw a semi ring - JavaFX](http://stackoverflow.com/questions/11719005/draw-a-semi-ring-javafx). Those may not assist you too much as you are looking for a Canvas based solution rather than a scene graph based solution. – jewelsea Oct 27 '15 at 17:45
  • Haven't had time to play with this enough for a complete answer, but you want your path to trace the outline of the shape, as though you were not taking the "pen" off the "paper". So you should have something like `gc.beginPath()` `gc.arc(...)` `gc.lineTo(...)` `gc.arc(...)` `gc.lineTo(...)` `gc.closePath()` in that order. Then `gc.fill()` should work as you want. – James_D Oct 27 '15 at 20:08

1 Answers1

2

There are a few issues with your code. The parameters of GraphicsContext.arc are not the same as GraphicsContext.strokeArc.

strokeArc(double x,
          double y,
          double w,
          double h,
          double startAngle,
          double arcExtent,
          ArcType closure)
arc(double centerX,
    double centerY,
    double radiusX,
    double radiusY,
    double startAngle,
    double length)

The difference is: strokeArc draws the arc that is part of the oval in the rectangle positioned at point (x, y) with given width and height. arc however constructs a path that is part of the oval centered at (centerX, centerY) with radii radiusX and radiusY. To get the appropriate parameters for arc you can use the following formulae:

centerX = (x+w)/2
centerY = (y+h)/2
radiusX = w/2
radiusY = h/2

Also you don't need to call moveTo to constuct the path. 2 arcs are sufficient to construct the path. Also make sure to use arcs that go in different directions (one clockwise one counter-clockwise) to connect the closest ends of the arcs:

gc.beginPath();
gc.arc(posX, posY, RADIUSHALF, RADIUSHALF, 45, 90);

// next arc in opposite direction
gc.arc(posX, posY + BRICK_HEIGHT, RADIUSHALF, RADIUSHALF, 90 + newAngleLowerArc, -2 * newAngleLowerArc);
gc.closePath();

gc.fill();
fabian
  • 80,457
  • 12
  • 86
  • 114
  • perfect it works. I missed looking at the parameters, the compiler did not complain after removing the "stroke" and the last parameter so I thought it would be ok – Westranger Oct 28 '15 at 07:17