@@ -23,6 +23,7 @@ import { ShapeIndexClippedShape } from './ShapeIndexClippedShape'
2323import { CrossingEdgeQuery } from './CrossingEdgeQuery'
2424import { ShapeIndexCell } from './ShapeIndexCell'
2525import { PaddedCell } from './PaddedCell'
26+ import { trueCentroid } from './centroids'
2627import {
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
679702export 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