ポリライン(折れ線)やポリゴン(多角形の図形)の情報を持つ PathIterator オブジェクトを Point2D オブジェクトの配列へ変換するサンプル。
2次パラメトリック曲線(SEG_QUADTO)や3次パラメトリック曲線(SEG_CUBICTO)のようなベジェスプライン曲線を含む PathIterator は事前に FlatteningPathIterator 等で平坦化しておくこと。

サンプルソースコード(Path2Points.java)


import java.awt.*;
import java.awt.geom.*;
import java.util.*;
 
public class Path2Points {
 
  // PathIterator を Point2D へ変換するサンプル
  public static void main(String[] args) {
    
    Shape polyline = createPolyline();
    Shape polygon1 = createPolygon1();
    Shape polygon2 = createPolygon2();
    Shape polygon3 = createPolygon3();
    
    // 点列情報を出力
    System.out.println("---  polyline ---");
    printPoints(path2points(polyline.getPathIterator(null)));
    System.out.println("---  polygon1 ---");
    printPoints(path2points(polygon1.getPathIterator(null)));
    System.out.println("---  polygon2 ---");
    printPoints(path2points(polygon2.getPathIterator(null)));
    System.out.println("---  polygon3 ---");
    printPoints(path2points(polygon3.getPathIterator(null)));
  }
 
  private static void printPoints(Point2D[][] p){
    for(int i=0; i<p.length; i++){
      for(int j=0; j<p[i].length; j++){
        System.out.print("(" + p[i][j].getX() + "," + p[i][j].getY() + ")");
      }
      System.out.println();
    }
  }
    
  private static Shape createPolyline() {
 
    // 9の字
    GeneralPath p = new GeneralPath();
    p.moveTo(40, 30);
    p.lineTo(10, 30);
    p.lineTo(10, 10);
    p.lineTo(50, 10);
    p.lineTo(50, 50);
    p.lineTo(10, 50);
 
    return p;
  }
 
  private static Shape createPolygon1() {
 
    // 9の字
    Polygon p = new Polygon();
    // 外側
    p.addPoint(10, 10);
    p.addPoint(50, 10);
    p.addPoint(50, 60);
    p.addPoint(10, 60);
    p.addPoint(10, 50);
    p.addPoint(40, 50);
    p.addPoint(40, 40);
    p.addPoint(10, 40);
    p.addPoint(10, 10);
    // 内側
    p.addPoint(20, 20);
    p.addPoint(40, 20);
    p.addPoint(40, 30);
    p.addPoint(20, 30);
    p.addPoint(20, 20);
 
    return p;
  }
 
  private static Shape createPolygon2() {
 
    // 9の字
    //パスの内部を決める偶奇屈曲規則
    GeneralPath p = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
    // 外側
    p.moveTo(10, 10);
    p.lineTo(50, 10);
    p.lineTo(50, 60);
    p.lineTo(10, 60);
    p.lineTo(10, 50);
    p.lineTo(40, 50);
    p.lineTo(40, 40);
    p.lineTo(10, 40);
    p.lineTo(10, 10);
    p.closePath();
    // 内側
    p.moveTo(20, 20);
    p.lineTo(40, 20);
    p.lineTo(40, 30);
    p.lineTo(20, 30);
    p.lineTo(20, 20);
    p.closePath();
 
    return p;
  }
 
  private static Shape createPolygon3() {
 
    // 9の字
    //パスの内部を決める非ゼロ屈曲規則
    GeneralPath p = new GeneralPath(GeneralPath.WIND_NON_ZERO);
    // 外側
    p.moveTo(10, 10);
    p.lineTo(50, 10);
    p.lineTo(50, 60);
    p.lineTo(10, 60);
    p.lineTo(10, 50);
    p.lineTo(40, 50);
    p.lineTo(40, 40);
    p.lineTo(10, 40);
    p.lineTo(10, 10);
    p.closePath();
    // 内側
    p.moveTo(20, 20);
    p.lineTo(40, 20);
    p.lineTo(40, 30);
    p.lineTo(20, 30);
    p.lineTo(20, 20);
    p.closePath();
 
    return p;
  }
 
