kb84tkhrのブログ

何を書こうか考え中です あ、あと組織とは関係ないってやつです 個人的なやつ

CGL_3_C: Polygon-Point Containment (続き4)

でもいったんいけるところまでこのままいってみよう
その後で考え直す

いけたので考え直す
たぶん解説にはもっとシンプルなのが載っていると見た

アルゴリズムの見直しもしたいけど、テストケースも見直してみたい気がしている
ただ、テストケースの見直しは考えてみるとなかなか難しい
追加ならいくらやってもいいけど
見直して整理したりしたときこれまでチェックできていたものが
テスト変更後も正しくチェックできているということを保証する方法って
あるのかな

アルゴリズムによりそうなところもあるしな
そういうのがテストケースに入るのはいいのか悪いのか
今回、たまたまx軸に平行な半直線を選んだから特殊ケースになったという
項目があってあとで付け足した
じゃあy軸に平行な半直線を選んだときに特殊になるケースは
テストケースから抜けたままにしておいていいのか
的な

そういう細々とした特殊ケースが出ないようなアルゴリズムを見つけられれば
いちばんいいんだけど

たとえば、今回半直線を使ったけど、別にこれは半直線に限る必要は
なくって、折れ線だっていいはず
面倒な特殊ケースにぶつかったら線を少し斜めにするとかずらすとか

逆に頂点の方をほんの少しずらすとか
こっちはほんの少しでもずらした結果答えが変わってくる可能性があるからダメかな

線分をひとつずつ取って半直線と交わる、ってやるよりも
点が移動していってところどころで半直線を横切る、っていうイメージのほうが
特殊ケースの考慮が減るかもな
半直線上にいる間はまだ横切ってない、とする

ONLINEの判定は先に済ませておいたほうがすっきりするかもしれない
効率上どうなのかはよくわからない

ちょっと書けそうな気がしてきた

    def contains(self, p: Point) -> Containment:
        def area(p1: Point) -> int:
            if p1.y - p.y < -EPS:
                return - 1
            elif p1.y - p.y > EPS:
                return 1
            else:
                return 0

        ps = Segment(p, Point(100000.0, p.y))

        # 辺の上にあればONLINEを返す
        for s in self.sides():
            if p.location(s) == PointLocation.ON_SEGMENT:
                # print("online")
                return Containment.ONLINE

        # pとy座標が異なる頂点から開始
        for k0, prev_p in enumerate(self.vertices):
            if area(prev_p) != 0:
                break
        prev_a = area(prev_p)

        count = 0
        k = 1
        while k <= self.n:
            # pとy座標が異なる頂点まで進む
            while True:
                k1 = (k + k0) % self.n
                cur_p = self.vertices[k1]
                cur_a = area(cur_p)
                if cur_a != 0:
                    break
                k += 1
                prev_p = cur_p

            if prev_a != cur_a and \
                Segment(prev_p, cur_p).intersects(s):
                count += 1
            prev_p = cur_p
            prev_a = cur_a
            k += 1

        return Containment.OUTSIDE if count % 2 == 0 \
            else Containment.INSIDE

よけい長くなった・・・
やってることはわかりやすくなった気がする(しなくもない)んだけど・・・