import { mat3, mat4 } from 'gl-matrix'; import Constants from 'vtk.js/Sources/Common/Transform/LandmarkTransform/Constants'; import macro from 'vtk.js/Sources/macros'; import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';
const { Mode } = Constants;
function vtkLandmarkTransform(publicAPI, model) { model.classHierarchy.push('vtkLandmarkTransform');
function update() { mat4.identity(model.matrix); const N_PTS = model.sourceLandmark.getNumberOfPoints(); if ( model.targetLandmark.getNumberOfPoints() !== N_PTS || model.sourceLandmark === null || model.targetLandmark === null || N_PTS === 0 ) { console.error('Error : Bad inputs of vtkLandmarkTransform'); return model.matrix; }
const sourceCentroid = [0, 0, 0]; const targetCentroid = [0, 0, 0]; const p = [0, 0, 0]; for (let i = 0; i < N_PTS; i++) { model.sourceLandmark.getPoint(i, p); sourceCentroid[0] += p[0]; sourceCentroid[1] += p[1]; sourceCentroid[2] += p[2]; model.targetLandmark.getPoint(i, p); targetCentroid[0] += p[0]; targetCentroid[1] += p[1]; targetCentroid[2] += p[2]; } sourceCentroid[0] /= N_PTS; sourceCentroid[1] /= N_PTS; sourceCentroid[2] /= N_PTS; targetCentroid[0] /= N_PTS; targetCentroid[1] /= N_PTS; targetCentroid[2] /= N_PTS;
if (N_PTS === 1) { mat4.identity(model.matrix); model.matrix[12] = targetCentroid[0] - sourceCentroid[0]; model.matrix[13] = targetCentroid[1] - sourceCentroid[1]; model.matrix[14] = targetCentroid[2] - sourceCentroid[2]; return model.matrix; }
const M = new Float64Array(9); const AAT = new Float64Array(9);
const a = [0, 0, 0]; const b = [0, 0, 0]; let sa = 0.0; let sb = 0.0; for (let pt = 0; pt < N_PTS; pt++) { model.sourceLandmark.getPoint(pt, a); a[0] -= sourceCentroid[0]; a[1] -= sourceCentroid[1]; a[2] -= sourceCentroid[2];
model.targetLandmark.getPoint(pt, b); b[0] -= targetCentroid[0]; b[1] -= targetCentroid[1]; b[2] -= targetCentroid[2];
for (let i = 0; i < 3; i++) { M[3 * 0 + i] += a[i] * b[0]; M[3 * 1 + i] += a[i] * b[1]; M[3 * 2 + i] += a[i] * b[2];
if (model.mode === Mode.AFFINE) { AAT[3 * 0 + i] += a[i] * a[0]; AAT[3 * 1 + i] += a[i] * a[1]; AAT[3 * 2 + i] += a[i] * a[2]; } } sa += a[0] * a[0] + a[1] * a[1] + a[2] * a[2]; sb += b[0] * b[0] + b[1] * b[1] + b[2] * b[2]; }
if (model.mode === Mode.AFFINE) { mat3.invert(AAT, AAT);
mat3.multiply(M, AAT, M);
for (let i = 0; i < 3; ++i) { for (let j = 0; j < 3; ++j) { model.matrix[4 * j + i] = M[4 * i + j]; } } } else { const scale = Math.sqrt(sb / sa);
const N = new Float64Array(16); N[0] = M[0] + M[4] + M[8]; N[5] = M[0] - M[4] - M[8]; N[10] = -M[0] + M[4] - M[8]; N[15] = -M[0] - M[4] + M[8]; N[4] = N[1] = M[7] - M[5]; N[8] = N[2] = M[2] - M[6]; N[12] = N[3] = M[3] - M[1];
N[9] = N[6] = M[3] + M[1]; N[13] = N[7] = M[2] + M[6]; N[14] = N[11] = M[7] + M[5];
const eigenVectors = [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, ]; const eigenValues = [0.0, 0.0, 0.0, 0.0];
vtkMath.jacobiN(N, 4, eigenValues, eigenVectors);
let w; let x; let y; let z;
if (eigenValues[0] === eigenValues[1] || N_PTS === 2) { const s0 = [0, 0, 0]; const t0 = [0, 0, 0]; const s1 = [0, 0, 0]; const t1 = [0, 0, 0]; model.sourceLandmark.getPoint(0, s0); model.targetLandmark.getPoint(0, t0); model.sourceLandmark.getPoint(1, s1); model.targetLandmark.getPoint(1, t1);
const ds = [0, 0, 0]; const dt = [0, 0, 0]; let rs = 0; let rt = 0; for (let i = 0; i < 3; i++) { ds[i] = s1[i] - s0[i]; rs = ds[i] * ds[i] + rs; dt[i] = t1[i] - t0[i]; rt = dt[i] * dt[i] + rt; }
rs = Math.sqrt(rs); ds[0] /= rs; ds[1] /= rs; ds[2] /= rs; rt = Math.sqrt(rt); dt[0] /= rt; dt[1] /= rt; dt[2] /= rt;
w = ds[0] * dt[0] + ds[1] * dt[1] + ds[2] * dt[2]; x = ds[1] * dt[2] - ds[2] * dt[1]; y = ds[2] * dt[0] - ds[0] * dt[2]; z = ds[0] * dt[1] - ds[1] * dt[0];
let r = Math.sqrt(x * x + y * y + z * z); const theta = Math.atan2(r, w);
w = Math.cos(theta / 2); if (r !== 0) { r = Math.sin(theta / 2) / r; x *= r; y *= r; z *= r; } else { vtkMath.perpendiculars(ds, dt, 0, 0); r = Math.sin(theta / 2); x = dt[0] * r; y = dt[1] * r; z = dt[2] * r; } } else { w = eigenVectors[0]; x = eigenVectors[4]; y = eigenVectors[8]; z = eigenVectors[12]; }
const ww = w * w; const wx = w * x; const wy = w * y; const wz = w * z;
const xx = x * x; const yy = y * y; const zz = z * z;
const xy = x * y; const xz = x * z; const yz = y * z;
model.matrix[0] = ww + xx - yy - zz; model.matrix[1] = 2.0 * (wz + xy); model.matrix[2] = 2.0 * (-wy + xz);
model.matrix[4] = 2.0 * (-wz + xy); model.matrix[5] = ww - xx + yy - zz; model.matrix[6] = 2.0 * (wx + yz);
model.matrix[8] = 2.0 * (wy + xz); model.matrix[9] = 2.0 * (-wx + yz); model.matrix[10] = ww - xx - yy + zz;
if (model.mode !== Mode.RIGID_BODY) { for (let i = 0; i < 3; i++) { model.matrix[4 * 0 + i] = model.matrix[4 * 0 + i] * scale; model.matrix[4 * 1 + i] = model.matrix[4 * 1 + i] * scale; model.matrix[4 * 2 + i] = model.matrix[4 * 2 + i] * scale; } } }
const sx = model.matrix[0] * sourceCentroid[0] + model.matrix[4] * sourceCentroid[1] + model.matrix[8] * sourceCentroid[2]; const sy = model.matrix[1] * sourceCentroid[0] + model.matrix[5] * sourceCentroid[1] + model.matrix[9] * sourceCentroid[2]; const sz = model.matrix[2] * sourceCentroid[0] + model.matrix[6] * sourceCentroid[1] + model.matrix[10] * sourceCentroid[2];
model.matrix[12] = targetCentroid[0] - sx; model.matrix[13] = targetCentroid[1] - sy; model.matrix[14] = targetCentroid[2] - sz;
model.matrix[3] = 0.0; model.matrix[7] = 0.0; model.matrix[11] = 0.0; model.matrix[15] = 1.0;
return model.matrix; }
publicAPI.update = update; }
const DEFAULT_VALUES = { mode: Mode.SIMILARITY, };
export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues); macro.obj(publicAPI, model);
model.matrix = mat4.identity(new Float64Array(16));
macro.setGet(publicAPI, model, ['sourceLandmark', 'targetLandmark', 'mode']); macro.get(publicAPI, model, ['matrix']);
vtkLandmarkTransform(publicAPI, model); }
export const newInstance = macro.newInstance(extend, 'vtkLandmarkTransform');
export default { newInstance, extend, ...Constants };
|