Java etc...

アクセスカウンタ

help RSS JavaFX の Bind で三角形の面積を求める... の続き(1)

<<   作成日時 : 2012/12/28 01:42   >>

かわいい ブログ気持玉 1 / トラックバック 0 / コメント 0

If you prefer to read this article in ENGLISH, go to [in English] page.


Pro JavaFX 2 に、Binding の例として三角形の面積を求める例があった。

3つの頂点(x1, y1), (x2, y2), (x3, y3) の座標を与えると、次のような公式に当てはめて面積が求まるというもの。

面積 = (x1*y2 + x2*y3 + x3*y1 - x1*y3 - x2*y1 - x3*y2)/2

ソースコードは、こんな感じ。


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());
  }
}

コンソールに実行結果が出力されている



これを一目見て、JavaFX を使っていながら GUI がないというのも面白いと思った。
だけど、やはり JavaFX なのだから GUI をつけてみたくなった。

そこで、おそらく、誰でも思いつくであろうアレンジだと思うけど、
マウスクリックを 3 箇所行って、それらを 3 つの頂点としてみなして面積を求めるというものを考えた。
見た目も、わかりやすくするために、クリックした箇所に Circle を表示し、Circle 間を Line で結線することしてみよう。

まず、クリックした 3 点の座標 (x1, y1), (x2, y2), (x3, y3) を、それぞれプロパティに持つ。
それらのプロパティで表現される頂点を表現する Circle の centerX と centerY にバインドする。
もちろん、頂点は 3 つなので、Circle も 3 つある。
さらに 2 つの Circle 間を結線する Line のstartX, startY, endX, endY に 2 つの Circle の centerX と centerY を、それぞれバインドする。

面積は、クリックした点 (X, Y) のピクセル単位の値のまま計算する。
そして、絶対値で求めるために When().then().otherwise() という興味深い API を使う。

ということで作ったソースがこれ。


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);
  }
}
三角形の頂点をマウスクリックして与える



これを実行すると、最初のクリックで、その箇所に点が表示される。
2回目のクリックで、その箇所に点が表示され、かつ、最初の点との間が線で結ばれる。
3回目のクリックで、その箇所に点が表示され、残りの 2 辺も表示される。
そして、標準出力にクリックした 3 箇所の座標と、その 3 点を頂点とする三角形の面積が出力される。
この時、表示される値は座標はピクセル単位、面積は平方ピクセル単位である。

4回目のクリックは、1回目のクリックがなされたものとして処理している。

しかし、これだと入力だけが GUI なので、面白みも半分。そこで、出力も GUI を使おうと思う。


(1エントリの文字数制限のがあるみたいなので) つづく

テーマ

注目テーマ 一覧


月別リンク

ブログ気持玉

クリックして気持ちを伝えよう!
ログインしてクリックすれば、自分のブログへのリンクが付きます。
→ログインへ
気持玉数 : 1
かわいい

トラックバック(0件)

タイトル (本文) ブログ名/日時

トラックバック用URL help


自分のブログにトラックバック記事作成(会員用) help

タイトル
本 文

コメント(0件)

内 容 ニックネーム/日時

コメントする help

ニックネーム
本 文
JavaFX の Bind で三角形の面積を求める... の続き(1) Java etc.../BIGLOBEウェブリブログ