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を使用していた箇所があったので修正しました。