余弦定理 2円の交点 計算幾何学

円と円の交点

円と円の交点
Feb. 2, 2020, 1:52 p.m.

目次

解説

2つの円の中心座標C1, C2と半径R1, R2が与えられて、その交点の座標を求めるアルゴリズム.
例えば以下のような条件.

$$
C_1(0, 0), R_1 2 C_2(0, 3), R_2 1
$$

ここでは関係ないが、交点を持つかどうかは中心間の距離が半径の合計より大きいかどうかを見れば良い.

交点と中心を三角形とみなすと、三辺の長さが分かっている.
よって余弦定理で中心間の線分ベクトルと交点へのベクトルの角度が求められる.
また、長さは円の半径である.
回転行列によりベクトルを回転させ、半径の長さでベクトルを伸縮してやれば交点になる.
交点は2つあるので、もう一つのベクトルは逆回転行列で逆回転させる.

コード

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
pair<Vector2, Vector2> crossPoints() {
  Vector2 baseVec = C2 - C1;
  double baseLen = baseVec.length();
  double cos_ = (baseLen*baseLen + R1*R1 - R2*R2) / (2 * baseLen * R1);
  double sin_ = sqrt(1 - cos_*cos_);
  // Counter-clockwise
  Vector2 a_(cos_ * baseVec.x + -sin_ * baseVec.y, sin_ * baseVec.x + cos_ * baseVec.y);
  // Clockwise
  Vector2 b_(cos_ * baseVec.x + sin_ * baseVec.y, -sin_ * baseVec.x + cos_ * baseVec.y);
  Vector2 a = C1 + a_ * R1 / baseLen;
  Vector2 b = C1 + b_ * R1 / baseLen;
  if (fabs(a.x) < EPS) a.x = 0.0;
  if (fabs(a.y) < EPS) a.y = 0.0;
  if (fabs(b.x) < EPS) b.x = 0.0;
  if (fabs(b.y) < EPS) b.y = 0.0;
  if (a < b) return make_pair(a, b);
  return make_pair(b, a);
}

ボツになったアルゴリズム

2つの交点を通る直線への垂線までの長さは半径の長さに比例するという仮定に基づいたアルゴリズム.

  1. 2円の半径の比から直線$l$と線分$C_1 C_2$の交点$p$を求める.
  2. 線分$C_1 C_2$に垂直でかつ$p$を始点とする単位ベクトルを求める.
  3. 三平方の定理で$p$から2交点$a$, $b$への距離を求める.
  4. 交点を計算する.

以下の入力でうまく動かない.
後ほど調査する.

1
2
0 0 10
0 10 8
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Vector2 mid(Vector2 c1, double r1, Vector2 c2, double r2) {
  return c1 + (c2 - c1) * (r1 / (r1 + r2));
}

Vector2 perpen(Vector2 a, Vector2 b) {
  Vector2 c = b - a;
  double d = c.x;
  c.x = c.y;
  c.y = -d;
  return c;
}

Vector2 unitVec(Vector2 v) {
  return v / v.length();
}

pair<Vector2, Vector2> crossPoints() {
  Vector2 p = mid(C1, R1, C2, R2);
  cout << p << endl;
  Vector2 e = unitVec(perpen(C1, p));
  double len = sqrt(R1*R1 - (p - C1).norm());
  Vector2 x1 = p + e * len;
  Vector2 x2 = p + e * (-len);
  if (fabs(x1.x) < EPS) x1.x = 0.0;
  if (fabs(x1.y) < EPS) x1.y = 0.0;
  if (fabs(x2.x) < EPS) x2.x = 0.0;
  if (fabs(x2.y) < EPS) x2.y = 0.0;
  if (x1 < x2) return make_pair(x1, x2);
  return make_pair(x2, x1);
}