  /**
   * パスを Point2D の配列へ変換します。
   * @param pi 1つまたは複数のパスである PathIterator オブジェクト
   * @return Point2D の配列の配列
   */
  private static Point2D[][] path2points(PathIterator pi) {
 
    // Point2D の配列の配列
    ArrayList pointsList = new ArrayList();
 
    // Point2D の配列
    ArrayList currentPoints = new ArrayList();
 
    while (!pi.isDone()) {
      double coords[] = new double[6];
      int type = pi.currentSegment(coords);
      switch (type) {
      // 折れ線の場合は moveto で新しい点列の開始
      // 多角形の場合は close & moveto で新しい点列の開始
      // close の直後に lineto が出現しても問題なくなる
      // close 連続は在り得るのか?
      case PathIterator.SEG_MOVETO:
        if (currentPoints.size() > 0) {
          pointsList.add(currentPoints.toArray(new Point2D[currentPoints.size()]));
          currentPoints = new ArrayList();
        }
        // 点を追加
        currentPoints.add(new Point2D.Double(coords[0], coords[1]));
        break;
      case PathIterator.SEG_CLOSE:
        if (currentPoints.size() > 0) {
          pointsList.add(currentPoints.toArray(new Point2D[currentPoints.size()]));
          currentPoints = new ArrayList();
        }
        break;
      case PathIterator.SEG_LINETO:
        // 点を追加
        currentPoints.add(new Point2D.Double(coords[0], coords[1]));
        break;
      case PathIterator.SEG_CUBICTO:
        // サポート外
        throw new IllegalArgumentException("This method don't support PathIterator.SEG_CUBICTO.");
      case PathIterator.SEG_QUADTO:
        // サポート外
        throw new IllegalArgumentException("This method don't support PathIterator.SEG_QUADTO.");
      }
      pi.next();
    }
 
    // close されていない PathIterator の残りを処理
    if (currentPoints.size() > 0) {
      pointsList.add(currentPoints.toArray(new Point2D[currentPoints.size()]));
    }
 
    return (Point2D[][]) pointsList.toArray(new Point2D[pointsList.size()][]);
  }
 
}

出力結果


---  polyline ---
(40.0,30.0)(10.0,30.0)(10.0,10.0)(50.0,10.0)(50.0,50.0)(10.0,50.0)
---  polygon1 ---
(10.0,10.0)(50.0,10.0)(50.0,60.0)(10.0,60.0)(10.0,50.0)(40.0,50.0)(40.0,40.0)(10.0,40.0)(10.0,10.0)(20.0,20.0)(40.0,20.0)(40.0,30.0)(20.0,30.0)(20.0,20.0)
---  polygon2 ---
(10.0,10.0)(50.0,10.0)(50.0,60.0)(10.0,60.0)(10.0,50.0)(40.0,50.0)(40.0,40.0)(10.0,40.0)(10.0,10.0)
(20.0,20.0)(40.0,20.0)(40.0,30.0)(20.0,30.0)(20.0,20.0)
---  polygon3 ---
(10.0,10.0)(50.0,10.0)(50.0,60.0)(10.0,60.0)(10.0,50.0)(40.0,50.0)(40.0,40.0)(10.0,40.0)(10.0,10.0)
(20.0,20.0)(40.0,20.0)(40.0,30.0)(20.0,30.0)(20.0,20.0)

追記: 2007-07-01

多角形の場合は、close時に先頭と末尾が一致していなかったら、末尾に先頭と同じ値を追加する必要がありそう。
先頭と末尾の値が一致していないと、java.awt.* が多角形と認識してくれないから。

PathIterator.SEG_CLOSE のときに


if(!currentPoints.get(0).equals(currentPoints.get(currentPoints.size()-1))){
  currentPoints.add(currentPoints.get(0));
}

のような処理を入れておく必要あり。

追記: 2011-02-25

あと、わざわざ記事にしたのは

[Java]PathIterator を Point2D 配列へ変換する

というNI-Lab’.sさんの古い記事が検索トップであったのですが、これが微妙に間違ってるので訂正もかねて。

マウスをShape(PathIterator)の形にドラッグ(移動)させよう - プログラムdeタマゴ

おぉっと。

で、自分で見てみたけど

「なにがちがうかよくわからんかったー」

ってTwitterでつぶやいたら、ありがたいことに解説エントリを書いてもらえたので メモメモ φ(..)

createPolygon2 について、

p.lineTo(10,10);とp.closePath();およびp.lineTo(20,20);とp.closePath();

のどちらかが不要です。

実際にlineToの方を外してGrahpics2Dのdraw(Shape)で描画してみます

きちんと閉じた図が得られました。

ですが、この外した状態ではNI-Lab.さんのコードでは最後の閉じるための点を取得できません。

GeneralPathとPathIteratorの諸注意 - プログラムdeタマゴ

ふむふむ φ( ̄ー ̄ )ノ

というわけで、NI-Lab.さんのPathIterator解読部分のソースコードを修正すると以下の様な感じになります。


      case PathIterator.SEG_CLOSE:
        if (currentPoints.size() > 0) {
          Point2D.Double d = currentPoints.get(0);
          currentPoints.add(new Point2D.Double(d.x,d.y));
          //ここまでで最後にmoveToされた点の追加が終了
          pointsList.add(currentPoints.toArray(new Point2D[currentPoints.size()]));
          currentPoints = new ArrayList();
          //次にlineToが来るときの為に現在の点を追加しておきます
          currentPoints.add(new Point2D.Double(d.x,d.y));
        }
        break;

GeneralPathとPathIteratorの諸注意 - プログラムdeタマゴ

なるほ。
「次にlineToが来るときの為に現在の点を追加」というのがミソですね。
参考になりました。ありがとうございますm(_ _)m

Ref. GeneralPathとPathIteratorの諸注意 - プログラムdeタマゴ

tags: zlashdot Java Java

Posted by NI-Lab. (@nilab)