The JavaFX Properties binding(1)

The JavaFX Properties binding for computing the area of triangle(1)


[In Japanese(日本語)]


As I wrote a previous article,
I found that JavaFX program using Properties' binding is in the Chapter 3, the book of "JavaFX 2 Pro".

That is the code that the triangle area can be computed by the following fomula,

Area = |(x1*y2 + x2*y3 + x3*y1 - x1*y3 - x2*y1 - x3*y2)| / 2

where the three vertices is at (x1, y1), (x2, y2), (x3, y3) on Cartesian plane.

Here is the source code, which is almost same as the code of Listing 3-6 in the JavaFX 2 Pro book.

public class TriangleAreaFluentExample {
  public static void main(String[] args) {
    IntegerProperty x1 = new SimpleIntegerProperty(0);
    IntegerProperty y1 = new SimpleIntegerProperty(0);
    IntegerProperty x2 = new SimpleIntegerProperty(0);
    IntegerProperty y2 = new SimpleIntegerProperty(0);
    IntegerProperty x3 = new SimpleIntegerProperty(0);
    IntegerProperty y3 = new SimpleIntegerProperty(0);

    final NumberBinding area =
                      x1.multiply(y2)
                        .add(x2.multiply(y3))
                        .add(x3.multiply(y1))
                        .subtract(x1.multiply(y3))
                        .subtract(x2.multiply(y1))
                        .subtract(x3.multiply(y2))
                        .divide(2.0D);

    StringExpression output = Bindings.format(
      "3点 A(%d,%d), B(%d,%d), C(%d,%d) " +
            "の三角形の面積は %3.1f",
                  x1, y1, x2, y2, x3, y3, area);

    x1.set(0); y1.set(0);
    x2.set(6); y2.set(0);
    x3.set(4); y3.set(3);
    System.out.println(output.get());

    x1.set(1); y1.set(0);
    x2.set(2); y2.set(2);
    x3.set(0); y3.set(1);
    System.out.println(output.get());

    x1.set(10); y1.set(10);
    x2.set(2); y2.set(2);
    x3.set(5); y3.set(1);
    System.out.println(output.get());
  }
}


The first time I saw the code, It is really interesting.
It's JavaFX code, but it's has no UI Controls !!

Now, I would like to add some UIs on the code.

First of all, I think it's a sort of ideas anyone can come up with,
trying to giving the three vertices by mouse clicking on the screen,
instead of by program code.

A small Circle(like a dot) is appears at the mouse clicked points, it's at most 3 points.
And also, The Line is drawn between each small circles.
Therefore, there are three Circles and Three Lines.

Some DoubleProperties introduced into the code as two of three elements of arrays.
One array is for x-coordinate and the other array is for y-coordinate of the three vertices,
which are setted the value of the SceneX and SceneY of MouseEvents raised when mouse is clicked.

To show up small Circle at the mouse clicked,
the Circle's Propertes centerX and centerY is bound to the DoubleProperties.
Also, To draw Line between two Circles,
the Line' Properties startX, startY, endX and endY is bound to the two Circle's Property centerX and centerY.

After the 3 mouse clicked occures, then the area of triangle is computed.
The code for computaion of the area is almost same as original code.
The extra code for getting the absolute value is added.
It use the When class and its then() and otherwise() methoed.
It's interesting class and methods as well.

Here is the source code.

import javafx.application.Application;
import javafx.beans.binding.NumberBinding;
import javafx.beans.binding.When;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.GroupBuilder;
import javafx.scene.Scene;
import javafx.scene.SceneBuilder;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.LineBuilder;
import javafx.scene.shape.RectangleBuilder;
import javafx.stage.Stage;

public class TriangleArea0 extends Application {
  private int clickcount = 0;
  private Circle c[] = new Circle[3];
  private Line l[] = new Line[3];
  private DoubleProperty x[] = new SimpleDoubleProperty[3];
  private DoubleProperty y[] = new SimpleDoubleProperty[3];

  private NumberBinding area;

  {
    for(int i = 0; i < 3; i++ ) {
        c[i] = new Circle();
        l[i] = new Line();
        x[i] = new SimpleDoubleProperty(0);
        y[i] = new SimpleDoubleProperty(0);
    }
  }

  public TriangleArea0() {
    initialize();

    for(int i = 0; i < 3; i++) {
      int j = (i == 2) ? 0 : i+1;

      l[i].startXProperty().bind(c[i].centerXProperty());
      l[i].startYProperty().bind(c[i].centerYProperty());
      l[i].endXProperty().bind(c[j].centerXProperty());
      l[i].endYProperty().bind(c[j].centerYProperty());

      c[i].centerXProperty().bind(x[i]);
      c[i].centerYProperty().bind(y[i]);
    }

    NumberBinding
      tmp_area = x[0].multiply(y[1])
            .add(x[1].multiply(y[2]))
            .add(x[2].multiply(y[0]))
            .subtract(x[0].multiply(y[2]))
            .subtract(x[1].multiply(y[0]))
            .subtract(x[2].multiply(y[1]))
            .divide(2.0);

    area = new When(tmp_area.lessThan(0))
              .then(tmp_area.negate())
              .otherwise(tmp_area);
  }

  private void initialize() {
    clickcount = 0;
  }

  private void print_area() {
    System.out.println(
        "A = (" + x[0].get() + ", " + y[0].get() + ")");
    System.out.println(
        "B = (" + x[1].get() + ", " + y[1].get() + ")");
    System.out.println(
        "C = (" + x[2].get() + ", " + y[2].get() + ")");
    System.out.println("Area = " + area.doubleValue());
  }

  @Override public void start(Stage stage) {
    final Group root;
    Scene scene = SceneBuilder.create()
      .width(500).height(500)
      .fill(Color.WHITE)
      .root(root = GroupBuilder.create()
        .children(RectangleBuilder.create()
          .layoutX(5).layoutY(5)
          .width(490).height(490)
          .fill(Color.LIGHTSKYBLUE)
          .onMouseClicked(new EventHandler<MouseEvent>() {
            @Override public void handle(MouseEvent e) {
              if (clickcount > 2) { initialize(); }
              for (int j = clickcount; j < 3; j++) {
                x[j].set(e.getSceneX());
                y[j].set(e.getSceneY());
              }
              clickcount++;
              if (clickcount == 3) { print_area(); }
            }
        }).build(),
        LineBuilder.create()
          .startX(5).startY(490)
          .endX(495).endY(490)
          .build(),
        LineBuilder.create()
          .startX(10).startY(5)
          .endX(10).endY(495)
          .build(),
        l[0], l[1], l[2],
        c[0], c[1], c[2]
      ).build()
    ).build();

    stage.setTitle("Triangle Area");
    stage.setScene(scene);
    stage.show();
  }

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


While the the code is running,
the first mouse click is indicated the first vertix is at the clicked potision,
and the small circle is shown at the position of the screen.
With a second click, other small circle is shown at the position,
and also the Line between the centers of 2 small circles is drawn.
With a third click, another small circle is shown at the position,
and the two other Lines are drawn.
The area of triangle is printed out the standard output.
The value of Area is expressed in terms of square pixels.

The occuring of fourth click is handling with the same way as the occuring the first click.

This code dose not output the area of triangle on the UI Screen. Next, try to output the area of triangle on the screen

(due to the limitation of the number of characters in a article) to be cotinuted ...

最近の画像付き記事