0

I am currently attempting to create a series of characters in a circle within an application of Java. Essentially, the phrase "Welcome to Java" will be placed in a circle, starting at the far right with W. As characters move down the circle, they are rotated in a fashion such that the bottoms of the letters are facing inwards, in order to form a circle.

My code seems to rotate the characters appropriately, but they will not appear anywhere other than the center of the pane. So my question is: How do you space the letters away from the center in a pane using javafx utilities? My code is below:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.layout.GridPane;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.geometry.Pos;

public class Characters extends Application{

@Override
public void start (Stage primaryStage){

    //Create the pane
    GridPane pane = new GridPane();
    pane.setPrefSize(600, 600);

    pane.setAlignment(Pos.CENTER);

    //Font class instance
    Font font = Font.font("Times New Roman", FontWeight.BOLD, FontPosture.REGULAR, 35);

    //Welcome to Java string
    String welcome = "Welcome to Java";
    double rotation = 90;
    double x = Math.cos(rotation)*100;
    double y = Math.sin(rotation)*100;

    //Loop
    for (int i = 0; i < welcome.length(); i++){
        x = Math.cos(Math.toRadians(rotation));
        y = Math.sin(Math.toRadians(rotation));
        System.out.println("Y: " + y);
        System.out.println("X: " + x);
        Text text = new Text(x, y, welcome.charAt(i)+"");
        System.out.println("Actual X" + text.getX());
        System.out.println("Actual Y" + text.getY());

        text.setFont(font);
        text.setRotate(rotation);
        pane.getChildren().add(text);
        rotation += 22.5;
    }

    //Create the scene for the application
    Scene scene = new Scene(pane, 500, 500);
    primaryStage.setTitle("Characters around circle");
    primaryStage.setScene(scene);
    //Display
    primaryStage.show();
}
public static void main (String [] args){
    launch(args);
}

}

