Skip to content

Commit c26606e

Browse files
committed
refactor(s2): Cap
1 parent 5da9457 commit c26606e

2 files changed

Lines changed: 89 additions & 63 deletions

File tree

s2/Cap.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import type { CellID } from './cellid'
1515
import type { Region } from './Region'
1616
import { Cell } from './Cell'
1717
import { LatLng } from './LatLng'
18+
import { interpolateAtDistance } from './edge_distances'
1819

1920
export const CENTER_POINT = Point.fromCoords(1.0, 0, 0)
2021

@@ -481,4 +482,29 @@ export class Cap implements Region {
481482
const r = 1 - 0.5 * this.height()
482483
return Point.fromVector(this.center.vector.mul(r * this.area()))
483484
}
485+
486+
/**
487+
* Returns the smallest cap which encloses this cap and another cap.
488+
*/
489+
union(oc: Cap): Cap {
490+
let c: Cap = this
491+
492+
// If the other cap is larger, swap c and other for the rest of the computations.
493+
if (c.rad < oc.rad) {
494+
;[c, oc] = [oc, c]
495+
}
496+
497+
if (c.isFull() || oc.isEmpty()) return c
498+
499+
const cRadius = c.radius()
500+
const otherRadius = oc.radius()
501+
const distance = c.center.distance(oc.center)
502+
503+
if (cRadius >= distance + otherRadius) return c
504+
505+
const resRadius = 0.5 * (distance + cRadius + otherRadius)
506+
const resCenter = interpolateAtDistance(0.5 * (distance - cRadius + otherRadius), c.center, oc.center)
507+
508+
return Cap.fromCenterAngle(resCenter, resRadius)
509+
}
484510
}

s2/Cap_test.ts

Lines changed: 63 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { LatLng } from './LatLng'
1212
import { DBL_EPSILON, EPSILON } from './predicates'
1313
import { DEGREE } from '../s1/angle_constants'
1414

15-
import { pointsNear, randomPoint, randomUniformFloat64 } from './testing'
15+
import { float64Eq, pointsNear, randomPoint, randomUniformFloat64 } from './testing'
1616
import { float64Near } from '../r1/math'
1717
import { MinWidthMetric } from './Metric_constants'
1818
import { Cell } from './Cell'
@@ -504,68 +504,68 @@ describe('s2.Cap', () => {
504504
}
505505
})
506506

