オブジェクトが接続されたパスの周りをCWまたはCCWで移動しているかどうかを確認するにはどうすればよいですか?


19

ギザギザの形状があるとしましょう:

shape0

そして、その輪郭に沿って移動する2つのクリーチャー。

次に、角を引き出して形状を完全に滑らかにします。

これを取得します。

なめらか

オレンジがCWと緑のCCWを動かしているのは簡単にわかります。形状を滑らかにすることなく、どの方向に移動しているかを知るにはどうすればよいですか?

新しいイメージ

ここに画像の説明を入力してください


ここに私の2セントは次のとおりです。i.imgur.com/zrBdw.png
ケンドール・フレイ

回答:


27

無限に線を引き、シェイプを横切る回数(偶数または奇数)をカウントします。クリーチャーが存在するセグメントはカウントしません。次に、クリーチャーがその行の左または右に移動しているかどうかを確認します。

例

この例では、形状を2回(非常に均等に)交差させて、左に進みます。結果はこの表からすぐに得られます:

   # Crosses | even  | odd
  Direction  |       |
-------------+-------+------
    left     | CCW   |  CW
    right    |  CW   | CCW

擬似コードで:

x, y = position of creature
vx, vy = direction of creature movement
crossings = 0
for each x1, y1, x2, y2 in shape segments:
    if (x1 < x and x <= x2) or (x2 < x and x <= x1):
        if y - y1 > (x - x1) * (y2 - y1) / (x2 - x1):
            ++crossings
if (crossings & 1) == (vx < 0):
    return CW
else
    return CCW

移動するラインクリーチャーを含めますか?
Ali1S232

@Gajoo:いいえ。したがって、6行目で> =の代わりに>を使用します。これについてメモを追加します。ただし、行を含めて、テーブルの内容を逆にすることができることに注意してください。
サムホセバー

1
私は、この方法に基づいて答えを出すことと、私が与えた答えの間で投げ合いました。ここに両方の​​アプローチがあることを嬉しく思います。これは概念的には単純で非常にエレガントですが、線分交差テストを実行する必要があり、堅牢にするためには注意が必要です。
トレバーパウエル

@TrevorPowell本当。最も遠いエッジを見つけることは混乱を招く可能性があります。最初にエッジの最も遠い頂点に基づいてチェックし、次にシェイプの中心から2つのエッジの中心(頂点を共有する2つ)を通って線を描き、線の1つが途中で別のエッジと交差するかどうかを確認しましたこれらのエッジの1つを越えた後の無限大。大丈夫だった
-wolfdawn

5

シェイプデータ構造から取得できる情報によって異なりますが、シェイプのアウトラインに沿ってCWを移動するクリーチャーは常に右側にシェイプの内側を持ち、CCWを移動するクリーチャーはシェイプの内側に移動しますその左。


はるかに簡単な解決策であり、最初に考えたことです。
Amplify91

どの方向が形状の内側にあるかをどのようにして知るのですか つまり、図形の内側の端に沿って移動するのは、左側または右側のいずれかです。どのようにしてそれがどのようにわかるのですか?
Ali1S232

非常にエレガントなソリューションですが、一般的にそうではありません。テーブルの上で平らにされて二次元形状を作るドーナツを想像してください。このシェイプの端に沿って歩き、シェイプの内側を左側に保ち、開始位置に応じて時計回りまたは反時計回りのラップを作成できます。
マルクストーマス

4
  1. 形状の中心点を計算します。
  2. 図形の最も遠い端を中心から選びます。
    • (最も遠いエッジを選択すると、シェイプの反転した凹面部分から開始しないようになります。これにより、シェイプ全体で時計回り/反時計回りの決定が逆になります)
  3. そのエッジに沿ったどの方向が時計回りであるかを決定する
    • (これを簡単に実装するには、形状の中心から選択したエッジの各端までの角度を比較します。角度の差の符号は、時計回りと反時計回りを示します)
  4. ステップ2で選択したエッジから開始して、形状のすべてのエッジを反復処理し、エッジのリストを作成します。エッジごとに、2つの頂点を時計回りの順序で保存します。
    • (形状が時間の経過とともに変化しない場合、このエッジリストを後で使用するために保存できるため、フレームごとに最初の4つのステップを実行する必要はありません)
    • (既にエッジリストがある場合があります。その場合、この同じ時計回りの頂点順序を同じリストに保存できます。)
  5. エンティティが時計回りに移動しているか反時計回りに移動しているかを判断するには:
    • エンティティが移動しているエッジを特定します。
    • エンティティの移動方向のドット積を、ステップ4で決定したエッジの時計回りの開始頂点から終了頂点までのベクトルに対して行います。
    • 内積の結果がゼロより大きい値である場合、エンティティは時計回りに移動しています。ゼロ未満は反時計回りを意味します。

非常に賢い答え
-wolfdawn

少し質問がありますか?あなたの答えに基づいて、彼の形状の頂点が左端のCWWから番号付けされていると仮定すると、6-> 7または9-> 10(ゼロベース)から時計回りに移動しているかどうかはどうすればわかりますか?
Ali1S232

最も遠い端から始め、その端で時計回りの方向を見つけます。エッジAが頂点「a」から「b」まで時計回りにあるとします。次に、エッジB(頂点 'b'と 'c'がある)に移動すると、Bが時計回りに 'b'から 'c'にあることがわかります。同様に、エッジCは時計回りに「c」から「d」になります。1つのエッジから正しい時計回りの方向がわかったら(ステップ1〜3)、形状のエッジの周りをその時計回りの方向に続けることで、実際にエッジの位置を確認せずに、すべてのエッジの正しい「時計回り」方向を推測できます。凹面は大丈夫です。
トレバーパウエル

エッジAが時計回りに「a」から「b」になっているのか、それとも時計回りに「b」から「a」になっているのかをどのように確認できますか?その部分を見逃したと思います。
Ali1S232

@Gajooこれはステップ3の括弧で囲まれたポイントです。おそらく、それはプロセス全体の重要なステップなので、括弧で囲まれるべきではありません。
トレバーパウエル

2

多角形がどの方向に定義されているか、頂点がどのようにそれを囲むかを知る必要があります。

これがわからない場合は、ポリゴンの面積を計算することで解決できます。

float Polygon::area() {
    float result = 0.0f;

    for(int a = 0; a < vertexCount; a ++) {
        int b = (a+1) % vertexCount;
        result += vertices[a].x * vertices[b].y;
        result -= vertices[a].y * vertices[b].x;
    }

    return result * .5f;
}

看板結果(正または負)は、時計回りか反時計回りかを示します。座標系に依存しているため、これを試して、どの方向に回っているかを確認する必要があります。

形状が時計回りの場合:

  • 行く生き物前方形状ラウンドが起こっている時計回りに、そして
  • 形の周り後方に進む生物は反時計回りに進んでいます

形状が反時計回りの場合:

  • 行く生き物前方形状ラウンドが起こっている反時計回りに、そして
  • 形の周りを後方に進むクリーチャーは時計回りになります。

0

トレバーはすでにこの質問をカバーしているようですが、ここに私の解決策があります:

  1. 形状がカバーする面積、つまり

    area = 0
    foreach (edge in shape)
        area += edge.begin.x * edge.end.y - edge.begin.y * edge.end.x
  2. 上記のように計算された面積を使用すると、形状自体が時計回りかどうかを簡単に判断できます。面積がゼロ未満の場合にのみ時計回りになります。

  3. オブジェクトが頂点の順序と同じように、または反対方向に移動しているかどうかを確認します。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.