2次元平面上の凸多角形の共通部分
2つの凸多角形同士の共通部分は、存在すれば必ず1つの凸多角形になる。
(凹多角形だと複数に分かれることがある)
凸多角形を P,QP,Q とし、((xp1,yp1),...,(xpN,ypN))((xp1,yp1),...,(xpN,ypN)) のように反時計回りの点列で与えられるとする。
PP の点の個数を NN、QQ の点の個数を MM とする。
共通部分の凸多角形を、同様に点列で求めたい。
アルゴリズム
O(NM)O(NM) アルゴリズム
共通部分の頂点を蓄積する集合 RsetRset を用意する。
- PP の各点につき、QQ に内包されている(または辺上にある)点を RsetRset に入れる
- QQ の各点につき、PP に内包されている(または辺上にある)点を RsetRset に入れる
- PP と QQ の各辺ペアにつき、交わるなら交点の座標を求め、RsetRset に入れる
- 端点で交わる(一方の頂点が他方の辺上にある)場合は含めなくてよい
- 2辺が同一線上にある場合は含めない
それぞれ O(NM)O(NM) ずつかかる。
これで、RsetRset には共通部分の全頂点が入った。
これをちゃんと順番通りに並べる。
まず重心を求める。
凸多角形の重心は必ず多角形内にあるので、重心からの偏角でソートすれば、順番通りになる。
O(N+M)O(N+M) アルゴリズム
以下、点同士の比較は (x,y)(x,y) 基準でおこなうものとする。(xx 優先、xx が同じなら yy で比較)
凸包を求めるアルゴリズムの1つMonotone Chainのように、上辺と下辺に分けて考える。
PP を最小の頂点 pminpmin から最大の頂点 pmaxpmax まで、
PP の上側を伝う頂点列と下側を使う頂点列に分ける。
ただし pminpmin と pmaxpmax は両方の最初・最後に重複して入っているとする。
QQ も同じようにし、共通部分も上と下に分けて求めるとすると、それらを構成する頂点は以下のいずれかになる。
- 共通部分の上の頂点列:
- PP の上側頂点のうち、その xx 座標において QQ の上辺と下辺の間にある頂点
- QQ の上側頂点のうち、その xx 座標において PP の上辺と下辺の間にある頂点
- 辺同士の交点
- 共通部分の下の頂点列:
- PP の下側頂点のうち、その xx 座標において QQ の上辺と下辺の間にある頂点
- QQ の下側頂点のうち、その xx 座標において PP の上辺と下辺の間にある頂点
- 辺同士の交点
実装上は、たとえば以下で管理する。
- plo,phi,qlo,qhiplo,phi,qlo,qhi: それぞれ P,QP,Q の下頂点列、上頂点列
- i,j,k,li,j,k,l: 上に対応して、それぞれどこまで見たかの4つのindex
- rlo,rhirlo,rhi: 共通部分の下頂点列、上頂点列
- 4つのそれぞれ次の頂点 ploi+1,phij+1,qlok+1,qhil+1ploi+1,phij+1,qlok+1,qhil+1 のうち、最も小さい頂点を求める
- 仮に、ploi+1ploi+1 が選ばれたとし、これが共通部分に含まれるかチェックする
- 以下の yy 座標を求める
- ploi+1ploi+1 の xx 座標における、線分 qlok→qlok+1qlok→qlok+1 の yy 座標
- ploi+1ploi+1 の xx 座標における、線分 qhil→qhil+1qhil→qhil+1 の yy 座標
- ただし線分が垂直な場合、後の方の yy 座標
- ploi+1ploi+1 の yy 座標が両者の間(境界含む)なら、ploi+1ploi+1 は共有部分の頂点
- 含まれる場合、選ばれたのが plo,qloplo,qlo ならrlorlo に追加、phi,qhiphi,qhi なら rhirhi に追加
- 次の辺の交点を求める
- 仮に ploiploi を ploi+1ploi+1 に進めたとする
- 次の点 ploi+2ploi+2 が存在するなら、以下の2つをチェックする
- 線分 ploi+1→ploi+2ploi+1→ploi+2 と qlok→qlok+1qlok→qlok+1 の交点
- 線分 ploi+1→ploi+2ploi+1→ploi+2 と qhil→qhil+1qhil→qhil+1 の交点
- 交点が存在すれば、以下のように rlo,rhirlo,rhi に追加する
- lo同士の交点なら、rlorlo に追加
- hi同士の交点なら、rhirhi に追加
- それ以外なら、両方に追加(必ず最初または最後の点)
- この時、線分の交点を計算する順番によって、rlo,rhirlo,rhi に大きい点が先に追加されないように注意する
- 繰り返す
どちらかのlo,hiがともに pmaxpmax または qmaxqmax に到達すれば終了。
最初は i,j,k,l=−1i,j,k,l=−1 などとしておくとよい。
一方の頂点が進められたとき、他方がまだ -1 ならindexだけ進めてスキップする。