シェイプのストローク

このエントリーは、2013 年の JavaFX Advent Calendar の 4 日目のエントリーです。

昨日はるーつにゃんの JavaFXでめくるエフェクト!!

明日は sipadan2003 さんです。

JavaFX でシェイプを扱っていると、ストロークに関連したプロパティがいろいろあるのに気がつきます。

色や太さはすぐに分かりますけど、それ以外はなじみがないものが多いのではないでしょうか。Illustrator とか使っている人であれば分かるとは思いますけど。

ということで、ここで少しまとめてみましょう。

ソースは gist にあげました。

まずは、こんなパスを描いておきましょう。

    private Path drawPath() {
        Path path = new Path(
                new MoveTo(40.0, 40.0),
                new LineTo(140.0, 40.0),
                new LineTo(40.0, 80.0)
        );

        return path;
    }

カタカナのフのような形です。このパスについては特に難しいところはないはずです。

では、プロパティをいろいろと設定していきましょう。まずは、線色と線の太さから。

  • Shape#setStrokeWidth(double) 線の太さ
  • Shape#setStroke(Paint) 線の色

これは簡単ですね。setStrokeWidth メソッドは引数が double ですが、もちろん正の値だけです。

setStroke メソッドは線色を設定します。線色の方は Paint クラスなので、グラデーションもできます。

ちなみに、デフォルトでは太さは 1、色は Color.BLACK です。

        // デフォルト (線幅: 1, 線色: 黒)
        Path path = drawPath();
        parent.add(path, 0, 0);
        
        // 線幅: 10
        path = drawPath();
        path.setStrokeWidth(10.0);
        parent.add(path, 1, 0);

        // 線幅: 10, 線色: 赤
        path = drawPath();
        path.setStrokeWidth(10.0);
        path.setStroke(Color.RED);
        parent.add(path, 2, 0);

この 3 種類を実行すると次のようになります。

さて、これからがよく分からなくなるところです。

まず、setStrokeLineJoin メソッドです。ストロークのラインがジョインするメソッドです。何のこっちゃという感じですが、角の部分をどのようにするかを指定するためのメソッドです。

setStrokeLineJoin メソッドの引数は enum の StrokeLineJoin です。この enum は 3 つの値をとります。

MITER はマイター接続は線をそのまま延ばしていって交わったところまで伸ばす接続です。ようするに、とんがっている接続ですね。これがデフォルトになります。

ROUND (ラウンド接続) は MITER とは逆に接続部が丸くなっている接続方法です。

BEVEL (ベベル接続) は直線の角と角を結んだような接続です。

これらの接続は線が細い時は目立たないのですが、太いと目立ちます。太い線が集まっていたりすると、MITER だと線がはみ出てしまうこともあるので、ROUND か BEVEL にする方がいいと思います。

ということで、ここでは太さ 10 の線を引いてみます。

    private Path drawThickPath() {
        Path path = drawPath();
        path.setFill(Color.PINK);
        path.setStroke(Color.BLACK);

        // 線の太さを10にする
        path.setStrokeWidth(20.0);

        return path;
    }

で、3 種類の接続を試してみましょう。分かりやすいように細い白い線を一緒に描画します。

        // 角のデフォルト (マイター接合)
        Group group = new Group();
        path = drawThickPath();
        path.setStrokeLineJoin(StrokeLineJoin.MITER);

        path = drawPath();
        path.setStroke(Color.WHITE);
        group.getChildren().add(group);
        parent.add(group, 0, 2);

        // 角を丸くする (ラウンド接合)
        group = new Group();
        path = drawThickPath();
        path.setStrokeLineJoin(StrokeLineJoin.ROUND);

        path = drawPath();
        path.setStroke(Color.WHITE);
        group.getChildren().add(group);
        parent.add(group, 1, 2);

        // 角を削る (ベベル結合)
        group = new Group();
        path = drawThickPath();
        path.setStrokeLineJoin(StrokeLineJoin.BEVEL);

        path = drawPath();
        path.setStroke(Color.WHITE);
        group.getChildren().add(group);
        parent.add(group, 2, 2);

で、実行するとこのようになります。

つづいて、線端です。線端も 3 種類あります。

線端は setStrokeLineCap メソッドで設定します。引数は、こちらも enum の StrokeLineCap です。

  • SQUARE
  • ROUND
  • BUTT

SQUARE はもともとの線の線端から線の太さの分だけ四角が飛び出ているような感じです。ROUND は同じように線端が円になっています。

最後の BUTT は端がブチッと切れてしまった感じの線端になります。

これも試してみましょう。

        // 線端のデフォルト (スクエア線端)
        group = new Group();
        path = drawThickPath();
        path.setStrokeLineCap(StrokeLineCap.SQUARE);
        group.getChildren().add(path);

        path = drawPath();
        path.setStroke(Color.WHITE);
        group.getChildren().add(path);
        parent.add(group, 0, 4);

        // 線端を丸くする (ラウンド線端)
        group = new Group();
        path = drawThickPath();
        path.setStrokeLineCap(StrokeLineCap.ROUND);
        group.getChildren().add(path);
        
        path = drawPath();
        path.setStroke(Color.WHITE);
        group.getChildren().add(path);
        parent.add(group, 1, 4);

        // 線端を四角くする (BUTT線端)
        group = new Group();
        path = drawThickPath();
        path.setStrokeLineCap(StrokeLineCap.BUTT);
        group.getChildren().add(path);

        path = drawPath();
        path.setStroke(Color.WHITE);
        group.getChildren().add(path);
        parent.add(group, 2, 4);

ここでも分かりやすいように、太い線の上に線幅1の白い線を描画してあります。

最後は点線です。

点線はちょっと分かりにくいんですよね。

点線にするには、getStrokeDashArray メソッドを使います。あれっ、get と思いますよね。設定するのに、get を使うというと、JavaFX では ObservableList を返すことが多いのです。

ここでも、getStrokeDashArray メソッドでは ObservableList オブジェクトが帰ります。この ObservableList に要素を入れているいくと点線になるのです。

たとえば、10.0 の ObservableList だとします。この場合、線が 10 ピクセル、空白が 10 ピクセルの点線になります。

10.0 と 5.0 が要素の場合、線が 10 ピクセル、空白が 5.0 ピクセルになります。

10, 2, 5, 2 が要素の場合、線が 10、空白 2 線が 5、空白 2 の続きます。つまり、一点鎖線になるわけです。

        // 点線 (線とスペースの長さが同じ)
        path = drawPath();
        path.getStrokeDashArray().addAll(10.0);
        path.setStrokeWidth(5.0);
        parent.add(path, 0, 6);

        // 点線 (線とスペースの長さが異なる)
        path = drawPath();
        path.getStrokeDashArray().addAll(10.0, 5.0);
        path.setStrokeWidth(5.0);
        parent.add(path, 1, 6);

        // 一点鎖線
        path = drawPath();
        path.getStrokeDashArray().addAll(10.0, 2.0, 5.0, 2.0);
        path.setStrokeWidth(5.0);
        parent.add(path, 2, 6);

すると、このようになります。

要素数が偶数だと線の長さから始まって、空白の長さで終わるので分かりやすいですけど、偶数で活けないわけではありません。ただし、奇数だとちょっと分かりにくいのです。

たとえば、10, 2, 5 だと、線 10、空白 2、線 5、空白 10、線 2、空白 5 という繰り返しになります。というように、ちょっと分かりにくいので、要素が 1 つにするか、偶数という方が分かりやすいと思います。

ということで、ちょっと地味なエントリーでした。

最後にソース全体です。


gist7761966