@@ -27,13 +27,14 @@ import {
2727 getGrapheneFragmentKey ,
2828 GRAPHENE_MESH_NEW_SEGMENT_RPC_ID ,
2929 ChunkedGraphSourceParameters ,
30- MeshSourceParameters ,
30+ MeshSourceParametersWithFocus ,
3131 CHUNKED_GRAPH_LAYER_RPC_ID ,
3232 CHUNKED_GRAPH_RENDER_LAYER_UPDATE_SOURCES_RPC_ID ,
3333 RENDER_RATIO_LIMIT ,
3434 isBaseSegmentId ,
3535 parseGrapheneError ,
3636 getHttpSource ,
37+ startLayerForBBox ,
3738} from "#src/datasource/graphene/base.js" ;
3839import { decodeManifestChunk } from "#src/datasource/precomputed/backend.js" ;
3940import { WithSharedKvStoreContextCounterpart } from "#src/kvstore/backend.js" ;
@@ -98,7 +99,7 @@ function downloadFragmentWithSharding(
9899function downloadFragment (
99100 fragmentKvStore : KvStoreWithPath ,
100101 fragmentId : string ,
101- parameters : MeshSourceParameters ,
102+ parameters : MeshSourceParametersWithFocus ,
102103 signal : AbortSignal ,
103104) : Promise < ReadResponse > {
104105 if ( parameters . sharding ) {
@@ -123,7 +124,7 @@ async function decodeDracoFragmentChunk(
123124@registerSharedObject ( )
124125export class GrapheneMeshSource extends WithParameters (
125126 WithSharedKvStoreContextCounterpart ( MeshSource ) ,
126- MeshSourceParameters ,
127+ MeshSourceParametersWithFocus ,
127128) {
128129 manifestRequestCount = new Map < string , number > ( ) ;
129130 newSegments = new Uint64Set ( ) ;
@@ -136,6 +137,13 @@ export class GrapheneMeshSource extends WithParameters(
136137 this . parameters . fragmentUrl ,
137138 ) ;
138139
140+ focusBoundingBox : SharedWatchableValue < Float32Array | undefined > ;
141+
142+ constructor ( rpc : RPC , options : MeshSourceParametersWithFocus ) {
143+ super ( rpc , options ) ;
144+ this . focusBoundingBox = rpc . get ( options . focusBoundingBox ) ;
145+ }
146+
139147 addNewSegment ( segment : bigint ) {
140148 const { newSegments } = this ;
141149 newSegments . add ( segment ) ;
@@ -146,12 +154,41 @@ export class GrapheneMeshSource extends WithParameters(
146154 }
147155
148156 async download ( chunk : ManifestChunk , signal : AbortSignal ) {
157+ const {
158+ focusBoundingBox : { value : focusBoundingBox } ,
159+ } = this ;
160+ const { chunkSize, nBitsForLayerId } = this . parameters ;
161+
162+ const unregister = this . registerDisposer (
163+ this . focusBoundingBox . changed . add ( ( ) => {
164+ chunk . downloadAbortController ?. abort ( "retry" ) ;
165+ unregister ( ) ;
166+ if ( chunk . newRequestedState !== ChunkState . NEW ) {
167+ // re-download manifest
168+ this . chunkManager . queueManager . updateChunkState (
169+ chunk ,
170+ ChunkState . QUEUED ,
171+ ) ;
172+ }
173+ } ) ,
174+ ) ;
175+
149176 const { parameters, newSegments, manifestRequestCount } = this ;
150- if ( isBaseSegmentId ( chunk . objectId , parameters . nBitsForLayerId ) ) {
177+ if ( isBaseSegmentId ( chunk . objectId , nBitsForLayerId ) ) {
151178 return decodeManifestChunk ( chunk , { fragments : [ ] } ) ;
152179 }
180+
153181 const { fetchOkImpl, baseUrl } = this . manifestHttpSource ;
154- const manifestPath = `/manifest/${ chunk . objectId } :${ parameters . lod } ?verify=1&prepend_seg_ids=1` ;
182+ let manifestPath = `/manifest/${ chunk . objectId } :${ parameters . lod } ?verify=1&prepend_seg_ids=1` ;
183+ if ( focusBoundingBox ) {
184+ const rank = focusBoundingBox . length / 2 ;
185+ const startLayer = startLayerForBBox ( focusBoundingBox , chunkSize ) ;
186+ const boundsStr = Array . from (
187+ new Array ( rank ) ,
188+ ( _ , i ) => `${ focusBoundingBox [ i ] } -${ focusBoundingBox [ i + rank ] } ` ,
189+ ) . join ( "_" ) ;
190+ manifestPath += `&bounds=${ boundsStr } &start_layer=${ startLayer } ` ;
191+ }
155192 const response = await (
156193 await fetchOkImpl ( baseUrl + manifestPath , { signal } )
157194 ) . json ( ) ;
0 commit comments