javaによる2次元座標の計算など
とりあえず、下記に共円チェッカーで利用しているクラスの全文を記載します。
2011/07/18 22:30 Lineのコンストラクタ内の数式が誤っていたため、修正。また、参考サイトを記載。
package hm.orz.chaos114.android.kyouenchecker; import java.util.ArrayList; import java.util.List; public class GameModel { class Point { double x; double y; Point(double x, double y) { this.x = x; this.y = y; } double getAbs() { return Math.sqrt(x * x + y * y); } Point difference(Point p2) { return new Point(x - p2.x, y - p2.y); } Point sum(Point p2) { return new Point(x + p2.x, y + p2.y); } @Override public String toString() { return "Point [x=" + x + ", y=" + y + "]"; } } /** * Ax+By+C=0を表現するクラス。 */ class Line { Point p1; Point p2; double a; double b; double c; Line(Point p1, Point p2) { this.p1 = p1; this.p2 = p2; a = p1.y - p2.y; b = p2.x - p1.x; c = p1.x * p2.y - p2.x * p1.y; } double getY(double x) { double y = -1 * (a * x + c) / b; return y; } @Override public String toString() { return "Line [p1=" + p1 + ", p2=" + p2 + ", a=" + a + ", b=" + b + ", c=" + c + "]"; } } /** * 共円情報を表現するクラス。 * * @author noboru */ class KyouenData { Point p1; Point p2; Point p3; Point p4; public KyouenData(Point p1, Point p2, Point p3, Point p4) { this.p1 = p1; this.p2 = p2; this.p3 = p3; this.p4 = p4; } } List<Point> points = new ArrayList<Point>(); public void add(int x, int y) { Point p = new Point(x, y); points.add(p); } public KyouenData isKyouen() { if (points.size() < 4) { return null; } Point p1 = points.get(points.size() - 1); for (int i = 0; i < points.size() - 1; i++) { Point p2 = points.get(i); for (int j = i + 1; j < points.size() - 1; j++) { Point p3 = points.get(j); for (int k = j + 1; k < points.size() - 1; k++) { Point p4 = points.get(k); boolean kyouen = isKyouen(p1, p2, p3, p4); if (kyouen) { return new KyouenData(p1, p2, p3, p4); } } } } return null; } public boolean isKyouen(Point p1, Point p2, Point p3, Point p4) { // p1,p2の垂直二等分線を求める Line l12 = getMidperpendicular(p1, p2); // p2,p3の垂直二等分線を求める Line l23 = getMidperpendicular(p2, p3); // 交点を求める Point intersection123 = getIntersection(l12, l23); if (intersection123 == null) { // p1,p2,p3が直線上に存在する場合 Line l34 = getMidperpendicular(p3, p4); Point intersection234 = getIntersection(l23, l34); if (intersection234 == null) { // p2,p3,p4が直線状に存在する場合 return true; } } else { double dist1 = getDistance(p1, intersection123); double dist2 = getDistance(p4, intersection123); if (Math.abs(dist1 - dist2) < 0.0000001) { return true; } } return false; } /** * 2点間の距離を求める。 * * @param p1 座標1 * @param p2 座標2 * @return 距離 */ public double getDistance(Point p1, Point p2) { Point dist = p1.difference(p2); return dist.getAbs(); } /** * 2直線の交点を求める。 * * @param l1 直線1 * @param l2 直線2 * @return 交点座標(交点が存在しない場合、null) */ public Point getIntersection(Line l1, Line l2) { double f1 = l1.p2.x - l1.p1.x; double g1 = l1.p2.y - l1.p1.y; double f2 = l2.p2.x - l2.p1.x; double g2 = l2.p2.y - l2.p1.y; double det = f2 * g1 - f1 * g2; if (det == 0) { return null; } double dx = l2.p1.x - l1.p1.x; double dy = l2.p1.y - l1.p1.y; double t1 = (f2 * dy - g2 * dx) / det; return new Point(l1.p1.x + f1 * t1, l1.p1.y + g1 * t1); } /** * 2点の垂直二等分線を求める。 * * @param p1 座標1 * @param p2 座標2 * @return 垂直二等分線 */ public Line getMidperpendicular(Point p1, Point p2) { Point midpoint = getMidpoint(p1, p2); Point dif = p1.difference(p2); Point gradient = new Point(dif.y, -1 * dif.x); Line midperpendicular = new Line(midpoint, midpoint.sum(gradient)); return midperpendicular; } /** * 中点を求める。 * * @param p1 座標1 * @param p2 座標2 * @return 中点座標 */ public Point getMidpoint(Point p1, Point p2) { Point midpoint = new Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2); return midpoint; } }
で、説明など。
Pointクラス
2次元座標(x座標,y座標)を表現するためのクラスです。
ベクトル(x方向成分,y方向成分)も表します。
getAbsメソッド
座標の原点からの距離を返します。
ベクトルの大きさを表します。
differenceメソッド
sumメソッド
それぞれ、保持している座標と引数の座標の和・差を返します。
Lineクラス
Ax+By+C=0を表現するクラスです。
コンストラクタ
引数として、2つの座標を受け取ります。
保持する情報は、その2点を通過する直線の式となります。
下記サイトに書いてある通りにしました。
http://bobobobo.wordpress.com/2008/01/07/solving-linear-equations-ax-by-c-0/
getYメソッド
引数としてx座標を受け、保持している直線上のy座標を返します。
KyouenDataクラス
呼び出し元に共円となった4点を返したいがために定義したクラスです。
List
isKyouenメソッド
4点が登録されていない場合、判定を行いません。
最後に登録した点を基準とします。
それ以外の既存の3点を順に選んでゆき、それら4点が共円かどうかを判定します。
実際の判定処理は次のisKyouen(Point, Point, Point, Point)で行います。
isKyouen(Point, Point, Point, Point)メソッド
実際の共円判定を行います。
引数の4点が円周上に存在する場合、trueを返します。
以下、手順です。
- 点1、点2の垂直二等分線を求めます。以降、線12とします。
- 同じく、点2、点3の垂直二等分線を求めます。以降、線23とします。
- 線12、線23の交点を求めます。(=点1、2、3を通る円の中心)
- 線12、線23が平行な場合、nullが返却されます。(=点1,2,3が直線上に存在する)
- 点3、点4の垂直二等分線を求めます。以降、線34とします。
- 線23、線34の交点を求め、取得できない(=平行)な場合、点1,2,3,4が直線上に並んでいることになるため、trueを返します。
- 線12、線23が平行でない場合、交点が返却されます。以降、中点123とします。
- 点1と、中点123の距離を距離1とします。
- 点4と、中点123の距離を距離2とします。
- 距離1、距離2の差の絶対値が、10^-7以下の場合は同一とみなし、trueを返します。(10^-7なのは、特に理由はありません)
- 線12、線23が平行な場合、nullが返却されます。(=点1,2,3が直線上に存在する)
getDistanceメソッド
引数の2ベクトルの差を求めます。
そのベクトルの大きさを返します。
getIntersectionメソッド
引数の2直線の交点を求めます。
下記のURLの計算を実行しました。
http://www.h4.dion.ne.jp/~zero1341/t/03.htm
あまり、意味がわかってないです。
getMidperpendicularメソッド
引数の点1、点2の中点を求めます。中点12とします。
点1、点2のベクトルの差を求めます。ベクトル12とします。
ベクトル12のy成分に-1を掛け、x,yを逆にしたベクトルをベクトル21とします。
中点12を通り、(中点12にベクトル21を足した点)を通る線を返します。
getMidpointメソッド
引数の2点のx,y座標の、それぞれの和を2で割った値を設定したPointを返します。
と、いった形でチェックしています。
計算が誤っている場合など、コメント下さい。