JavaFXでドラゴン曲線
パッケージJava製品開発担当の大です。
あけましておめでとうございます。
昨年にひきつづき、今年もブログトップバッターです。昨年のプログラム書初めは、2011という数字にちなんで素数判定でした。今年は辰年ということで、ドラゴン曲線を描いてみます。
プログラム
今回はJavaFXで描いてみました。
JavaFX、昨年末にリリースされたJavaSE 7u2から、ついにOracle JDK同梱になりましたね。
JavaSE 8ではJavaFX 3.0が標準に組み込まれ、Swing/AWTを置き換えていくそうなので、いまから期待しています。
※ 以下のコードは、Windows上のJavaFX 2.0.2で動作確認しています。JavaFX、とくにFXMLまわりの仕様は、まだはっきりしない(ドキュメント化されていない)ところも多く、今後動作が変更されるかもしれませんので、環境やバージョンによって動かない場合はご容赦ください。。。
Main.java:
package tekito;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
Application.launch(Main.class, args);
}
@Override
public void start(Stage primaryStage) throws IOException {
primaryStage.setTitle("ドラゴン曲線");
Parent root = FXMLLoader.load(getClass().getResource("Dragon.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Dragon.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?language javascript?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.shape.*?>
<VBox xmlns:fx="http://javafx.com/fxml" style="-fx-padding: 10;-fx-spacing: 10;">
<children>
<HBox>
<children>
<TextField fx:id="iteration" prefColumnCount="2" text="10" style="-fx-text-alignment: right;" />
<Label text="次のドラゴン曲線を描画します。枠内でマウスをドラッグしてください。" style="-fx-font-size: 15;" />
<Button text="すべて消去!" onAction="canvas.children.clear();" />
</children>
</HBox>
<Pane fx:id="canvas" prefWidth="600" prefHeight="600"
style="-fx-background-color: darkseagreen;-fx-background-radius: 10;" />
<fx:define>
<Line fx:id="guide" />
</fx:define>
<fx:script source="Turtle.js" />
<fx:script><![CDATA[
importPackage(Packages.javafx.scene.shape);
// ドラゴン曲線描画
Turtle.prototype.drawDragonCurve = function(len, iter, odd) {
if (iter < 1) {
this.forward(len);
return;
}
var angle = odd ? 45 : -45;
len = Math.sqrt(2) * len / 2;
this.turnRight(angle);
this.drawDragonCurve(len, iter - 1, true);
this.turnLeft(angle * 2);
this.drawDragonCurve(len, iter - 1, false);
this.turnRight(angle);
}
// マウスボタンが押された
canvas.onMousePressed = function(event) {
guide.startX = guide.endX = event.x;
guide.startY = guide.endY = event.y;
canvas.children.add(guide);
}
// ドラッグ中
canvas.onMouseDragged = function(event) {
guide.endX = event.x;
guide.endY = event.y;
}
// マウスボタンが離された
canvas.onMouseReleased = function(event) {
canvas.children.remove(guide);
var xlen = event.x - guide.startX;
var ylen = event.y - guide.startY;
var len = Math.sqrt(xlen * xlen + ylen * ylen);
var rad = Math.atan2(ylen, xlen);
var turtle = new Turtle(guide.startX, guide.startY, rad);
turtle.path.clip = new Rectangle(0, 0, canvas.width, canvas.height);
canvas.children.add(turtle.path);
turtle.drawDragonCurve(len, iteration.text, true);
}
]]></fx:script>
</children>
</VBox>
Turtle.js:
importPackage(Packages.javafx.scene.shape);
// タートル
function Turtle(x, y, direction) {
this.x = x;
this.y = y;
this.direction = direction;
this.isPenDown = true;
this.path = new Path();
this.path.elements.add(new MoveTo(x, y));
};
// ペンを下ろす
Turtle.prototype.penDown = function() {
this.isPenDown = true;
}
// ペンを上げる
Turtle.prototype.penUp = function() {
this.isPenDown = false;
}
// 右回転
Turtle.prototype.turnRight = function(angle) {
this.direction += angle * (Math.PI / 180.0);
}
// 左回転
Turtle.prototype.turnLeft = function(angle) {
this.turnRight(-angle);
}
// 前進
Turtle.prototype.forward = function(len) {
this.x += Math.cos(this.direction) * len;
this.y += Math.sin(this.direction) * len;
if (this.isPenDown) {
this.path.elements.add(new LineTo(this.x, this.y));
} else {
this.path.elements.add(new MoveTo(this.x, this.y));
}
}
// 後退
Turtle.prototype.back = function(len) {
this.forward(-len);
}
実行
マウスをドラッグすると、
ドラッグの開始位置から終了位置まで、ドラゴン曲線を描画します。
これだけです。
ほんとは、直線から順にヌルヌルと次数を高めていくアニメーションにしようと思ったのですが、JavaFX 2.0ではモーフィングが今のところサポートされていないようなので、あきらめました。
というわけで、本年もHOSをよろしくお願いします。
※ 2012/01/06 コード中で古いAPIを使用していた箇所があったので修正しました。

