Skip to content

Commit

Permalink
feat(r1): Interval.approxEqual, Interval.directedHausdorffDistance
Browse files Browse the repository at this point in the history
  • Loading branch information
missinglink committed Jul 19, 2024
1 parent c21d906 commit 3422fe8
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 0 deletions.
20 changes: 20 additions & 0 deletions r1/Interval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,26 @@ export class Interval {
return new Interval(this.lo - margin, this.hi + margin)
}

/**
* Reports whether the interval can be transformed into the given interval by moving each endpoint a small distance.
* The empty interval is considered to be positioned arbitrarily on the real line, so any interval with a small enough length will match the empty interval.
*/
approxEqual(oi: Interval): boolean {
if (this.isEmpty()) return oi.length() <= 2 * 1e-15
if (oi.isEmpty()) return this.length() <= 2 * 1e-15
return Math.abs(oi.lo - this.lo) <= 1e-15 && Math.abs(oi.hi - this.hi) <= 1e-15
}

/**
* Returns the Hausdorff distance to the given interval. For two intervals x and y, this distance is defined as:
* h(x, y) = max_{p in x} min_{q in y} d(p, q).
*/
directedHausdorffDistance(oi: Interval): number {
if (this.isEmpty()) return 0
if (oi.isEmpty()) return Infinity
return Math.max(0, Math.max(this.hi - oi.hi, oi.lo - this.lo))
}

/**
* Truncates {lo, hi} floats to n digits of precision.
*/
Expand Down
42 changes: 42 additions & 0 deletions r1/Interval_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,48 @@ test('expanded', t => {
ok(UNIT.expanded(-0.51).equal(EMPTY))
})

test('approxEqual', t => {
// Choose two values lo and hi such that it's okay to shift an endpoint by
// kLo (i.e., the resulting interval is equivalent) but not by kHi.
const lo = 4 * 2.220446049250313e-16 // < max_error default
const hi = 6 * 2.220446049250313e-16 // > max_error default

// Empty intervals.
ok(EMPTY.approxEqual(EMPTY))
ok(new Interval(0, 0).approxEqual(EMPTY))
ok(EMPTY.approxEqual(new Interval(0, 0)))
ok(new Interval(1, 1).approxEqual(EMPTY))
ok(EMPTY.approxEqual(new Interval(1, 1)))
ok(!EMPTY.approxEqual(new Interval(0, 1)))
ok(EMPTY.approxEqual(new Interval(1, 1 + 2 * lo)))
ok(!EMPTY.approxEqual(new Interval(1, 1 + 2 * hi)))

// Singleton intervals.
ok(new Interval(1, 1).approxEqual(new Interval(1, 1)))
ok(new Interval(1, 1).approxEqual(new Interval(1 - lo, 1 - lo)))
ok(new Interval(1, 1).approxEqual(new Interval(1 + lo, 1 + lo)))
ok(!new Interval(1, 1).approxEqual(new Interval(1 - hi, 1)))
ok(!new Interval(1, 1).approxEqual(new Interval(1, 1 + hi)))
ok(new Interval(1, 1).approxEqual(new Interval(1 - lo, 1 + lo)))
ok(!new Interval(0, 0).approxEqual(new Interval(1, 1)))

// Other intervals.
ok(new Interval(1 - lo, 2 + lo).approxEqual(new Interval(1, 2)))
ok(new Interval(1 + lo, 2 - lo).approxEqual(new Interval(1, 2)))
ok(!new Interval(1 - hi, 2 + lo).approxEqual(new Interval(1, 2)))
ok(!new Interval(1 + hi, 2 - lo).approxEqual(new Interval(1, 2)))
ok(!new Interval(1 - lo, 2 + hi).approxEqual(new Interval(1, 2)))
ok(!new Interval(1 + lo, 2 - hi).approxEqual(new Interval(1, 2)))
})

test('directedHausdorffDistance', t => {
equal(EMPTY.directedHausdorffDistance(EMPTY), 0)
equal(UNIT.directedHausdorffDistance(EMPTY), Infinity)
equal(new Interval(1, 1).directedHausdorffDistance(new Interval(1, 1)), 0)
equal(new Interval(1, 3).directedHausdorffDistance(new Interval(1, 1)), 2)
equal(new Interval(1, 1).directedHausdorffDistance(new Interval(3, 5)), 2)
})

test('toString', t => {
equal(new Interval(2, 4.5).toString(), '[2.0000000, 4.5000000]')
})

0 comments on commit 3422fe8

Please sign in to comment.