Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
1c5868b
Add intersects util
seadowg Nov 10, 2025
fd4a6a1
Add intersects function handler
seadowg Nov 11, 2025
ab235b8
Use prototypes to get already evaluated args
seadowg Nov 11, 2025
4fb7739
Add test for non geotrace/shape strings
seadowg Nov 17, 2025
3419811
Add test cases for repeated segments and closed traced intersection
seadowg Nov 17, 2025
9641e08
Add intersects function handler to Collect
seadowg Nov 17, 2025
b2f3cc7
Move parseGeometry and parseGeometryPoint tests to geo
seadowg Nov 17, 2025
a11a20d
Convert GeoUtils to Kotlin
seadowg Nov 17, 2025
7508fe8
Move parseGeometryPoint to GeoUtils
seadowg Nov 17, 2025
17e52e8
Fix import in test
seadowg Nov 18, 2025
83a60e2
Throw exception for non-geotrace inputs
seadowg Nov 18, 2025
281fd94
Improve converted code
seadowg Nov 18, 2025
d21470c
Use zipWithNext to simplify creating segments
seadowg Nov 18, 2025
7256252
Add test to check arg length is enforced
seadowg Nov 18, 2025
77bec29
Switch test to Hamcrest
seadowg Nov 18, 2025
2a3cd02
Remove unused imports
seadowg Nov 19, 2025
da8e9a7
Add another test case to make sure we check intersections from both s…
seadowg Nov 19, 2025
d853d05
Add failing tests for non crossing intersections
seadowg Nov 20, 2025
292b7be
Rename method
seadowg Nov 20, 2025
e074729
Fix origin of one segment touching another case
seadowg Nov 20, 2025
7055bdc
Correct test
seadowg Nov 20, 2025
527ae8e
Fix line moving back on itself case
seadowg Nov 20, 2025
e7a8662
Add test for shape that closes outside the origin
seadowg Nov 20, 2025
0800a0e
Simplify non-origin vertex closing
seadowg Nov 21, 2025
602fee7
Use bounding box check based on orientation to solve right angled tri…
seadowg Nov 21, 2025
ad81da9
Fix special case with two segmnt intersection
seadowg Nov 21, 2025
69d4c8a
Pull out specific 2D geometry code
seadowg Nov 21, 2025
8860a4a
Make sure checking instersection between segments is exhaustive
seadowg Nov 21, 2025
e7b2a71
Remove unneeded return
seadowg Nov 25, 2025
30dbba2
Correct bounding box check direction
seadowg Nov 25, 2025
8822207
Add additional (failing) test for 2 segment self intersection
seadowg Nov 26, 2025
8e0e0c7
Account for case where first endpoint intersects with second segment …
seadowg Nov 26, 2025
970b508
Filter out zero length segments in traces
seadowg Nov 26, 2025
1d1a320
Remove unneeded return
seadowg Nov 26, 2025
bba4264
Fix reversed line with 3 points case
seadowg Nov 27, 2025
2fb19f8
Add basic metamorphic test for intersects
seadowg Nov 27, 2025
9f596bc
Improve adding intersecting segment using interpolation
seadowg Nov 27, 2025
29936a8
Add epsilon to colinearity check to prevent precision errors
seadowg Nov 27, 2025
08c5997
Fix accidental map
seadowg Nov 27, 2025
c6aa7c0
Add docs for interpolate
seadowg Nov 27, 2025
6a0e2e0
Increase the number of possible random intersection points in test
seadowg Nov 28, 2025
ac13533
Add tests for LineSegment#interpolate
seadowg Nov 28, 2025
d88de71
Add quickCheck helper
seadowg Nov 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Add quickCheck helper
  • Loading branch information
seadowg committed Nov 28, 2025
commit d88de713c6bda5396a250daaa70d4d5bec77e0a7
16 changes: 16 additions & 0 deletions shared/src/test/java/org/odk/collect/shared/QuickCheck.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.odk.collect.shared

/**
* Simple helper to allow writing neater [QuickCheck](https://en.wikipedia.org/wiki/QuickCheck)
* style tests.
*/
fun <Input, Output> ((Input) -> Output).quickCheck(
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did have a quick look at using an existing framework (https://github.com/pholser/junit-quickcheck for example), but it felt like we didn't really need anything so heavy yet. Maybe if we end up writing more of these kinds of tests down the line!

iterations: Int,
generator: Sequence<Input>,
checks: (Input, Output) -> Unit
) {
generator.take(iterations).forEach { input ->
val output = this(input)
checks(input, output)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.odk.collect.shared.geometry
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.junit.Test
import org.odk.collect.shared.quickCheck
import kotlin.random.Random

class GeometryTest {
Expand Down Expand Up @@ -208,10 +209,10 @@ class GeometryTest {

@Test
fun `Trace#intersects satisfies metamorphic relationships`() {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@grzesiek2010 Here's the metamorphic test I've added. It currently checks that intersection is consistent when reversing/scaling traces and that adding an intersecting segment to a non-intersecting trace causes intersects to become true. We can tidy this up/formalize it a bit, but I wanted to get your thoughts first! Right now, I'm checking 1000 random traces, but this could of course be less or more depending on how much time/power we want to expend.

Copy link
Copy Markdown
Member Author

@seadowg seadowg Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll also see that I had to add an epsilon to the collinearity check to handle precision errors and that actually got revealed because of this test which was pretty cool. I'm going to involve @getodk/testers now so we can get some more feedback on using this. It'll be interesting to see if our current calculations are actually too precise when playing around in the real world.

repeat(1000) {
val trace = generateTrace()
val intersects = trace.intersects()

{ trace: Trace -> trace.intersects() }.quickCheck(
iterations = 1000,
generator = getTraceGenerator()
) { trace, intersects ->
// Check intersects is consistent when trace is reversed
val reversedTrace = Trace(trace.points.reversed())
assertThat(
Expand Down Expand Up @@ -299,23 +300,25 @@ class GeometryTest {
assertThat(interpolatedPoint.within(segment), equalTo(true))
}

private fun generateTrace(maxLength: Int = 10, maxCoordinate: Double = 100.0): Trace {
val length = Random.nextInt(2, maxLength)
val trace = Trace(0.until(length).map {
Point(
Random.nextDouble(maxCoordinate * -1, maxCoordinate),
Random.nextDouble(maxCoordinate * -1, maxCoordinate)
)
})

return if (trace.isClosed()) {
trace
} else {
val shouldClose = Random.nextBoolean()
if (shouldClose) {
trace.copy(points = trace.points + trace.points.first())
} else {
private fun getTraceGenerator(maxLength: Int = 10, maxCoordinate: Double = 100.0): Sequence<Trace> {
return generateSequence {
val length = Random.nextInt(2, maxLength)
val trace = Trace(0.until(length).map {
Point(
Random.nextDouble(maxCoordinate * -1, maxCoordinate),
Random.nextDouble(maxCoordinate * -1, maxCoordinate)
)
})

if (trace.isClosed()) {
trace
} else {
val shouldClose = Random.nextBoolean()
if (shouldClose) {
trace.copy(points = trace.points + trace.points.first())
} else {
trace
}
}
}
}
Expand Down