Skip to content

Commit a210a06

Browse files
committed
feat(s2): Polygon
1 parent 1c04b76 commit a210a06

9 files changed

Lines changed: 1798 additions & 34 deletions

File tree

s2/Loop.ts

Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { ShapeIndexClippedShape } from './ShapeIndexClippedShape'
2323
import { CrossingEdgeQuery } from './CrossingEdgeQuery'
2424
import { ShapeIndexCell } from './ShapeIndexCell'
2525
import { PaddedCell } from './PaddedCell'
26+
import { trueCentroid } from './centroids'
2627
import {
2728
clipToPaddedFace,
2829
edgeIntersectsRect,
@@ -175,9 +176,7 @@ export class Loop implements Shape {
175176

176177
validate(): Error | null {
177178
const err = this.findValidationErrorNoIndex()
178-
if (err) {
179-
return err
180-
}
179+
if (err) return err
181180
return null
182181
}
183182

@@ -190,19 +189,17 @@ export class Loop implements Shape {
190189
}
191190

192191
if (this.vertices.length < 3) {
193-
if (this.isEmptyOrFull()) {
194-
return null
195-
}
192+
if (this.isEmptyOrFull()) return null
196193
return new Error('non-empty, non-full loops must have at least 3 vertices')
197194
}
198195

199196
for (let i = 0; i < this.vertices.length; i++) {
200-
if (this.vertices[i] === this.vertex(i + 1)) {
197+
if (this.vertices[i].equals(this.vertex(i + 1))) {
201198
return new Error(`edge ${i} is degenerate (duplicate vertex)`)
202199
}
203200

204201
const other = Point.fromVector(this.vertex(i + 1).vector.mul(-1))
205-
if (this.vertices[i] === other) {
202+
if (this.vertices[i].equals(other)) {
206203
return new Error(`vertices ${i} and ${(i + 1) % this.vertices.length} are antipodal`)
207204
}
208205
}
@@ -267,7 +264,7 @@ export class Loop implements Shape {
267264
if (this.isEmptyOrFull()) return this.isEmpty() === o.isEmpty()
268265

269266
for (let offset = 0; offset < this.vertices.length; offset++) {
270-
if (this.vertices[offset] === o.vertex(0)) {
267+
if (this.vertices[offset].equals(o.vertex(0))) {
271268
for (let i = 0; i < this.vertices.length; i++) {
272269
if (this.vertex(i + offset) !== o.vertex(i)) {
273270
return false
@@ -559,7 +556,7 @@ export class Loop implements Shape {
559556
const notFound = 0
560557
if (this.vertices.length < 10) {
561558
for (let i = 1; i <= this.vertices.length; i++) {
562-
if (this.vertex(i) === p) return [i, true]
559+
if (this.vertex(i).equals(p)) return [i, true]
563560
}
564561
return [notFound, false]
565562
}
@@ -570,10 +567,10 @@ export class Loop implements Shape {
570567
const aClipped = it.indexCell().findByShapeID(0)
571568
for (let i = aClipped.numEdges() - 1; i >= 0; i--) {
572569
const ai = aClipped.edges[i]
573-
if (this.vertex(ai) === p) {
570+
if (this.vertex(ai).equals(p)) {
574571
return ai === 0 ? [this.vertices.length, true] : [ai, true]
575572
}
576-
if (this.vertex(ai + 1) === p) {
573+
if (this.vertex(ai + 1).equals(p)) {
577574
return [ai + 1, true]
578575
}
579576
}
@@ -601,7 +598,7 @@ export class Loop implements Shape {
601598
for (let i = 1; i + 1 < this.vertices.length; i++) {
602599
if (this.vertex(i + 1).vector.angle(origin.vector) > maxLength) {
603600
const oldOrigin = origin
604-
if (origin === this.vertex(0)) {
601+
if (origin.equals(this.vertex(0))) {
605602
origin = Point.fromVector(this.vertex(0).pointCross(this.vertex(i)).vector.normalize())
606603
} else if (this.vertex(i).vector.angle(this.vertex(0).vector) < maxLength) {
607604
origin = this.vertex(0)
@@ -627,7 +624,7 @@ export class Loop implements Shape {
627624
for (let i = 1; i + 1 < this.vertices.length; i++) {
628625
if (this.vertex(i + 1).vector.angle(origin.vector) > maxLength) {
629626
const oldOrigin = origin
630-
if (origin === this.vertex(0)) {
627+
if (origin.equals(this.vertex(0))) {
631628
origin = Point.fromVector(this.vertex(0).pointCross(this.vertex(i)).vector.normalize())
632629
} else if (this.vertex(i).vector.angle(this.vertex(0).vector) < maxLength) {
633630
origin = this.vertex(0)
@@ -664,16 +661,42 @@ export class Loop implements Shape {
664661
return area
665662
}
666663

667-
// centroid(): Point {
668-
// return this.surfaceIntegralPoint(TrueCentroid)
669-
// }
664+
centroid(): Point {
665+
return this.surfaceIntegralPoint(trueCentroid)
666+
}
670667

671668
// xyzFaceSiTiVertices(): XyzFaceSiTi[] {
672669
// return this.vertices.map((v) => {
673670
// const [face, si, ti, level] = xyzToFaceSiTi(v)
674671
// return new XyzFaceSiTi(v, face, si, ti, level)
675672
// })
676673
// }
674+
675+
/**
676+
* Reports whether given two loops whose boundaries
677+
* do not cross (see compareBoundary), if this loop contains the boundary of the
678+
* other loop. If reverse is true, the boundary of the other loop is reversed
679+
* first (which only affects the result when there are shared edges). This method
680+
* is cheaper than compareBoundary because it does not test for edge intersections.
681+
*
682+
* This function requires that neither loop is empty, and that if the other is full,
683+
* then reverse == false.
684+
*/
685+
containsNonCrossingBoundary(other: Loop, reverseOther: boolean): boolean {
686+
// The bounds must intersect for containment.
687+
if (!this.bound.intersects(other.bound)) return false
688+
689+
// Full loops are handled as though the loop surrounded the entire sphere.
690+
if (this.isFull()) return true
691+
if (other.isFull()) return false
692+
693+
const [m, ok] = this.findVertex(other.vertex(0))
694+
if (!ok) {
695+
return this.containsPoint(other.vertex(0)) // Since the other loops vertex 0 is not shared, we can check if this contains it.
696+
}
697+
// Otherwise check whether the edge (b0, b1) is contained by this loop.
698+
return wedgeContainsSemiwedge(this.vertex(m - 1), this.vertex(m), this.vertex(m + 1), other.vertex(1), reverseOther)
699+
}
677700
}
678701

679702
export const containsCenterMatches = (a: ShapeIndexClippedShape, target: CrossingTarget): boolean => {
@@ -837,18 +860,6 @@ export const wedgeContainsSemiwedge = (a0: Point, ab1: Point, a2: Point, b2: Poi
837860
return Point.orderedCCW(a0, a2, b2, ab1)
838861
}
839862

840-
export const containsNonCrossingBoundary = (l: Loop, other: Loop, reverseOther: boolean): boolean => {
841-
if (!l.bound.intersects(other.bound)) return false
842-
843-
if (l.isFull()) return true
844-
if (other.isFull()) return false
845-
846-
const [m, ok] = l.findVertex(other.vertex(0))
847-
if (!ok) return l.containsPoint(other.vertex(0))
848-
849-
return wedgeContainsSemiwedge(l.vertex(m - 1), l.vertex(m), l.vertex(m + 1), other.vertex(1), reverseOther)
850-
}
851-
852863
// ----
853864

854865
/**

0 commit comments

Comments
 (0)