-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcompose.ts
90 lines (73 loc) · 4.22 KB
/
compose.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/*
* Copyright (c) 2016 - now, David Sehnal, licensed under Apache 2.0, See LICENSE file for more info.
*/
import * as DataFormat from '../../common/data-format'
import * as Data from './data-model'
import * as Box from '../algebra/box'
import * as Coords from '../algebra/coordinate'
import * as File from '../../common/file'
export default async function compose(query: Data.QueryContext.Data) {
for (const block of query.samplingInfo.blocks) {
await fillBlock(query, block);
}
}
async function readBlock(query: Data.QueryContext.Data, coord: Coords.Grid<'Block'>, blockBox: Box.Fractional): Promise<Data.BlockData> {
const numChannels = query.data.header.channels.length;
const blockSampleCount = Box.dimensions(Box.fractionalToGrid(blockBox, query.samplingInfo.sampling.dataDomain));
const size = numChannels * blockSampleCount[0] * blockSampleCount[1] * blockSampleCount[2];
const { valueType, blockSize } = query.data.header;
const dataSampleCount = query.data.header.sampling[query.samplingInfo.sampling.index].sampleCount;
const buffer = File.createTypedArrayBufferContext(size, valueType);
const byteOffset = query.samplingInfo.sampling.byteOffset
+ DataFormat.getValueByteSize(valueType) * numChannels * blockSize
* (blockSampleCount[1] * blockSampleCount[2] * coord[0]
+ dataSampleCount[0] * blockSampleCount[2] * coord[1]
+ dataSampleCount[0] * dataSampleCount[1] * coord[2]);
const values = await File.readTypedArray(buffer, query.data.file, byteOffset, size, 0);
return {
sampleCount: blockSampleCount,
values
};
}
function fillData(query: Data.QueryContext.Data, blockData: Data.BlockData, blockGridBox: Box.Grid<'BlockGrid'>, queryGridBox: Box.Grid<'Query'>) {
const source = blockData.values;
const { sizeX: tSizeH, sizeXY: tSizeHK } = Coords.gridMetrics(query.samplingInfo.gridDomain.sampleCount);
const { sizeX: sSizeH, sizeXY: sSizeHK } = Coords.gridMetrics(blockData.sampleCount);
const offsetTarget = queryGridBox.a[0] + queryGridBox.a[1] * tSizeH + queryGridBox.a[2] * tSizeHK;
const [maxH, maxK, maxL] = Box.dimensions(blockGridBox);
for (let channelIndex = 0, _ii = query.data.header.channels.length; channelIndex < _ii; channelIndex++) {
const target = query.values[channelIndex];
const offsetSource = channelIndex * blockGridBox.a.domain.sampleVolume
+ blockGridBox.a[0] + blockGridBox.a[1] * sSizeH + blockGridBox.a[2] * sSizeHK;
for (let l = 0; l < maxL; l++) {
for (let k = 0; k < maxK; k++) {
for (let h = 0; h < maxH; h++) {
target[offsetTarget + h + k * tSizeH + l * tSizeHK]
= source[offsetSource + h + k * sSizeH + l * sSizeHK];
}
}
}
}
}
function createBlockGridDomain(block: Coords.Grid<'Block'>, grid: Coords.GridDomain<'Data'>): Coords.GridDomain<'BlockGrid'> {
const blockBox = Box.fractionalFromBlock(block);
const origin = blockBox.a;
const dimensions = Coords.sub(blockBox.b, blockBox.a);
const sampleCount = Coords.sampleCounts(dimensions, grid.delta);
return Coords.domain<'BlockGrid'>('BlockGrid', { origin, dimensions, delta: grid.delta, sampleCount });
}
/** Read the block data and fill all the overlaps with the query region. */
async function fillBlock(query: Data.QueryContext.Data, block: Data.QueryBlock) {
const baseBox = Box.fractionalFromBlock(block.coord);
const blockGridDomain = createBlockGridDomain(block.coord, query.samplingInfo.sampling.dataDomain);
const blockData: Data.BlockData = await readBlock(query, block.coord, baseBox);
for (const offset of block.offsets) {
const offsetQueryBox = Box.shift(query.samplingInfo.fractionalBox, offset);
const dataBox = Box.intersect(baseBox, offsetQueryBox);
if (!dataBox) continue;
const offsetDataBox = Box.shift(dataBox, Coords.invert(offset));
const blockGridBox = Box.clampGridToSamples(Box.fractionalToGrid(dataBox, blockGridDomain));
const queryGridBox = Box.clampGridToSamples(Box.fractionalToGrid(offsetDataBox, query.samplingInfo.gridDomain));
fillData(query, blockData, blockGridBox, queryGridBox);
}
}