507-
// test('union', () => {
508-
// const a = Cap.fromCenterAngle(Point.fromLatLng(LatLng.fromDegrees(50.0, 10.0)), 0.2 * DEGREE)
509-
// const b = Cap.fromCenterAngle(Point.fromLatLng(LatLng.fromDegrees(50.0, 10.0)), 0.3 * DEGREE)
510-
// ok(b.contains(a), `${b}.contains(${a}) = false, want true`)
511-
// ok(b.approxEqual(a.union(b)), `${b}.approxEqual(${a.union(b)}) = false, want true`)
512-
513-
// ok(a.union(Cap.fullCap()).isFull(), `${a}.union(${Cap.fullCap()}).isFull() = false, want true`)
514-
515-
// ok(a.union(Cap.emptyCap()).approxEqual(a), `${a}.union(Cap.emptyCap()) = ${a.union(Cap.emptyCap())}, want ${a}`)
516-
517-
// const c = Cap.fromCenterAngle(Point.fromLatLng(LatLng.fromDegrees(51.0, 11.0)), 1.5 * DEGREE)
518-
// ok(c.contains(a), `${c}.contains(${a}) = false, want true`)
519-
// ok(a.union(c).center.approxEqual(c.center), `${a}.union(${c}).center = ${a.union(c).center}, want ${c.center}`)
520-
// ok(
521-
// s2.float64Eq(a.union(c).radius(), c.radius()),
522-
// `${a}.union(${c}).radius = ${a.union(c).radius()}, want ${c.radius()}`
523-
// )
524-
525-
// const d = Cap.fromCenterAngle(Point.fromLatLng(LatLng.fromDegrees(51.0, 11.0)), 0.1 * DEGREE)
526-
// ok(!d.contains(a), `${d}.contains(${a}) = true, want false`)
527-
// ok(!d.intersects(a), `${d}.intersects(${a}) = true, want false`)
528-
529-
// ok(a.union(d).approxEqual(d.union(a)), `${a}.union(${d}).approxEqual(${d.union(a)}) = false, want true`)
530-
// ok(
531-
// float64Near(LatLng.fromPoint(a.union(d).center).lat.degrees(), 50.4588, 0.001),
532-
// `${a.union(d)}.center.lat = ${LatLng.fromPoint(a.union(d).center).lat.degrees()}, want 50.4588`
533-
// )
534-
// ok(
535-
// float64Near(LatLng.fromPoint(a.union(d).center).lng.degrees(), 10.4525, 0.001),
536-
// `${a.union(d)}.center.lng = ${LatLng.fromPoint(a.union(d).center).lng.degrees()}, want 10.4525`
537-
// )
538-
// ok(
539-
// float64Near(a.union(d).radius().degrees(), 0.7425, 0.001),
540-
// `${a.union(d)}.radius = ${a.union(d).radius().degrees()}, want 0.7425`
541-
// )
542-
543-
// const e = Cap.fromCenterAngle(Point.fromLatLng(LatLng.fromDegrees(50.3, 10.3)), 0.2 * DEGREE)
544-
// ok(!e.contains(a), `${e}.contains(${a}) = false, want true`)
545-
// ok(e.intersects(a), `${e}.intersects(${a}) = false, want true`)
546-
// ok(a.union(e).approxEqual(e.union(a)), `${a}.union(${e}).approxEqual(${e.union(a)}) = false, want true`)
547-
// ok(
548-
// float64Near(LatLng.fromPoint(a.union(e).center).lat.degrees(), 50.15, 0.001),
549-
// `${a.union(e)}.center.lat = ${LatLng.fromPoint(a.union(e).center).lat.degrees()}, want 50.1500`
550-
// )
551-
// ok(
552-
// float64Near(LatLng.fromPoint(a.union(e).center).lng.degrees(), 10.1495, 0.001),
553-
// `${a.union(e)}.center.lng = ${LatLng.fromPoint(a.union(e).center).lng.degrees()}, want 10.1495`
554-
// )
555-
// ok(
556-
// float64Near(a.union(e).radius().degrees(), 0.3781, 0.001),
557-
// `${a.union(e)}.radius = ${a.union(e).radius().degrees()}, want 0.3781`
558-
// )
559-
560-
// const p1 = new Point(0, 0, 1)
561-
// const p2 = new Point(0, 1, 0)
562-
// const f = Cap.fromCenterAngle(p1, 150 * DEGREE)
563-
// const g = Cap.fromCenterAngle(p2, 150 * DEGREE)
564-
// ok(f.union(g).isFull(), `${f}.union(${g}).isFull() = false, want true`)
565-
566-
// const hemi = Cap.fromCenterHeight(p1, 1)
567-
// ok(hemi.union(hemi.complement()).isFull(), `${hemi}.union(${hemi.complement()}).isFull() = false, want true`)
568-
// })
507+
test('union', () => {
508+
const a = Cap.fromCenterAngle(Point.fromLatLng(LatLng.fromDegrees(50.0, 10.0)), 0.2 * DEGREE)
509+
const b = Cap.fromCenterAngle(Point.fromLatLng(LatLng.fromDegrees(50.0, 10.0)), 0.3 * DEGREE)
510+
ok(b.contains(a), `${b}.contains(${a}) = false, want true`)
511+
ok(b.approxEqual(a.union(b)), `${b}.approxEqual(${a.union(b)}) = false, want true`)
512+
513+
ok(a.union(Cap.fullCap()).isFull(), `${a}.union(${Cap.fullCap()}).isFull() = false, want true`)
514+
515+
ok(a.union(Cap.emptyCap()).approxEqual(a), `${a}.union(Cap.emptyCap()) = ${a.union(Cap.emptyCap())}, want ${a}`)
516+
517+
const c = Cap.fromCenterAngle(Point.fromLatLng(LatLng.fromDegrees(51.0, 11.0)), 1.5 * DEGREE)
518+
ok(c.contains(a), `${c}.contains(${a}) = false, want true`)
519+
ok(a.union(c).center.approxEqual(c.center), `${a}.union(${c}).center = ${a.union(c).center}, want ${c.center}`)
520+
ok(
521+
float64Eq(a.union(c).radius(), c.radius()),
522+
`${a}.union(${c}).radius = ${a.union(c).radius()}, want ${c.radius()}`
523+
)
524+
525+
const d = Cap.fromCenterAngle(Point.fromLatLng(LatLng.fromDegrees(51.0, 11.0)), 0.1 * DEGREE)
526+
ok(!d.contains(a), `${d}.contains(${a}) = true, want false`)
527+
ok(!d.intersects(a), `${d}.intersects(${a}) = true, want false`)
528+
529+
ok(a.union(d).approxEqual(d.union(a)), `${a}.union(${d}).approxEqual(${d.union(a)}) = false, want true`)
530+
ok(
531+
float64Near(angle.degrees(LatLng.fromPoint(a.union(d).center).lat), 50.4588, 0.001),
532+
`${a.union(d)}.center.lat = ${angle.degrees(LatLng.fromPoint(a.union(d).center).lat)}, want 50.4588`
533+
)
534+
ok(
535+
float64Near(angle.degrees(LatLng.fromPoint(a.union(d).center).lng), 10.4525, 0.001),
536+
`${a.union(d)}.center.lng = ${angle.degrees(LatLng.fromPoint(a.union(d).center).lng)}, want 10.4525`
537+
)
538+
ok(
539+
float64Near(angle.degrees(a.union(d).radius()), 0.7425, 0.001),
540+
`${a.union(d)}.radius = ${angle.degrees(a.union(d).radius())}, want 0.7425`
541+
)
542+
543+
const e = Cap.fromCenterAngle(Point.fromLatLng(LatLng.fromDegrees(50.3, 10.3)), 0.2 * DEGREE)
544+
ok(!e.contains(a), `${e}.contains(${a}) = false, want true`)
545+
ok(e.intersects(a), `${e}.intersects(${a}) = false, want true`)
546+
ok(a.union(e).approxEqual(e.union(a)), `${a}.union(${e}).approxEqual(${e.union(a)}) = false, want true`)
547+
ok(
548+
float64Near(angle.degrees(LatLng.fromPoint(a.union(e).center).lat), 50.15, 0.001),
549+
`${a.union(e)}.center.lat = ${angle.degrees(LatLng.fromPoint(a.union(e).center).lat)}, want 50.1500`
550+
)
551+
ok(
552+
float64Near(angle.degrees(LatLng.fromPoint(a.union(e).center).lng), 10.1495, 0.001),
553+
`${a.union(e)}.center.lng = ${angle.degrees(LatLng.fromPoint(a.union(e).center).lng)}, want 10.1495`
554+
)
555+
ok(
556+
float64Near(angle.degrees(a.union(e).radius()), 0.3781, 0.001),
557+
`${a.union(e)}.radius = ${angle.degrees(a.union(e).radius())}, want 0.3781`
558+
)
559+
560+
const p1 = new Point(0, 0, 1)
561+
const p2 = new Point(0, 1, 0)
562+
const f = Cap.fromCenterAngle(p1, 150 * DEGREE)
563+
const g = Cap.fromCenterAngle(p2, 150 * DEGREE)
564+
ok(f.union(g).isFull(), `${f}.union(${g}).isFull() = false, want true`)
565+
566+
const hemi = Cap.fromCenterHeight(p1, 1)
567+
ok(hemi.union(hemi.complement()).isFull(), `${hemi}.union(${hemi.complement()}).isFull() = false, want true`)
568+
})
569569

570570
test('equal', () => {
571571
const tests = [

0 commit comments

Comments
 (0)