Sammy Pajammy
  • 21
  • 2
  • 4
  • 1
    Possible duplicate of [How to write text along a Bezier Curve?](http://stackoverflow.com/questions/17300457/how-to-write-text-along-a-bezier-curve) – aw-think Jul 16 '16 at 06:19

3 Answers3

1

This will get you a little bit closer to your desired result. There are several mistakes in there. You should not use a GridPane here because its layout will interfere with your text placement and your formula is wrong too.

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class Characters extends Application {

    @Override
    public void start(Stage primaryStage) {

        // Create the pane
        // GridPane pane = new GridPane();
        Pane pane = new Pane();
        pane.setPrefSize(600, 600);

        // pane.setAlignment(Pos.CENTER);

        // Font class instance
        Font font = Font.font("Times New Roman", FontWeight.BOLD, FontPosture.REGULAR, 35);

        // Welcome to Java string
        String welcome = "Welcome to Java";
        double rotation = 90;
        double offset = pane.getPrefWidth() / 2;
        double radius = 100;
        double x = offset + Math.cos(rotation) * radius;
        double y = offset + Math.sin(rotation) * radius;

        // Loop
        for (int i = 0; i < welcome.length(); i++) {
            x = offset + Math.cos(Math.toRadians(rotation)) * radius;
            y = offset + Math.sin(Math.toRadians(rotation)) * radius;
            System.out.println("Y: " + y);
            System.out.println("X: " + x);
            Text text = new Text(x, y, welcome.charAt(i) + "");
            System.out.println("Actual X" + text.getX());
            System.out.println("Actual Y" + text.getY());

            text.setFont(font);
            text.setRotate(rotation);
            pane.getChildren().add(text);
            rotation += 22.5;
        }

        // Create the scene for the application
        // Scene scene = new Scene(pane, 500, 500);
        Scene scene = new Scene(pane);
        primaryStage.setTitle("Characters around circle");
        primaryStage.setScene(scene);
        // Display
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
mipa
  • 10,369
  • 2
  • 16
  • 35
1

GridPane will basically ignore your positioning and decide the position of it's children on it's own. In this case all Text elements are put in the center of the "cell" with column=0; row=0) of the GridPane.

I personally prefer putting the Text nodes in a Group and add the Group to the parent layout instead of placing it in a Parent with a complex layouting algorithm.

The rotation is slightly easier to achieve using a Rotate transform, since this allows you to specify the pivot point of the rotation:

@Override
public void start(Stage primaryStage) {
    //Create the pane
    GridPane pane = new GridPane();
    pane.setAlignment(Pos.CENTER);

    Group textGroup = new Group();

    //Font class instance
    Font font = Font.font("Times New Roman", FontWeight.BOLD, FontPosture.REGULAR, 35);

    //Welcome to Java string
    String welcome = "Welcome to Java";
    double rotation = 90;

    double radius = 100d;

    //Loop
    for (char c : welcome.toCharArray()) {
        // ignore whitespace, otherwise add rotated char
        if (!Character.isWhitespace(c)) {
            Text text = new Text(Character.toString(c));
            text.setFont(font);

            Rotate rotationMatrix = new Rotate(rotation, 0, radius);
            text.getTransforms().add(rotationMatrix);

            textGroup.getChildren().add(text);
        }
        rotation += 22.5;
    }

    pane.getChildren().add(textGroup);

    //Create the scene for the application
    Scene scene = new Scene(pane, 500, 500);


    primaryStage.setTitle("Characters around circle");
    primaryStage.setScene(scene);

    //Display
    primaryStage.show();
}

Result

Resulting Application Window

fabian
  • 80,457
  • 12
  • 86
  • 114
  • This is much simpler than mine, but it ignores whitespace in front or end of the string?! So the space between ending and beginning of the word (or sentence) is not distingueshed from a normal space. Looks like there is no really perfect solution?! – aw-think Jul 16 '16 at 09:27
  • @NwDx: AFAIK the OP did want the `W` to be placed at the rightmost position, i.e. with a rotation of 90°. Therefore there seems to be no reason to start the string with a whitespace char, which is why I used the `String` from the OP's code. – fabian Jul 16 '16 at 09:33
  • True, haven't cosidered that. I cannot add any degree to my solution, so this should be the "correct" way of doing it. But mine is able to use other shapes, like rectangles, triangles, and so on :-) So even if my solution not fully fits, I'll remain it. – aw-think Jul 16 '16 at 09:37
0

You can do it with a PathTransition, but there is no kerning for the Text. So the characters won't look pretty well placed, also I had to insert some whitespace. Maybe some other one will make it a bit nicer.

But as a nice add-on, if you comment the line timer.start();, then the text is rotating around the circle.

import javafx.animation.*;
import javafx.application.Application;
import javafx.collections.*;
import javafx.scene.*;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Duration;

public class BezierTextPlotter extends Application {

  private static final String CIRCLE_TEXT = "  Welcome to Java  ";

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

    final Text text = new Text(CIRCLE_TEXT);
    text.setStyle("-fx-font-size: 40px");

    Circle circle = new Circle(200, 200, 100);

    final ObservableList<Text> parts = FXCollections.observableArrayList();
    final ObservableList<PathTransition> transitions
            = FXCollections.observableArrayList();

    for (char character : text.textProperty().get().toCharArray()) {
      Text c = new Text(character + "");
      c.setEffect(text.getEffect());
      c.setStyle(text.getStyle());
      parts.add(c);
      transitions.add(createPathTransition(circle, c));
    }

    AnchorPane ap = new AnchorPane();
    ap.getChildren().addAll(parts);

    for (int i = 0; i < parts.size(); i++) {
      final Transition t = transitions.get(i);
      t.stop();
      t.jumpTo(Duration.seconds(10).multiply((i + 0.5) * 1.0 / parts.size()));

      AnimationTimer timer = new AnimationTimer() {
        int frameCounter = 0;

        @Override
        public void handle(long l) {
          frameCounter++;
          if (frameCounter == 1) {
            t.stop();
            stop();
          }
        }
      };
      timer.start();
      t.play();
    }

    stage.setTitle("Circle Text Sample");
    stage.setScene(new Scene(ap, 400, 400, Color.ALICEBLUE));
    stage.show();
  }

  private PathTransition createPathTransition(Shape shape, Text text) {
    final PathTransition trans
            = new PathTransition(Duration.seconds(10), shape, text);
    trans.setAutoReverse(false);
    trans.setCycleCount(PathTransition.INDEFINITE);
    trans.setOrientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
    trans.setInterpolator(Interpolator.LINEAR);

    return trans;
  }

  /**
   * @param args the command line arguments
   */
  public static void main(String[] args) {
    launch(args);
  }
}

enter image description here

And with the line timer.start() commented:

enter image description here

aw-think
  • 4,723
  • 2
  • 21
  • 42