@@ -24,13 +24,13 @@ export class HierarchicalLock extends EventDispatcher {
2424 // mark the tile as "active", meaning it should be visible
2525 markActive ( x , y , level ) {
2626
27- this . _incrLock ( x , y , level , true ) ;
27+ this . _incrActiveLock ( x , y , level , true ) ;
2828
2929 }
3030
3131 markInactive ( x , y , level ) {
3232
33- this . _incrLock ( x , y , level , false ) ;
33+ this . _incrActiveLock ( x , y , level , false ) ;
3434
3535 }
3636
@@ -40,132 +40,180 @@ export class HierarchicalLock extends EventDispatcher {
4040
4141 const { locks } = this ;
4242 const childKey = getKey ( x , y , level ) ;
43- let ax = x ;
44- let ay = y ;
45- let al = level ;
43+ this . _ensureLock ( childKey , x , y , level ) ;
44+
45+ const childLock = locks [ childKey ] ;
46+ childLock . loading ++ ;
4647
47- // Create the lock for the child and mark it as loading
48- if ( ! ( childKey in locks ) ) {
48+ if ( childLock . loading === 1 && childLock . active > 0 ) {
4949
50- locks [ childKey ] = this . _createLock ( x , y , level ) ;
50+ this . _lockAncestor ( x , y , level ) ;
5151
5252 }
5353
54+ this . _tryFireEvent ( childKey ) ;
55+
56+ }
57+
58+ unmarkLoading ( x , y , level ) {
59+
60+ const { locks } = this ;
61+ const childKey = getKey ( x , y , level ) ;
5462 const childLock = locks [ childKey ] ;
55- childLock . loading ++ ;
56- this . _checkToggle ( childKey ) ;
63+ childLock . loading -- ;
64+
65+ if ( childLock . loading === 0 && childLock . active > 0 ) {
66+
67+ this . _unlockAncestor ( x , y , level ) ;
68+
69+ }
70+
71+ this . _tryFireEvent ( childKey ) ;
72+ this . _tryDeleteLock ( childKey ) ;
73+
74+ }
75+
76+ //
77+
78+ _lockAncestor ( x , y , level ) {
79+
80+ const { locks } = this ;
81+ const childKey = getKey ( x , y , level ) ;
82+ const childLock = locks [ childKey ] ;
83+ this . _ensureLock ( childKey , x , y , level ) ;
84+ if ( childLock . lockedAncestor ) return ;
85+
86+ let ax = x ;
87+ let ay = y ;
88+ let al = level ;
5789
5890 // Only lock the ancestor on the first markLoading call — subsequent concurrent
5991 // loads for the same tile share the same ancestor hold
60- if ( childLock . loading === 1 ) {
92+ while ( al > 0 ) {
6193
62- while ( al > 0 ) {
94+ al -- ;
95+ ax >>= 1 ;
96+ ay >>= 1 ;
6397
64- al -- ;
65- ax >>= 1 ;
66- ay >>= 1 ;
98+ const ancestorKey = getKey ( ax , ay , al ) ;
99+ this . _ensureLock ( ancestorKey , ax , ay , al ) ;
100+ locks [ ancestorKey ] . loadingDescendants . add ( childKey ) ;
67101
68- const ancestorKey = getKey ( ax , ay , al ) ;
69- if ( ancestorKey in locks && locks [ ancestorKey ] . dispatched ) {
102+ // save the reference so we can unlock it later when load is finished
103+ if ( locks [ ancestorKey ] . dispatched && ! childLock . lockedAncestor ) {
70104
71- // save the reference so we can unlock it later when load is finished
72- this . _incrLock ( ax , ay , al , true ) ;
73- childLock . lockedAncestor = { x : ax , y : ay , level : al } ;
74- break ;
75-
76- }
105+ this . _incrActiveLock ( ax , ay , al , true ) ;
106+ childLock . lockedAncestor = { x : ax , y : ay , level : al } ;
77107
78108 }
79109
80110 }
81111
82112 }
83113
84- unmarkLoading ( x , y , level ) {
114+ _unlockAncestor ( x , y , level ) {
85115
86116 const { locks } = this ;
87117 const childKey = getKey ( x , y , level ) ;
88118 const childLock = locks [ childKey ] ;
119+ if ( ! childLock ) return ;
89120
90- if ( ! childLock ) {
121+ // unmark the descendants list
122+ let ax = x ;
123+ let ay = y ;
124+ let al = level ;
125+ while ( al > 0 ) {
91126
92- throw new Error ( 'HierarchicalLock: unmarkLoading called without a matching markLoading.' ) ;
127+ al -- ;
128+ ax >>= 1 ;
129+ ay >>= 1 ;
93130
94- }
131+ const ancestorKey = getKey ( ax , ay , al ) ;
132+ locks [ ancestorKey ] . loadingDescendants . delete ( childKey ) ;
133+ this . _tryDeleteLock ( ancestorKey ) ;
95134
96- childLock . loading -- ;
97- this . _checkToggle ( childKey ) ;
135+ }
98136
99- // Release the ancestor only when the last concurrent load finishes
100- if ( childLock . loading === 0 && childLock . lockedAncestor ) {
137+ // remove the locked ancestor
138+ if ( childLock . lockedAncestor ) {
101139
102- const { x : ax , y : ay , level : al } = childLock . lockedAncestor ;
103- this . _incrLock ( ax , ay , al , false ) ;
140+ // unlock the ancestor
141+ const { x : lx , y : ly , level : ll } = childLock . lockedAncestor ;
142+ this . _incrActiveLock ( lx , ly , ll , false ) ;
104143 childLock . lockedAncestor = null ;
105144
106145 }
107146
108- this . _tryDeleteLock ( childKey ) ;
109-
110147 }
111148
112- //
113-
149+ // delete the lock if all the fields have been settled
114150 _tryDeleteLock ( key ) {
115151
116152 const lock = this . locks [ key ] ;
117- if ( lock && lock . active === 0 && lock . lockedAncestor === null && lock . loading === 0 ) {
153+ if (
154+ lock && lock . active === 0 &&
155+ lock . lockedAncestor === null &&
156+ lock . loading === 0 &&
157+ lock . loadingDescendants . size === 0
158+ ) {
118159
119160 delete this . locks [ key ] ;
120161
121162 }
122163
123164 }
124165
125- _createLock ( x , y , level ) {
166+ // ensure the lock exists
167+ _ensureLock ( key , x , y , level ) {
126168
127- return {
128- x,
129- y,
130- level,
131- active : 0 ,
132- loading : 0 ,
133- dispatched : false ,
134- lockedAncestor : null ,
135- } ;
169+ const { locks } = this ;
170+ if ( ! ( key in locks ) ) {
171+
172+ locks [ key ] = {
173+ x,
174+ y,
175+ level,
176+ active : 0 ,
177+ loading : 0 ,
178+ dispatched : false ,
179+ lockedAncestor : null ,
180+ loadingDescendants : new Set ( ) ,
181+ } ;
182+
183+ }
184+
185+ return locks [ key ] ;
136186
137187 }
138188
139- _checkToggle ( key ) {
189+ // checks whether an event should be fired
190+ _tryFireEvent ( key ) {
140191
141192 const { locks } = this ;
142193 const lock = locks [ key ] ;
143- if ( ! lock ) {
144-
145- return ;
146-
147- }
148194
149195 const shouldShow = lock . loading === 0 && lock . active > 0 ;
150196 if ( shouldShow !== lock . dispatched ) {
151197
152198 lock . dispatched = shouldShow ;
153- this . dispatchEvent ( { type : 'toggle' , active : shouldShow , x : lock . x , y : lock . y , level : lock . level } ) ;
199+ this . dispatchEvent ( {
200+ type : 'toggle' ,
201+ active : shouldShow ,
202+ x : lock . x ,
203+ y : lock . y ,
204+ level : lock . level ,
205+ } ) ;
154206
155207 }
156208
157209 }
158210
159- _incrLock ( x , y , level , incr ) {
211+ // increments the active lock for the tile
212+ _incrActiveLock ( x , y , level , incr ) {
160213
161214 const { locks } = this ;
162215 const key = getKey ( x , y , level ) ;
163-
164- if ( ! ( key in locks ) ) {
165-
166- locks [ key ] = this . _createLock ( x , y , level ) ;
167-
168- }
216+ this . _ensureLock ( key , x , y , level ) ;
169217
170218 const lock = locks [ key ] ;
171219 lock . active += incr ? 1 : - 1 ;
@@ -175,7 +223,51 @@ export class HierarchicalLock extends EventDispatcher {
175223
176224 }
177225
178- this . _checkToggle ( key ) ;
226+ // try to lock the ancestors if the lock became active and is loading
227+ if ( lock && lock . loading > 0 ) {
228+
229+ if ( incr && lock . active === 1 ) {
230+
231+ this . _lockAncestor ( x , y , level ) ;
232+
233+ } else if ( ! incr && lock . active === 0 ) {
234+
235+ this . _unlockAncestor ( x , y , level ) ;
236+
237+ }
238+
239+ }
240+
241+ // replace all the existing locks up this chain
242+ if ( incr && lock . active === 1 ) {
243+
244+ lock . loadingDescendants . forEach ( key => {
245+
246+ const childLock = locks [ key ] ;
247+ if ( childLock . lockedAncestor ) {
248+
249+ const { x : lx , y : ly , level : ll } = childLock . lockedAncestor ;
250+ this . markInactive ( lx , ly , ll ) ;
251+ lock . active ++ ;
252+
253+ childLock . lockedAncestor . x = x ;
254+ childLock . lockedAncestor . y = y ;
255+ childLock . lockedAncestor . level = level ;
256+
257+ } else if ( lock . loading === 0 && childLock . active > 0 && childLock . loading > 0 ) {
258+
259+ // No ancestor was dispatched when this descendant started loading;
260+ // this tile is now visible so use it as the placeholder.
261+ lock . active ++ ;
262+ childLock . lockedAncestor = { x, y, level } ;
263+
264+ }
265+
266+ } ) ;
267+
268+ }
269+
270+ this . _tryFireEvent ( key ) ;
179271 this . _tryDeleteLock ( key ) ;
180272
181273 }
0 commit comments