Prepare the sources for switching the linter from ESLint to Biome. - Use template literals instead of string concatenation - Use the ** operator instead of Math.pow - Drop a redundant continue and annotate an untyped let - Remove the eslint-disable-line comments
639 lines
20 KiB
TypeScript
639 lines
20 KiB
TypeScript
export interface M3DHeader {
|
|
magic: number;
|
|
x: number;
|
|
y: number;
|
|
z: number;
|
|
filename: string;
|
|
subject: string;
|
|
direction: string;
|
|
date: number;
|
|
fov: number;
|
|
weight: number;
|
|
ax: number;
|
|
ay: number;
|
|
az: number;
|
|
ox: number;
|
|
oy: number;
|
|
oz: number;
|
|
dlen: number;
|
|
dth: number;
|
|
memo: string;
|
|
}
|
|
|
|
interface M3DImageRenderingContext {
|
|
a1: number;
|
|
a2: number;
|
|
a3: number;
|
|
b1: number;
|
|
b2: number;
|
|
b3: number;
|
|
c1: number;
|
|
c2: number;
|
|
c3: number;
|
|
d1: number;
|
|
d2: number;
|
|
d3: number;
|
|
ra1: number;
|
|
ra1_2: number;
|
|
ra2: number;
|
|
ra2_2: number;
|
|
ra3: number;
|
|
ra3_2: number;
|
|
rb1: number;
|
|
rb1_2: number;
|
|
rb2: number;
|
|
rb2_2: number;
|
|
rb3: number;
|
|
rb3_2: number;
|
|
rc1: number;
|
|
rc1_2: number;
|
|
rc2: number;
|
|
rc2_2: number;
|
|
rc3: number;
|
|
rc3_2: number;
|
|
rd1: number;
|
|
rd2: number;
|
|
rd3: number;
|
|
}
|
|
|
|
/* MeshLength unit is same as fov unit */
|
|
const MeshLength = 10;
|
|
enum DrawingLineType {
|
|
VLine = 0,
|
|
VDottedLine = 1,
|
|
HLine = 2,
|
|
HDottedLine = 3,
|
|
}
|
|
const LineBrightness = 200;
|
|
const DottedLineBrightness = 128;
|
|
const EXACTSWITCH: 0 | 1 = 1;
|
|
const MaxIntensity = 200;
|
|
|
|
class M3D {
|
|
private data: ArrayBuffer;
|
|
private header: M3DHeader | null = null;
|
|
private xposView: Int32Array | null = null;
|
|
private zposView: Int32Array | null = null;
|
|
private yposView: Int32Array | null = null;
|
|
private img3dView: Uint8ClampedArray | null = null;
|
|
private imageSize = 0; // imagesize size : max (size_x, size_y, size_z)
|
|
private centerX = 0; // camera coordinates origin : x
|
|
private centerY = 0; // camera coordinates origin : y
|
|
private centerZ = 0; // camera coordinates origin : z
|
|
private ox = 0; // object coordinates origin : x
|
|
private oy = 0; // object coordinates origin : y
|
|
private oz = 0; // object coordinates origin : z
|
|
|
|
constructor(data: ArrayBuffer) {
|
|
this.data = data;
|
|
this._initialize();
|
|
}
|
|
|
|
isBigEndian(): boolean {
|
|
return new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x11;
|
|
}
|
|
|
|
getHeader(): M3DHeader | null {
|
|
return this.header;
|
|
}
|
|
|
|
drawImage(
|
|
canvas: HTMLCanvasElement,
|
|
ax: number,
|
|
ay: number,
|
|
az: number,
|
|
cutdepth: number,
|
|
th: number,
|
|
is3d: boolean,
|
|
mesh: boolean,
|
|
): boolean {
|
|
const irc: M3DImageRenderingContext = {
|
|
a1: 0,
|
|
a2: 0,
|
|
a3: 0,
|
|
b1: 0,
|
|
b2: 0,
|
|
b3: 0,
|
|
c1: 0,
|
|
c2: 0,
|
|
c3: 0,
|
|
d1: 0,
|
|
d2: 0,
|
|
d3: 0,
|
|
ra1: 0,
|
|
ra1_2: 0,
|
|
ra2: 0,
|
|
ra2_2: 0,
|
|
ra3: 0,
|
|
ra3_2: 0,
|
|
rb1: 0,
|
|
rb1_2: 0,
|
|
rb2: 0,
|
|
rb2_2: 0,
|
|
rb3: 0,
|
|
rb3_2: 0,
|
|
rc1: 0,
|
|
rc1_2: 0,
|
|
rc2: 0,
|
|
rc2_2: 0,
|
|
rc3: 0,
|
|
rc3_2: 0,
|
|
rd1: 0,
|
|
rd2: 0,
|
|
rd3: 0,
|
|
};
|
|
// console.log(ax, ay, az, cutdepth, th, dim, mesh)
|
|
if (this.header === null) {
|
|
return false;
|
|
}
|
|
|
|
canvas.width = this.imageSize;
|
|
canvas.height = this.imageSize;
|
|
const ctx = canvas.getContext('2d');
|
|
if (ctx === null) {
|
|
return false;
|
|
}
|
|
ctx.fillStyle = 'rgb(0, 0, 0)';
|
|
ctx.fillRect(0, 0, this.imageSize, this.imageSize);
|
|
const img = ctx.getImageData(0, 0, this.imageSize, this.imageSize);
|
|
|
|
this._setMatrix(ax, ay, az, irc);
|
|
if (is3d) {
|
|
const ddimg = [...Array<number>(this.imageSize * this.imageSize)].map(() => 0);
|
|
this._makeDIMG(img, cutdepth, th, ddimg, irc);
|
|
this._makeShadingImage(img, ddimg);
|
|
} else {
|
|
this._makeSliceImage(img, cutdepth, irc);
|
|
}
|
|
if (mesh) {
|
|
this._drawMesh(img, irc);
|
|
}
|
|
ctx.putImageData(img, 0, 0);
|
|
return true;
|
|
}
|
|
|
|
private _makeDIMG(img: ImageData, cutdepth: number, th: number, ddimg: number[], irc: M3DImageRenderingContext) {
|
|
if (this.header === null) {
|
|
return;
|
|
}
|
|
const pdimg = [...Array<number>(this.imageSize * this.imageSize)].map(() => 0);
|
|
const sw = th > this.header.dth ? 1 : th < this.header.dth ? 2 : EXACTSWITCH;
|
|
switch (sw) {
|
|
case 0: {
|
|
this._transSurface(cutdepth, pdimg, irc);
|
|
let pos = 0;
|
|
for (let j = 0; j < this.imageSize; j++) {
|
|
for (let i = 0; i < this.imageSize; i++, pos++) {
|
|
ddimg[pos] = pdimg[pos];
|
|
const pix = this._getPixel(i, j, cutdepth, irc);
|
|
if (pix > th) {
|
|
this._drawPixel(img, pos, pix, pix, pix);
|
|
ddimg[pos] = cutdepth;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 1: {
|
|
this._transSurface(cutdepth, pdimg, irc);
|
|
let pos = 0;
|
|
for (let y = 0; y < this.imageSize; y++) {
|
|
for (let x = 0; x < this.imageSize; x++, pos++) {
|
|
let pdPos = (ddimg[pos] = pdimg[pos]);
|
|
if (pdPos === 0) {
|
|
continue;
|
|
}
|
|
const pix = this._getPixel(x, y, cutdepth, irc);
|
|
if (pix > th) {
|
|
this._drawPixel(img, pos, pix, pix, pix);
|
|
ddimg[pos] = cutdepth;
|
|
continue;
|
|
}
|
|
if (pdPos < cutdepth) {
|
|
pdPos = cutdepth;
|
|
}
|
|
ddimg[pos] = 0;
|
|
for (let z = pdPos; z < this.imageSize; z++) {
|
|
const pix = this._getPixel(x, y, z, irc);
|
|
if (pix > th) {
|
|
ddimg[pos] = z;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 2: {
|
|
let pos = 0;
|
|
for (let y = 0; y < this.imageSize; y++) {
|
|
for (let x = 0; x < this.imageSize; x++, pos++) {
|
|
ddimg[pos] = 0;
|
|
for (let z = cutdepth; z < this.imageSize; z++) {
|
|
const pix = this._getPixel(x, y, z, irc);
|
|
if (pix > th) {
|
|
if (z === cutdepth) {
|
|
this._drawPixel(img, pos, pix, pix, pix);
|
|
} else {
|
|
ddimg[pos] = z;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private _makeShadingImage(img: ImageData, ddimg: number[]) {
|
|
let pos = 0;
|
|
for (let x = 0; x < this.imageSize; x++) {
|
|
for (let y = 0; y < this.imageSize; y++, pos++) {
|
|
const base = pos * 4;
|
|
if (img.data[base] === 0) {
|
|
let timg2d = 0;
|
|
if (x < this.imageSize - 2 && y < this.imageSize - 2) {
|
|
if (ddimg[pos] !== 0) {
|
|
const nx =
|
|
(ddimg[pos] + ddimg[pos + this.imageSize] - ddimg[pos + 2] - ddimg[pos + 2 + this.imageSize]) / 4.0;
|
|
const ny =
|
|
(ddimg[pos] + ddimg[pos + 1] - ddimg[pos + this.imageSize * 2] - ddimg[pos + 1 + this.imageSize * 2]) /
|
|
4.0;
|
|
const n = Math.sqrt(nx * nx + ny * ny + 1);
|
|
timg2d = Math.round(MaxIntensity / n);
|
|
}
|
|
}
|
|
this._drawPixel(img, pos, timg2d, timg2d, timg2d);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private _makeSliceImage(img: ImageData, cutdepth: number, irc: M3DImageRenderingContext) {
|
|
let pos = 0;
|
|
for (let y = 0; y < this.imageSize; y++) {
|
|
for (let x = 0; x < this.imageSize; x++, pos++) {
|
|
const pix = this._getPixel(x, y, cutdepth, irc);
|
|
this._drawPixel(img, pos, pix, pix, pix);
|
|
}
|
|
}
|
|
}
|
|
|
|
private _drawMesh(img: ImageData, irc: M3DImageRenderingContext) {
|
|
let origx: number;
|
|
let origy: number;
|
|
if (this.header === null) {
|
|
return;
|
|
}
|
|
if (this.header.ox !== 0 && this.header.oy !== 0) {
|
|
origx = this.header.ox * irc.ra1 + this.header.oy * irc.rb1 + this.header.oz * irc.rc1 + irc.rd1;
|
|
origy = this.header.ox * irc.ra2 + this.header.oy * irc.rb2 + this.header.oz * irc.rc2 + irc.rd2;
|
|
} else {
|
|
origx = this.centerX;
|
|
origy = this.centerY;
|
|
}
|
|
origx = this._fround(origx, 5);
|
|
origy = this._fround(origy, 5);
|
|
|
|
const band = (this.header.x * MeshLength) / this.header.fov;
|
|
const r = 0;
|
|
const g = 0;
|
|
const b = LineBrightness;
|
|
const sr = 0;
|
|
const sg = 0;
|
|
const sb = DottedLineBrightness;
|
|
this._drawLine(img, Math.round(origx), r, g, b, DrawingLineType.VLine);
|
|
this._drawLine(img, Math.round(origy), r, g, b, DrawingLineType.HLine);
|
|
for (let pos = Math.round(origx) + band; pos < this.imageSize; pos += band) {
|
|
this._drawLine(img, Math.round(pos), sr, sg, sb, DrawingLineType.VDottedLine);
|
|
}
|
|
for (let pos = Math.round(origx) - band; pos >= 0; pos -= band) {
|
|
this._drawLine(img, Math.round(pos), sr, sg, sb, DrawingLineType.VDottedLine);
|
|
}
|
|
for (let pos = Math.round(origy) + band; pos < this.imageSize; pos += band) {
|
|
this._drawLine(img, Math.round(pos), sr, sg, sb, DrawingLineType.HDottedLine);
|
|
}
|
|
for (let pos = Math.round(origy) - band; pos >= 0; pos -= band) {
|
|
this._drawLine(img, Math.round(pos), sr, sg, sb, DrawingLineType.HDottedLine);
|
|
}
|
|
}
|
|
|
|
private _drawLine(img: ImageData, value: number, r: number, g: number, b: number, type: DrawingLineType) {
|
|
switch (type) {
|
|
case DrawingLineType.VLine: {
|
|
let pos = value;
|
|
for (let y = 0; y < this.imageSize; y++, pos += this.imageSize) {
|
|
this._drawPixel(img, pos, r, g, b);
|
|
}
|
|
break;
|
|
}
|
|
case DrawingLineType.VDottedLine: {
|
|
let pos = value;
|
|
for (let y = 0; y < this.imageSize; y += 2, pos += this.imageSize * 2) {
|
|
this._drawPixel(img, pos, r, g, b);
|
|
}
|
|
break;
|
|
}
|
|
case DrawingLineType.HLine: {
|
|
let pos = value * this.imageSize;
|
|
for (let x = 0; x < this.imageSize; x++, pos++) {
|
|
this._drawPixel(img, pos, r, g, b);
|
|
}
|
|
break;
|
|
}
|
|
case DrawingLineType.HDottedLine: {
|
|
let pos = value * this.imageSize;
|
|
for (let x = 0; x < this.imageSize; x += 2, pos += 2) {
|
|
this._drawPixel(img, pos, r, g, b);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
private _drawPixel(img: ImageData, pos: number, r: number, g: number, b: number) {
|
|
let base = pos * 4;
|
|
img.data[base++] = r;
|
|
img.data[base++] = g;
|
|
img.data[base++] = b;
|
|
img.data[base] = 255;
|
|
}
|
|
|
|
private _getPixel(x: number, y: number, z: number, irc: M3DImageRenderingContext): number {
|
|
const len = [...Array<number>(8)].map(() => 0);
|
|
const ratio = [...Array<number>(8)].map(() => 0);
|
|
|
|
const tx = x * irc.a1 + y * irc.b1 + z * irc.c1 + irc.d1;
|
|
const ty = x * irc.a2 + y * irc.b2 + z * irc.c2 + irc.d2;
|
|
const tz = x * irc.a3 + y * irc.b3 + z * irc.c3 + irc.d3;
|
|
|
|
let ix = Math.floor(tx);
|
|
let iy = Math.floor(ty);
|
|
let iz = Math.floor(tz);
|
|
|
|
if (this.header === null || this.img3dView === null) {
|
|
return 0;
|
|
}
|
|
if (ix < 0 || ix + 1 >= this.header.x || iy < 0 || iy + 1 >= this.header.y || iz < 0 || iz + 1 >= this.header.z) {
|
|
return 0;
|
|
}
|
|
|
|
const ax1 = Math.abs(tx - ix);
|
|
const ax2 = Math.abs(1 - ax1);
|
|
const ay1 = Math.abs(ty - iy);
|
|
const ay2 = Math.abs(1 - ay1);
|
|
const az1 = Math.abs(tz - iz);
|
|
const az2 = Math.abs(1 - az1);
|
|
|
|
let sumlen1 = 0;
|
|
for (let i = 0; i < 8; i++) {
|
|
len[i] = i & 1 ? ax2 : ax1;
|
|
len[i] += i & 2 ? ay2 : ay1;
|
|
len[i] += i & 4 ? az2 : az1;
|
|
if (len[i] < 0.001) {
|
|
ix += i & 1 ? 1 : 0;
|
|
iy += i & 2 ? 1 : 0;
|
|
iz += i & 4 ? 1 : 0;
|
|
return this.img3dView[ix + iy * this.header.x + iz * this.header.x * this.header.y];
|
|
}
|
|
sumlen1 += ratio[i] = 1 / len[i];
|
|
}
|
|
let pos = ix + iy * this.header.x + iz * this.header.x * this.header.y;
|
|
let sumlen2 = this.img3dView[pos] * ratio[0];
|
|
sumlen2 += this.img3dView[pos + 1] * ratio[1];
|
|
sumlen2 += this.img3dView[pos + this.header.x] * ratio[2];
|
|
sumlen2 += this.img3dView[pos + 1 + this.header.x] * ratio[3];
|
|
pos += this.header.x * this.header.y;
|
|
sumlen2 += this.img3dView[pos] * ratio[4];
|
|
sumlen2 += this.img3dView[pos + 1] * ratio[5];
|
|
sumlen2 += this.img3dView[pos + this.header.x] * ratio[6];
|
|
sumlen2 += this.img3dView[pos + 1 + this.header.x] * ratio[7];
|
|
|
|
return Math.round(sumlen2 / sumlen1);
|
|
}
|
|
|
|
private _transSurface(cutdepth: number, pdimg: number[], irc: M3DImageRenderingContext) {
|
|
if (this.header === null || this.xposView === null || this.yposView === null || this.zposView === null) {
|
|
return;
|
|
}
|
|
const ix = [...Array<number>(8)].map(() => 0);
|
|
const iy = [...Array<number>(8)].map(() => 0);
|
|
const iz = [...Array<number>(8)].map(() => 0);
|
|
for (let i = 0; i < this.header.dlen; i++) {
|
|
const x = this.xposView[i];
|
|
const y = this.yposView[i];
|
|
const z = this.zposView[i];
|
|
ix[0] = x * irc.ra1 + y * irc.rb1 + z * irc.rc1 + irc.rd1;
|
|
iy[0] = x * irc.ra2 + y * irc.rb2 + z * irc.rc2 + irc.rd2;
|
|
iz[0] = x * irc.ra3 + y * irc.rb3 + z * irc.rc3 + irc.rd3;
|
|
ix[1] = ix[0] + irc.ra1_2;
|
|
iy[1] = iy[0] + irc.ra2_2;
|
|
iz[1] = iz[0] + irc.ra3_2;
|
|
ix[2] = ix[0] + irc.rb1_2;
|
|
iy[2] = iy[0] + irc.rb2_2;
|
|
iz[2] = iz[0] + irc.rb3_2;
|
|
ix[3] = ix[0] + irc.rc1_2;
|
|
iy[3] = iy[0] + irc.rc2_2;
|
|
iz[3] = iz[0] + irc.rc3_2;
|
|
ix[4] = ix[1] + irc.rb1_2;
|
|
iy[4] = iy[1] + irc.rb2_2;
|
|
iz[4] = iz[1] + irc.rb3_2;
|
|
ix[5] = ix[1] + irc.rc1_2;
|
|
iy[5] = iy[1] + irc.rc2_2;
|
|
iz[5] = iz[1] + irc.rc3_2;
|
|
ix[6] = ix[2] + irc.rc1_2;
|
|
iy[6] = iy[2] + irc.rc2_2;
|
|
iz[6] = iz[2] + irc.rc3_2;
|
|
ix[7] = ix[4] + irc.rc1_2;
|
|
iy[7] = iy[4] + irc.rc2_2;
|
|
iz[7] = iz[4] + irc.rc3_2;
|
|
for (let j = 0; j < 8; j++) {
|
|
const xi = Math.round(ix[j]);
|
|
const yi = Math.round(iy[j]);
|
|
if (
|
|
xi < 0 ||
|
|
xi >= this.imageSize ||
|
|
yi < 0 ||
|
|
yi >= this.imageSize ||
|
|
iz[j] < cutdepth ||
|
|
iz[j] >= this.imageSize
|
|
) {
|
|
continue;
|
|
}
|
|
const pos = xi + yi * this.imageSize;
|
|
if (pdimg[pos] === 0 || pdimg[pos] > iz[j]) {
|
|
pdimg[pos] = iz[j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private _setMatrix(x: number, y: number, z: number, irc: M3DImageRenderingContext) {
|
|
const xtr = [...Array<number>(9)].map(() => 0);
|
|
const ytr = [...Array<number>(9)].map(() => 0);
|
|
const ztr = [...Array<number>(9)].map(() => 0);
|
|
const tmp = [...Array<number>(9)].map(() => 0);
|
|
const tr = [...Array<number>(9)].map(() => 0);
|
|
this._setXtr(xtr, (x * Math.PI) / 180.0);
|
|
this._setYtr(ytr, (y * Math.PI) / 180.0);
|
|
this._setZtr(ztr, (z * Math.PI) / 180.0);
|
|
this._multi(ytr, xtr, tmp);
|
|
this._multi(ztr, tmp, tr);
|
|
irc.a1 = tr[0];
|
|
irc.b1 = tr[1];
|
|
irc.c1 = tr[2];
|
|
irc.a2 = tr[3];
|
|
irc.b2 = tr[4];
|
|
irc.c2 = tr[5];
|
|
irc.a3 = tr[6];
|
|
irc.b3 = tr[7];
|
|
irc.c3 = tr[8];
|
|
irc.d1 = -irc.a1 * this.centerX - irc.b1 * this.centerY - irc.c1 * this.centerZ + this.ox;
|
|
irc.d2 = -irc.a2 * this.centerX - irc.b2 * this.centerY - irc.c2 * this.centerZ + this.oy;
|
|
irc.d3 = -irc.a3 * this.centerX - irc.b3 * this.centerY - irc.c3 * this.centerZ + this.oz;
|
|
|
|
this._setXtr(xtr, (-x * Math.PI) / 180.0);
|
|
this._setYtr(ytr, (-y * Math.PI) / 180.0);
|
|
this._setZtr(ztr, (-z * Math.PI) / 180.0);
|
|
this._multi(ytr, ztr, tmp);
|
|
this._multi(xtr, tmp, tr);
|
|
irc.ra1 = tr[0];
|
|
irc.rb1 = tr[1];
|
|
irc.rc1 = tr[2];
|
|
irc.ra2 = tr[3];
|
|
irc.rb2 = tr[4];
|
|
irc.rc2 = tr[5];
|
|
irc.ra3 = tr[6];
|
|
irc.rb3 = tr[7];
|
|
irc.rc3 = tr[8];
|
|
irc.rd1 = -irc.ra1 * this.ox - irc.rb1 * this.oy - irc.rc1 * this.oz + this.centerX;
|
|
irc.rd2 = -irc.ra2 * this.ox - irc.rb2 * this.oy - irc.rc2 * this.oz + this.centerY;
|
|
irc.rd3 = -irc.ra3 * this.ox - irc.rb3 * this.oy - irc.rc3 * this.oz + this.centerZ;
|
|
irc.ra1_2 = irc.ra1 / 2;
|
|
irc.rb1_2 = irc.rb1 / 2;
|
|
irc.rc1_2 = irc.rc1 / 2;
|
|
irc.ra2_2 = irc.ra2 / 2;
|
|
irc.rb2_2 = irc.rb2 / 2;
|
|
irc.rc2_2 = irc.rc2 / 2;
|
|
irc.ra3_2 = irc.ra3 / 2;
|
|
irc.rb3_2 = irc.rb3 / 2;
|
|
irc.rc3_2 = irc.rc3 / 2;
|
|
}
|
|
|
|
private _setXtr(xtr: number[], ang: number) {
|
|
xtr[0] = 1;
|
|
xtr[1] = 0;
|
|
xtr[2] = 0;
|
|
xtr[3] = 0;
|
|
xtr[4] = Math.cos(ang);
|
|
xtr[5] = -Math.sin(ang);
|
|
xtr[6] = 0;
|
|
xtr[7] = Math.sin(ang);
|
|
xtr[8] = Math.cos(ang);
|
|
}
|
|
|
|
private _setYtr(ytr: number[], ang: number) {
|
|
ytr[0] = Math.cos(ang);
|
|
ytr[1] = 0;
|
|
ytr[2] = Math.sin(ang);
|
|
ytr[3] = 0;
|
|
ytr[4] = 1;
|
|
ytr[5] = 0;
|
|
ytr[6] = -Math.sin(ang);
|
|
ytr[7] = 0;
|
|
ytr[8] = Math.cos(ang);
|
|
}
|
|
|
|
private _setZtr(ztr: number[], ang: number) {
|
|
ztr[0] = Math.cos(ang);
|
|
ztr[1] = -Math.sin(ang);
|
|
ztr[2] = 0;
|
|
ztr[3] = Math.sin(ang);
|
|
ztr[4] = Math.cos(ang);
|
|
ztr[5] = 0;
|
|
ztr[6] = 0;
|
|
ztr[7] = 0;
|
|
ztr[8] = 1;
|
|
}
|
|
|
|
private _multi(l: number[], r: number[], a: number[]) {
|
|
a[0] = l[0] * r[0] + l[1] * r[3] + l[2] * r[6];
|
|
a[3] = l[3] * r[0] + l[4] * r[3] + l[5] * r[6];
|
|
a[6] = l[6] * r[0] + l[7] * r[3] + l[8] * r[6];
|
|
a[1] = l[0] * r[1] + l[1] * r[4] + l[2] * r[7];
|
|
a[4] = l[3] * r[1] + l[4] * r[4] + l[5] * r[7];
|
|
a[7] = l[6] * r[1] + l[7] * r[4] + l[8] * r[7];
|
|
a[2] = l[0] * r[2] + l[1] * r[5] + l[2] * r[8];
|
|
a[5] = l[3] * r[2] + l[4] * r[5] + l[5] * r[8];
|
|
a[8] = l[6] * r[2] + l[7] * r[5] + l[8] * r[8];
|
|
}
|
|
|
|
private _fround(x: number, n: number) {
|
|
return Math.round(x * 10 ** n) / 10 ** n;
|
|
}
|
|
|
|
private _initialize(): void {
|
|
const view = new DataView(this.data);
|
|
const magic = view.getInt32(0, true);
|
|
const isLittleEndianFile = magic === 0x4d523344;
|
|
if (!isLittleEndianFile && magic !== 0x4433524d) {
|
|
console.log('invalid data format, this data is not m3d format');
|
|
return;
|
|
}
|
|
let offset = 0;
|
|
const decoder = new TextDecoder('utf-8');
|
|
this.header = {
|
|
magic: view.getInt32(offset, isLittleEndianFile),
|
|
x: view.getInt32((offset += 4), isLittleEndianFile),
|
|
y: view.getInt32((offset += 4), isLittleEndianFile),
|
|
z: view.getInt32((offset += 4), isLittleEndianFile),
|
|
filename: decoder.decode(new Uint8Array(this.data, (offset += 4), 256)).replace(/\0+$/, ''),
|
|
subject: decoder.decode(new Uint8Array(this.data, (offset += 256), 256)).replace(/\0+$/, ''),
|
|
direction: decoder.decode(new Uint8Array(this.data, (offset += 256), 4)).replace(/\0+$/, ''),
|
|
date: view.getUint32((offset += 4), isLittleEndianFile),
|
|
fov: view.getFloat64((offset += 4), isLittleEndianFile),
|
|
weight: view.getFloat64((offset += 8), isLittleEndianFile),
|
|
ax: view.getFloat64((offset += 8), isLittleEndianFile),
|
|
ay: view.getFloat64((offset += 8), isLittleEndianFile),
|
|
az: view.getFloat64((offset += 8), isLittleEndianFile),
|
|
ox: view.getFloat64((offset += 8), isLittleEndianFile),
|
|
oy: view.getFloat64((offset += 8), isLittleEndianFile),
|
|
oz: view.getFloat64((offset += 8), isLittleEndianFile),
|
|
dlen: view.getInt32((offset += 8), isLittleEndianFile),
|
|
dth: view.getInt32((offset += 4), isLittleEndianFile),
|
|
memo: decoder.decode(new Uint8Array(this.data, (offset += 4), 256)).replace(/\0+$/, ''),
|
|
};
|
|
// swap byte order if endian is not matched
|
|
if ((this.isBigEndian() && isLittleEndianFile) || (!this.isBigEndian() && !isLittleEndianFile)) {
|
|
const start = 864;
|
|
const end = start + this.header.dlen * 4 * 3;
|
|
for (let i = start; i < end; i += 4) {
|
|
const data = new Uint8Array(this.data, i, 4);
|
|
data.reverse();
|
|
}
|
|
}
|
|
this.xposView = new Int32Array(this.data, 864, this.header.dlen);
|
|
this.yposView = new Int32Array(this.data, 864 + this.header.dlen * 4, this.header.dlen);
|
|
this.zposView = new Int32Array(this.data, 864 + this.header.dlen * 8, this.header.dlen);
|
|
this.img3dView = new Uint8ClampedArray(
|
|
this.data,
|
|
864 + this.header.dlen * 12,
|
|
this.header.x * this.header.y * this.header.z,
|
|
);
|
|
// set image generation offset
|
|
this.imageSize = Math.ceil(Math.max(this.header.x, this.header.y, this.header.z) * Math.sqrt(3.0)) + 1;
|
|
this.centerX = (this.imageSize - 1) / 2.0;
|
|
this.centerY = (this.imageSize - 1) / 2.0;
|
|
this.centerZ = (this.imageSize - 1) / 2.0;
|
|
this.ox = (this.header.x - 1) / 2.0;
|
|
this.oy = (this.header.y - 1) / 2.0;
|
|
this.oz = (this.header.z - 1) / 2.0;
|
|
}
|
|
}
|
|
|
|
export default M3D;
|