Skip to content

Commit b597378

Browse files
committed
✨ Circle class which computes intersections with another Circle
1 parent e47baac commit b597378

File tree

3 files changed

+71
-0
lines changed

3 files changed

+71
-0
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.truelaurel.math.geometry
2+
3+
import scala.math.sqrt
4+
5+
case class Circle(center: Pos, radius: Int) {
6+
def sqr(x: Double) = x * x
7+
8+
// http://nains-games.over-blog.com/2014/12/intersection-de-deux-cercles.html
9+
// https://pastebin.com/5hT3G3V8
10+
def intersections(ci: Circle): Seq[Pos] = {
11+
val rc1 = radius
12+
val rc2 = ci.radius
13+
val xc1 = center.x
14+
val yc1 = center.y
15+
val xc2 = ci.center.x
16+
val yc2 = ci.center.y
17+
18+
if (yc1 == yc2) {
19+
val a = (xc1 - xc2).abs
20+
val XIa = (sqr(rc2) - sqr(a) - sqr(rc1)) / (-2 * a)
21+
val root = sqr(rc2) - sqr(a - XIa)
22+
if (root >= 0) {
23+
val YIa = yc1 + sqrt(root)
24+
val YIb = yc1 - sqrt(root)
25+
Seq(new Pos(XIa, YIa), new Pos(XIa, YIb)).distinct
26+
} else Seq.empty
27+
} else {
28+
val a = (-sqr(xc1) - sqr(yc1) + sqr(xc2) + sqr(yc2) + sqr(rc1) - sqr(rc2)) / (2 * (yc2 - yc1))
29+
val d = (xc2.toDouble - xc1) / (yc2 - yc1)
30+
val A = sqr(d) + 1
31+
val B = -2 * xc1 + 2 * yc1 * d - 2 * a * d
32+
val C = sqr(xc1) + sqr(yc1) - 2 * yc1 * a + sqr(a) - sqr(rc1)
33+
val delta = sqr(B) - 4 * A * C
34+
if (delta >= 0) {
35+
val XIa = (-B + sqrt(delta)) / (2 * A)
36+
val XIb = (-B - sqrt(delta)) / (2 * A)
37+
val YIa = a - ((-B + sqrt(delta)) / (2 * A)) * d
38+
val YIb = a - ((-B - sqrt(delta)) / (2 * A)) * d
39+
Seq(new Pos(XIa, YIa), new Pos(XIb, YIb)).distinct
40+
} else Seq.empty
41+
}
42+
}
43+
}

src/main/scala/com/truelaurel/math/geometry/Pos.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.truelaurel.math.geometry
22

33
case class Pos(x: Int, y: Int) {
4+
def this(x: Double, y: Double) = this(x.toInt, y.toInt)
5+
46
def neighbours4: Seq[Pos] =
57
Seq(Pos(x + 1, y),
68
Pos(x - 1, y),
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.truelaurel.math.geometry
2+
3+
import org.scalatest.{FlatSpec, Matchers}
4+
5+
class CircleTest extends FlatSpec with Matchers {
6+
7+
"intersection" should "be empty for circles too far away" in {
8+
Circle(Pos(100, 150), 100).intersections(Circle(Pos(500, 150), 100)) shouldBe empty
9+
}
10+
11+
it should "be a single point when tangent" in {
12+
Circle(Pos(0, 0), 100).intersections(Circle(Pos(200, 0), 100)) shouldBe Seq(Pos(100, 0))
13+
Circle(Pos(0, 400), 200).intersections(Circle(Pos(600, 400), 400)) shouldBe Seq(Pos(200, 400))
14+
}
15+
16+
it should "be a 2 points when secant" in {
17+
Circle(Pos(0, 0), 100).intersections(Circle(Pos(0, 75), 100)) should
18+
contain theSameElementsAs Seq(Pos(92, 37), Pos(-92, 37))
19+
20+
Circle(Pos(0, 50), 100).intersections(Circle(Pos(50, 0), 100)) should
21+
contain theSameElementsAs Seq(Pos(91, 91), Pos(-41, -41))
22+
//detects rounding bug
23+
Circle(Pos(1735, 542), 180).intersections(Circle(Pos(1476, 490), 220)) should
24+
contain theSameElementsAs List(Pos(1664, 376), Pos(1605, 667))
25+
}
26+
}

0 commit comments

Comments
 (0)