プログラムdeタマゴ

nodamushiの著作物は、文章、画像、プログラムにかかわらず全てUnlicenseです

マウスをShape(PathIterator)の形にドラッグ(移動)させよう

今日はShape等の形に合わせてマウスを動かす話です。
マウスを複雑な形に動かしたり、ドラッグさせるにはShapeを使うのが簡単です。

あと、わざわざ記事にしたのは
[Java]PathIterator を Point2D 配列へ変換する
というNI-Lab’.sさんの古い記事が検索トップであったのですが、これが微妙に間違っ てる ていたので訂正もかねて。
まずはこんな感じでマウスを動かす為の関数が用意されていることにしておきます。

Robot rob;
{
    try {
        rob = new Robot();
    } catch (AWTException e) {
        e.printStackTrace();
        System.exit(1);
    }
    rob.setAutoDelay(30);
}

//毎回(int)xと書くのがめんどうなのでdouble型
public void move(double x,double y){
    rob.mouseMove((int)x, (int)y);
}

public void press(double x,double y){
    move(x,y);
    press();
}

public void press(){
    rob.mousePress(InputEvent.BUTTON1_DOWN_MASK);
}

public void release(double x,double y){
    move(x,y);
    release();
}

public void release(){
    rob.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
} 

ついでに、こんな内部クラスを用意しておきました。

class MousePoint{
    double x,y;
    boolean move=true;//press,release時に移動するかどうか。
    int type;//0:move 1:press 2:release
    void doWork(){
        switch(type){
        case 0://move
            move(x,y);
            break;
        case 1://press
            if(move)press(x,y);
            else press();
            break;
        case 2://release
            if(move)release(x,y);
            else release();
            break;
        }
    }
}

これで準備おk。
 
では、実際にShapeを使ってマウスをドラッグしてみましょう。
ここではLINETOとCLOSEというセグメントが出たときにドラッグするようにしたよ。

public void dragShape(Shape s) {

    //ShapeからPathIteratorを取得
    PathIterator path= s.getPathIterator(null);

    //FlatteningPathIteratorでペジェ曲線を直線の集まりに変換。
    FlatteningPathIterator p = new FlatteningPathIterator(path, 0.1);

    //マウスポイントを保持するリスト
    ArrayList<MousePoint> list = new ArrayList<MousePoint>();

    //PathIteratorから点を読み込むのに使います。定石です。 
    double[] coords = new double[6];
    int type;


    MousePoint beforepoint=null,
    lastmoveto=null;//SEG_MOVETOの最も新しい点は必ず一つ保存しておきます。

    while(!p.isDone()){
        type = p.currentSegment(coords);
        switch(type){
        case PathIterator.SEG_MOVETO:
            beforepoint =lastmoveto= new MousePoint();
            beforepoint.x =coords[0];
            beforepoint.y =coords[1];
            beforepoint.type = 0;//move
            list.add(beforepoint);
            break;
        case PathIterator.SEG_CLOSE:
            beforepoint.type =(beforepoint.type==2)?0: 1;//releaseならmoveにそうでなければpressに
            beforepoint = new MousePoint();
            beforepoint.x =lastmoveto.x;//最後にmovetoした点に移動する。
            beforepoint.y =lastmoveto.y;
            beforepoint.type = 2;//release
            list.add(beforepoint);
            break;
        case PathIterator.SEG_LINETO:
            beforepoint.type =(beforepoint.type==2)?0: 1;//releaseならmoveにそうでなければpressに
            beforepoint = new MousePoint();
            beforepoint.x =coords[0];
            beforepoint.y =coords[1];
            beforepoint.type = 2;//release
            list.add(beforepoint);
            break;
        }
        p.next();
    }
    ///////////////ここまででPathIteratorをマウス座標に変換完了

    //実際に動かす。
    for(MousePoint m:list)m.doWork();
}

問題となるのはセグメントCLOSEの時に最後のMOVETOセグメントの座標へ移動すると言うこと。これを忘れないようにしましょう。
 
 
実際にこんな感じのソースで動きをsaiに描画させて試してみましょう。

//saiにフォーカスを合わせる為にクリックしておきます。
move(400,400);
press();
release();
//////////////////////////////////////////////////
//閉じた円弧のShapeを作成
Shape r = new Arc2D.Double(600, 200, 300, 300, 0, 230, Arc2D.CHORD);
dragShape(r);


 
ちゃんとShapeの形が出てきましたね。