2007年5月8日火曜日

点がパスの内部にあるか判定

任意の点から無限遠方に半直線を延ばした場合、
点がパスの内部なら奇数回、パスの外部なら偶数回パスと交差することを
利用。パスの時計回り・反時計回りは影響しない。

かなり古いコードなので、今ならもっと整理できるところがありそう。
#module cross
#deffunc setline int p1, int p2, int p3, int p4
    x1 = p1 : y1 = p2
    x2 = p3 : y2 = p4

    type1 = x1 != x2
    if type1 {
        // 傾きあり
        a1 = double(y2 - y1)/(x2 - x1)
        b1 = double(x2 * y1 - x1 * y2)/(x2 - x1)
    }
    return

#deffunc serchline int x3, int y3, int x4, int y4
    result = 0
    type2 = x3 != x4
    if type2 {
        a2 = double(y4 - y3)/(x4 - x3)
        b2 = double(x4 * y3 - x3 * y4)/(x4 - x3)
    }

    if (type1 == 0)&(type2 == 0) {
        // 共にY軸に並行
        result = 0
    }
    if (type1 == 0)&(type2 == 1){
        // OK
        CrossX = int(x1)
        CrossY = int(a2 * x1 + b2)
        result = ((CrossY - y1)*(CrossY - y2)<=0) & ((CrossX - x3)*(CrossX - x4)<=0) & ((CrossY - y3)*(CrossY - y4)<=0)
    }
    if (type1 == 1)&(type2 == 0) {
        CrossX = int(x3)
        CrossY = int(a4 * x3 + b4)
        result = ((CrossX - x1)*(CrossX - x2)<=0) & ((CrossY - y1)*(CrossY - y2)<=0) & ((CrossX - x3)*(CrossX - x4)<=0)
    }
    if (type1 == 1)&(type2 == 1){
        if a1 == a2 {
            result = 0
        } else {
            CrossX = -int((b1 - b2)/(a1 - a2))
            CrossY =  int((a1 * b2 - a2 * b1)/(a1 - a2))
            result = ((CrossX - x1)*(CrossX - x2)<=0) & ((CrossY - y1)*(CrossY - y2)<=0) & ((CrossX - x3)*(CrossX - x4)<=0) & ((CrossY - y3)*(CrossY - y4)<=0)
        }
    }
    return result

#defcfunc GetCrossX
    return CrossX

#defcfunc GetCrossY
    return CrossY
#global

#module path
#deffunc renew
    index = 0
    return

#deffunc addpoint int p1, int p2
    x(index) = p1 : y(index) = p2

    if index >= 2 {
        // すでにあるパスと交差していないか?
        setline x(index - 1), y(index - 1), x(index), y(index)
        l = -1
        repeat index - 2
            serchline x(cnt), y(cnt), x(cnt + 1), y(cnt + 1)
            if stat : l = cnt : break
        loop

        if l>=0 {
            // 交点を新たなパスにする
            x(index) = GetCrossX()      // int(NewX)
            y(index) = GetCrossY()      // int(NewY)
            // No.0~lまでを削除(l+1個点が消えることになる)
            index -= l
            repeat index
                x(cnt) = x(cnt + l + 1)
                y(cnt) = y(cnt + l + 1)
            loop
            return 1    // パスが完結しました!
        }
    }
    index++
    return 0

#deffunc drawpoint int p1
    repeat index
        circle x(cnt)-p1, y(cnt)-p1, x(cnt)+p1, y(cnt)+p1
    loop
    return

#define global drawedge(%1=-1%2=0%3=0) _drawedge %1%2%3
#deffunc _drawedge int p1, int p2, int p3
    if index < 2 : return
    pos x(0), y(0)
    repeat index - 11
        line x(cnt), y(cnt)
    loop
    if p3{
        if p1 >= 0 : line p1, p2
    } else {
        line x(0), y(0)
    }
    return

#defcfunc inner int p1, int p2
    ; 点と同じYだと異常動作
    if index <= 2 : return 0
    i = 0
    repeat index ; 右に伸ばす
        EdgeBack  = cnt                 // ひとつ前の点
        EdgeStart = (cnt + 1) \ index   // この点は含む
        EdgeEnd   = (cnt + 2) \ index   // この点は含まない
        if (x(EdgeStart)<p1)&(x(EdgeEnd)<=p1) : continue
        if (y(EdgeStart)<p2)&(y(EdgeEnd)<=p2) : continue
        if (y(EdgeStart)>p2)&(y(EdgeEnd)>=p2) : continue

        if y(EdgeStart) == y(EdgeEnd) {
            if x(EdgeStart) > p1 : i++
        }else{
            if (x(EdgeEnd) + (x(EdgeStart) - x(EdgeEnd))*(p2 - y(EdgeEnd))/(y(EdgeStart) - y(EdgeEnd))) >= p1 {
                i++
                if (y(EdgeStart) == p2)&(y(EdgeStart) - y(EdgeEnd))*(y(EdgeStart) - y(EdgeBack)) > 0 : i++
            }
        }
    loop
    return i \ 2

#defcfunc lastx
    if index : return x(index-1)
    return 0

#defcfunc lasty
    if index : return y(index-1)
    return 0

#global

    screen 0384288
    renew

*main
    wait 4
    getkey MouseLeft, 1
    if MouseLeft & Click {
        R = sqrt((mousex-lastx())*(mousex-lastx()) + (mousey-lasty())*(mousey-lasty()))
        if R > 15 : addpoint mousexmousey
        if stat == 1 : Click = 0        // パス完結
    } else {
        Click = 0
    }

    stick Key
    if Key & 256 {
        Click = 1
        renew
        addpoint mousexmousey
    }

    redraw 0
    color 255255255 : boxf
    color 064128  : drawedge mousexmousey, Click

    repeat ginfo_winx/48
        x = cnt*48 + 24
        repeat ginfo_winy/48
            y = cnt*48 + 24
            color 00255
            if inner(x, y) : color 2551280
            circle x-3, y-3, x+3, y+3
        loop
    loop
    redraw 1

    if inner(mousexmousey) {
        title "マウスカーソルはパスの内部にあります"
    } else {
        title "マウスカーソルはパスの外部にあります"
    }
    goto *main

0 件のコメント: