@@ -78,6 +78,53 @@ export const quaternaryToFlips = (n: Quaternary): [Flip, Flip] => {
7878
7979const FLIP_SHIFT = vec2 . fromValues ( - 1 , 1 ) as IJ ;
8080
81+
82+ // Patterns used to rearrange the cells when shifting. This adjusts the layout so that
83+ // children always overlap with their parent cells.
84+ function reversePattern ( pattern : number [ ] ) : number [ ] {
85+ return Array . from ( { length : pattern . length } , ( _ , i ) => pattern . indexOf ( i ) ) ;
86+ }
87+
88+ const PATTERN = [ 0 , 1 , 3 , 4 , 5 , 6 , 7 , 2 ] ;
89+ const PATTERN_FLIPPED = [ 0 , 1 , 2 , 7 , 3 , 4 , 5 , 6 ] ;
90+ const PATTERN_REVERSED = reversePattern ( PATTERN ) ;
91+ const PATTERN_FLIPPED_REVERSED = reversePattern ( PATTERN_FLIPPED ) ;
92+
93+ const _shiftDigits = (
94+ digits : Quaternary [ ] ,
95+ i : number ,
96+ flips : [ Flip , Flip ] ,
97+ invertJ : boolean ,
98+ pattern : number [ ]
99+ ) : void => {
100+ if ( i <= 0 ) return ;
101+
102+ const parentK = digits [ i ] || 0 ;
103+ const childK = digits [ i - 1 ] ;
104+ const F = flips [ 0 ] + flips [ 1 ] ;
105+
106+ // Detect when cells need to be shifted
107+ let needsShift : boolean = true ;
108+ let first : boolean = true ;
109+
110+ // The value of F which cells need to be shifted
111+ // The rule is flipped depending on the orientation, specifically on the value of invertJ
112+ if ( invertJ !== ( F === 0 ) ) {
113+ needsShift = parentK === 1 || parentK === 2 ; // Second & third pentagons only
114+ first = parentK === 1 ; // Second pentagon is first
115+ } else {
116+ needsShift = parentK < 2 ; // First two pentagons only
117+ first = parentK === 0 ; // First pentagon is first
118+ }
119+ if ( ! needsShift ) return ;
120+
121+ // Apply the pattern by setting the digits based on the value provided
122+ const src = first ? childK : childK + 4 ;
123+ const dst = pattern [ src ] ;
124+ digits [ i - 1 ] = dst % 4 as Quaternary ;
125+ digits [ i ] = ( parentK + 4 + Math . floor ( dst / 4 ) - Math . floor ( src / 4 ) ) % 4 as Quaternary ;
126+ }
127+
81128export const sToAnchor = ( s : number | bigint , resolution : number , orientation : Orientation ) : Anchor => {
82129 let input = BigInt ( s ) ;
83130 const reverse = orientation === 'vu' || orientation === 'wu' || orientation === 'vw' ;
@@ -86,7 +133,7 @@ export const sToAnchor = (s: number | bigint, resolution: number, orientation: O
86133 if ( reverse ) {
87134 input = ( 1n << BigInt ( 2 * resolution ) ) - input - 1n ;
88135 }
89- const anchor = _sToAnchor ( input ) ;
136+ const anchor = _sToAnchor ( input , resolution , invertJ , flipIJ ) ;
90137 if ( flipIJ ) {
91138 const { offset : [ _i , _j ] , flips : [ flipX , flipY ] } = anchor ;
92139 anchor . offset = [ _j , _i ] as IJ ;
@@ -106,30 +153,39 @@ export const sToAnchor = (s: number | bigint, resolution: number, orientation: O
106153 return anchor ;
107154}
108155
109- export const _sToAnchor = ( s : number | bigint ) : Anchor => {
110- const k = Number ( s ) % 4 as Quaternary ;
156+ export const _sToAnchor = ( s : number | bigint , resolution : number , invertJ : boolean , flipIJ : boolean ) : Anchor => {
111157 const offset = vec2 . create ( ) as KJ ;
112158 const flips = [ NO , NO ] as [ Flip , Flip ] ;
113159 let input = BigInt ( s ) ;
114160
115161 // Get all quaternary digits first
116162 const digits : Quaternary [ ] = [ ] ;
117- while ( input > 0n ) {
163+ while ( input > 0n || digits . length < resolution ) {
118164 digits . push ( Number ( input % 4n ) as Quaternary ) ;
119165 input = input >> 2n ;
120166 }
121-
167+
168+ const pattern = flipIJ ? PATTERN_FLIPPED : PATTERN ;
169+
122170 // Process digits from left to right (most significant first)
171+ for ( let i = digits . length - 1 ; i >= 0 ; i -- ) {
172+ _shiftDigits ( digits , i , flips , invertJ , pattern ) ;
173+ vec2 . multiply ( flips , flips , quaternaryToFlips ( digits [ i ] ) ) ;
174+ }
175+
176+ flips [ 0 ] = NO ; flips [ 1 ] = NO ; // Reset flips for the next loop
123177 for ( let i = digits . length - 1 ; i >= 0 ; i -- ) {
124178 // Scale up existing anchor
125179 vec2 . scale ( offset , offset , 2 ) ;
126-
180+
127181 // Get child anchor and combine with current anchor
128182 const childOffset = quaternaryToKJ ( digits [ i ] , flips ) ;
129183 vec2 . add ( offset , offset , childOffset ) ;
130184 vec2 . multiply ( flips , flips , quaternaryToFlips ( digits [ i ] ) ) ;
131185 }
132186
187+ const k = digits [ 0 ] || 0 as Quaternary ;
188+
133189 return { flips, k, offset : KJToIJ ( offset ) } ;
134190}
135191
@@ -182,26 +238,26 @@ export const IJToS = (input: IJ, resolution: number, orientation: Orientation =
182238 ij [ 1 ] = ( 1 << resolution ) - ( i + j ) ;
183239 }
184240
185- let S = _IJToS ( ij ) ;
241+ let S = _IJToS ( ij , invertJ , flipIJ , resolution ) ;
186242 if ( reverse ) {
187243 S = ( 1n << BigInt ( 2 * resolution ) ) - S - 1n ;
188244 }
189245 return S ;
190246}
191247
192- export const _IJToS = ( input : IJ ) : bigint => {
248+ export const _IJToS = ( input : IJ , invertJ : boolean , flipIJ : boolean , resolution : number ) : bigint => {
193249 // Get number of digits we need to process
194- const numDigits = getRequiredDigits ( input ) ;
250+ const numDigits = resolution ;
195251 const digits : Quaternary [ ] = new Array ( numDigits ) ;
196252
197253 const flips : [ Flip , Flip ] = [ NO , NO ] ;
198254 const pivot = vec2 . create ( ) as IJ ;
199255
200256 // Process digits from left to right (most significant first)
201- for ( let i = 0 ; i < numDigits ; i ++ ) {
257+ for ( let i = numDigits - 1 ; i >= 0 ; i -- ) {
202258 const relativeOffset = vec2 . subtract ( vec2 . create ( ) , input , pivot ) as IJ ;
203259
204- const scale = 1 << ( numDigits - 1 - i ) ;
260+ const scale = 1 << i ;
205261 const scaledOffset = vec2 . scale ( vec2 . create ( ) , relativeOffset , 1 / scale ) as IJ ;
206262
207263 const digit = IJtoQuaternary ( scaledOffset , flips ) ;
@@ -214,9 +270,16 @@ export const _IJToS = (input: IJ): bigint => {
214270 vec2 . multiply ( flips , flips , quaternaryToFlips ( digit ) ) ;
215271 }
216272
273+ const pattern = flipIJ ? PATTERN_FLIPPED_REVERSED : PATTERN_REVERSED ;
274+
275+ for ( let i = 0 ; i < digits . length ; i ++ ) {
276+ vec2 . multiply ( flips , flips , quaternaryToFlips ( digits [ i ] ) ) ;
277+ _shiftDigits ( digits , i , flips , invertJ , pattern ) ;
278+ }
279+
217280 let output = 0n ;
218- for ( let i = 0 ; i < numDigits ; i ++ ) {
219- const scale = 1n << BigInt ( 2 * ( numDigits - 1 - i ) ) ;
281+ for ( let i = numDigits - 1 ; i >= 0 ; i -- ) {
282+ const scale = 1n << BigInt ( 2 * i ) ;
220283 output += BigInt ( digits [ i ] ) * scale ;
221284 }
222285
0 commit comments