Skip to content
This repository was archived by the owner on May 3, 2026. It is now read-only.

Commit b95a252

Browse files
committed
Add ObstructionCalculator
Signed-off-by: Jonah Snider <jonah@jonahsnider.com>
1 parent 108f80c commit b95a252

4 files changed

Lines changed: 166 additions & 0 deletions

File tree

comp-bot/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ dependencies {
7474

7575
// https://central.sonatype.com/artifact/com.google.guava/guava
7676
implementation 'com.google.guava:guava:33.5.0-jre'
77+
78+
// https://central.sonatype.com/artifact/org.apache.commons/commons-geometry-euclidean
79+
implementation 'org.apache.commons:commons-geometry-euclidean:1.0'
7780
}
7881

7982
test {

shared/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ dependencies {
2121
// https://central.sonatype.com/artifact/com.google.guava/guava
2222
implementation 'com.google.guava:guava:33.5.0-jre'
2323

24+
// https://central.sonatype.com/artifact/org.apache.commons/commons-geometry-euclidean
25+
implementation 'org.apache.commons:commons-geometry-euclidean:1.0'
26+
2427
// https://central.sonatype.com/artifact/org.apache.commons/commons-math4-core
2528
implementation 'org.apache.commons:commons-math4-legacy-core:4.0-beta1'
2629
implementation("org.apache.commons:commons-math4-legacy:4.0-beta1")
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.team581.math;
2+
3+
import edu.wpi.first.math.geometry.Translation2d;
4+
import java.util.Arrays;
5+
import java.util.List;
6+
import org.apache.commons.geometry.euclidean.twod.ConvexArea;
7+
import org.apache.commons.geometry.euclidean.twod.Lines;
8+
import org.apache.commons.geometry.euclidean.twod.RegionBSPTree2D;
9+
import org.apache.commons.geometry.euclidean.twod.Vector2D;
10+
import org.apache.commons.numbers.core.Precision;
11+
12+
public class ObstructionCalculator {
13+
private static final Precision.DoubleEquivalence PRECISION =
14+
Precision.doubleEquivalenceOfEpsilon(1e-6);
15+
16+
@SafeVarargs
17+
public static ObstructionCalculator fromTranslations(List<Translation2d>... obstructionCorners) {
18+
return new ObstructionCalculator(
19+
Arrays.stream(obstructionCorners)
20+
.map(corners -> corners.stream().map(ObstructionCalculator::vector2d).toList())
21+
.toList());
22+
}
23+
24+
private static final Vector2D vector2d(Translation2d translation) {
25+
return Vector2D.of(translation.getX(), translation.getY());
26+
}
27+
28+
private final RegionBSPTree2D region;
29+
30+
/**
31+
* @param obstructionCorners Corners of polygons representing obstructions on the field.
32+
*/
33+
public ObstructionCalculator(List<List<Vector2D>> obstructionCorners) {
34+
region = RegionBSPTree2D.empty();
35+
36+
for (List<Vector2D> corners : obstructionCorners) {
37+
ConvexArea area = ConvexArea.convexPolygonFromVertices(corners, PRECISION);
38+
region.add(area);
39+
}
40+
}
41+
42+
public boolean isObstructed(Translation2d point) {
43+
return isObstructed(vector2d(point));
44+
}
45+
46+
public boolean isObstructed(Translation2d start, Translation2d end) {
47+
return isObstructed(vector2d(start), vector2d(end));
48+
}
49+
50+
public boolean isObstructed(Vector2D point) {
51+
return region.contains((point));
52+
}
53+
54+
public boolean isObstructed(Vector2D start, Vector2D end) {
55+
return region.contains(start)
56+
|| region.contains(end)
57+
|| region.linecastFirst(Lines.segmentFromPoints(start, end, PRECISION)) != null;
58+
}
59+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package com.team581.math;
2+
3+
import static org.junit.jupiter.api.Assertions.assertFalse;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import edu.wpi.first.math.geometry.Translation2d;
7+
import java.util.List;
8+
import org.junit.jupiter.api.Test;
9+
10+
final class ObstructionCalculatorTest {
11+
private static final ObstructionCalculator CALCULATOR =
12+
ObstructionCalculator.fromTranslations(
13+
List.of(
14+
new Translation2d(0, 0),
15+
new Translation2d(2, 0),
16+
new Translation2d(2, 2),
17+
new Translation2d(0, 2)));
18+
19+
@Test
20+
void multipleObstructions() {
21+
var calculator =
22+
ObstructionCalculator.fromTranslations(
23+
List.of(
24+
new Translation2d(0, 0),
25+
new Translation2d(1, 0),
26+
new Translation2d(1, 1),
27+
new Translation2d(0, 1)),
28+
List.of(
29+
new Translation2d(5, 5),
30+
new Translation2d(6, 5),
31+
new Translation2d(6, 6),
32+
new Translation2d(5, 6)));
33+
34+
assertTrue(calculator.isObstructed(new Translation2d(0.5, 0.5)));
35+
assertTrue(calculator.isObstructed(new Translation2d(5.5, 5.5)));
36+
assertFalse(calculator.isObstructed(new Translation2d(3, 3)));
37+
}
38+
39+
@Test
40+
void pointInsideObstructionIsObstructed() {
41+
assertTrue(CALCULATOR.isObstructed(new Translation2d(1, 1)));
42+
}
43+
44+
@Test
45+
void pointOnCornerIsObstructed() {
46+
assertTrue(CALCULATOR.isObstructed(new Translation2d(0, 0)));
47+
}
48+
49+
@Test
50+
void pointOnEdgeIsObstructed() {
51+
assertTrue(CALCULATOR.isObstructed(new Translation2d(1, 0)));
52+
}
53+
54+
@Test
55+
void pointOutsideObstructionIsNotObstructed() {
56+
assertFalse(CALCULATOR.isObstructed(new Translation2d(5, 5)));
57+
}
58+
59+
@Test
60+
void segmentBetweenObstructionsIsNotObstructed() {
61+
var calculator =
62+
ObstructionCalculator.fromTranslations(
63+
List.of(
64+
new Translation2d(0, 0),
65+
new Translation2d(1, 0),
66+
new Translation2d(1, 1),
67+
new Translation2d(0, 1)),
68+
List.of(
69+
new Translation2d(3, 0),
70+
new Translation2d(4, 0),
71+
new Translation2d(4, 1),
72+
new Translation2d(3, 1)));
73+
74+
assertFalse(calculator.isObstructed(new Translation2d(1.5, 0.5), new Translation2d(2.5, 0.5)));
75+
}
76+
77+
@Test
78+
void segmentEndingAtEdgeIsObstructed() {
79+
assertTrue(CALCULATOR.isObstructed(new Translation2d(-1, 1), new Translation2d(0, 1)));
80+
}
81+
82+
@Test
83+
void segmentEntirelyInsideObstructionIsObstructed() {
84+
assertTrue(CALCULATOR.isObstructed(new Translation2d(0.5, 0.5), new Translation2d(1.5, 1.5)));
85+
}
86+
87+
@Test
88+
void segmentOutsideObstructionIsNotObstructed() {
89+
assertFalse(CALCULATOR.isObstructed(new Translation2d(3, 3), new Translation2d(5, 5)));
90+
}
91+
92+
@Test
93+
void segmentStartingInsideIsObstructed() {
94+
assertTrue(CALCULATOR.isObstructed(new Translation2d(1, 1), new Translation2d(5, 5)));
95+
}
96+
97+
@Test
98+
void segmentThroughObstructionIsObstructed() {
99+
assertTrue(CALCULATOR.isObstructed(new Translation2d(-1, 1), new Translation2d(3, 1)));
100+
}
101+
}

0 commit comments

Comments
 (0)