parser.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /**
  2. * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { Task, RuntimeContext } from 'mol-task';
  7. import { Dsn6File, Dsn6Header } from './schema'
  8. import { ReaderResult as Result } from '../result'
  9. import { FileHandle } from '../../common/file-handle';
  10. import { SimpleBuffer } from 'mol-io/common/simple-buffer';
  11. export const dsn6HeaderSize = 512;
  12. function parseBrixHeader(str: string): Dsn6Header {
  13. return {
  14. xStart: parseInt(str.substr(10, 5)),
  15. yStart: parseInt(str.substr(15, 5)),
  16. zStart: parseInt(str.substr(20, 5)),
  17. xExtent: parseInt(str.substr(32, 5)),
  18. yExtent: parseInt(str.substr(38, 5)),
  19. zExtent: parseInt(str.substr(42, 5)),
  20. xRate: parseInt(str.substr(52, 5)),
  21. yRate: parseInt(str.substr(58, 5)),
  22. zRate: parseInt(str.substr(62, 5)),
  23. xlen: parseFloat(str.substr(73, 10)),
  24. ylen: parseFloat(str.substr(83, 10)),
  25. zlen: parseFloat(str.substr(93, 10)),
  26. alpha: parseFloat(str.substr(103, 10)),
  27. beta: parseFloat(str.substr(113, 10)),
  28. gamma: parseFloat(str.substr(123, 10)),
  29. divisor: parseFloat(str.substr(138, 12)),
  30. summand: parseInt(str.substr(155, 8)),
  31. sigma: parseFloat(str.substr(170, 12))
  32. }
  33. }
  34. function parseDsn6Header(buffer: SimpleBuffer, littleEndian: boolean): Dsn6Header {
  35. const readInt = littleEndian ? (o: number) => buffer.readInt16LE(o * 2) : (o: number) => buffer.readInt16BE(o * 2)
  36. const factor = 1 / readInt(17)
  37. return {
  38. xStart: readInt(0),
  39. yStart: readInt(1),
  40. zStart: readInt(2),
  41. xExtent: readInt(3),
  42. yExtent: readInt(4),
  43. zExtent: readInt(5),
  44. xRate: readInt(6),
  45. yRate: readInt(7),
  46. zRate: readInt(8),
  47. xlen: readInt(9) * factor,
  48. ylen: readInt(10) * factor,
  49. zlen: readInt(11) * factor,
  50. alpha: readInt(12) * factor,
  51. beta: readInt(13) * factor,
  52. gamma: readInt(14) * factor,
  53. divisor: readInt(15) / 100,
  54. summand: readInt(16),
  55. sigma: undefined
  56. }
  57. }
  58. function getBlocks(header: Dsn6Header) {
  59. const { xExtent, yExtent, zExtent } = header
  60. const xBlocks = Math.ceil(xExtent / 8)
  61. const yBlocks = Math.ceil(yExtent / 8)
  62. const zBlocks = Math.ceil(zExtent / 8)
  63. return { xBlocks, yBlocks, zBlocks }
  64. }
  65. export async function readDsn6Header(file: FileHandle): Promise<{ header: Dsn6Header, littleEndian: boolean }> {
  66. const { buffer } = await file.readBuffer(0, dsn6HeaderSize)
  67. const brixStr = String.fromCharCode.apply(null, buffer) as string
  68. const isBrix = brixStr.startsWith(':-)')
  69. const littleEndian = isBrix || buffer.readInt16LE(18 * 2) === 100
  70. const header = isBrix ? parseBrixHeader(brixStr) : parseDsn6Header(buffer, littleEndian)
  71. return { header, littleEndian }
  72. }
  73. export async function parseDsn6Values(header: Dsn6Header, source: Uint8Array, target: Float32Array, littleEndian: boolean) {
  74. if (!littleEndian) {
  75. // even though the values are one byte they need to be swapped like they are 2
  76. SimpleBuffer.flipByteOrderInPlace2(source.buffer)
  77. }
  78. const { divisor, summand, xExtent, yExtent, zExtent } = header
  79. const { xBlocks, yBlocks, zBlocks } = getBlocks(header)
  80. let offset = 0
  81. // loop over blocks
  82. for (let zz = 0; zz < zBlocks; ++zz) {
  83. for (let yy = 0; yy < yBlocks; ++yy) {
  84. for (let xx = 0; xx < xBlocks; ++xx) {
  85. // loop inside block
  86. for (let k = 0; k < 8; ++k) {
  87. const z = 8 * zz + k
  88. for (let j = 0; j < 8; ++j) {
  89. const y = 8 * yy + j
  90. for (let i = 0; i < 8; ++i) {
  91. const x = 8 * xx + i
  92. // check if remaining slice-part contains values
  93. if (x < xExtent && y < yExtent && z < zExtent) {
  94. const idx = ((((x * yExtent) + y) * zExtent) + z)
  95. target[idx] = (source[offset] - summand) / divisor
  96. ++offset
  97. } else {
  98. offset += 8 - i
  99. break
  100. }
  101. }
  102. }
  103. }
  104. }
  105. }
  106. }
  107. }
  108. export function getDsn6Counts(header: Dsn6Header) {
  109. const { xExtent, yExtent, zExtent } = header
  110. const { xBlocks, yBlocks, zBlocks } = getBlocks(header)
  111. const valueCount = xExtent * yExtent * zExtent
  112. const count = xBlocks * 8 * yBlocks * 8 * zBlocks * 8
  113. const elementByteSize = 1
  114. const byteCount = count * elementByteSize
  115. return { count, byteCount, valueCount }
  116. }
  117. async function parseInternal(file: FileHandle, size: number, ctx: RuntimeContext): Promise<Dsn6File> {
  118. await ctx.update({ message: 'Parsing DSN6/BRIX file...' });
  119. const { header, littleEndian } = await readDsn6Header(file)
  120. const { buffer } = await file.readBuffer(dsn6HeaderSize, size - dsn6HeaderSize)
  121. const { valueCount } = getDsn6Counts(header)
  122. const values = new Float32Array(valueCount)
  123. await parseDsn6Values(header, buffer, values, littleEndian)
  124. const result: Dsn6File = { header, values };
  125. return result;
  126. }
  127. export function parseFile(file: FileHandle, size: number) {
  128. return Task.create<Result<Dsn6File>>('Parse DSN6/BRIX', async ctx => {
  129. try {
  130. return Result.success(await parseInternal(file, size, ctx));
  131. } catch (e) {
  132. return Result.error(e);
  133. }
  134. })
  135. }
  136. export function parse(buffer: Uint8Array) {
  137. return parseFile(FileHandle.fromBuffer(SimpleBuffer.fromUint8Array(buffer)), buffer.length)
  138. }