@galaxyproject/nora
Version:
NORA Medical Imaging Viewer
1,117 lines (931 loc) • 27.8 kB
JavaScript
try {
importScripts('DPX_sharedBackendFuns.js');
importScripts('KMiscFuns.js');
importScripts('KView/KMedImageViewer.js');
importScripts('kmath.js');
importScripts('KTools/KAtlasTool.js');
}
catch (err)
{
}
self.addEventListener('message', function(e) {
var execObj = e.data;
if (execObj.msg == 'kill')
{
self.close();
}
if (execObj.func == 'createFiberVisitMap')
{
if (execObj.lines == undefined)
execObj.lines = self.lines;
else
self.lines = execObj.lines;
createFiberVisitMap(execObj,function(perc){self.postMessage({msg:'rendering visits '+(100*perc).toFixed(0)+"%"}) })
self.postMessage({msg:'done',execObj:execObj},[execObj.buffer]);
}
else if (execObj.func == 'createISOSurf')
{
var res = computeIsoSurf(execObj,function(perc){self.postMessage({msg:'rendering isosurface '+(100*perc).toFixed(0)+"%"}) })
self.postMessage({msg:'done',execObj:res},[]);
}
else if (execObj.func == 'atlasToRoi')
{
atlasToRoi(execObj,function(perc){self.postMessage({msg:'rendering '+(100*perc).toFixed(0)+"%"}) })
self.postMessage({msg:'done',execObj:{buffer:execObj.img.buffer}},[execObj.img.buffer]);
}
else if (execObj.func == 'RoiToAtlas')
{
RoiToAtlas(execObj,function(perc){self.postMessage({msg:'rendering '+(100*perc).toFixed(0)+"%"}) })
self.postMessage({msg:'done',execObj:{buffer:execObj.atlas.buffer}},[execObj.atlas.buffer]);
}
else if (execObj.func == 'fillholes')
{
var res = fillholes(execObj.data,execObj.size)
self.postMessage({msg:'done',execObj:{buf:execObj.buf,changedPoints:res.changedPoints}},[execObj.buf]);
}
else if (execObj.func == 'removesalt')
{
var changedPoints = removesalt(execObj.data,execObj.size,execObj.threshold)
self.postMessage({msg:'done',execObj:{buf:execObj.buf,changedPoints:changedPoints}},[execObj.buf]);
}
else if (execObj.func == 'invert')
{
var tot = 1
for (var k = 0; k < execObj.size.length;k++)
tot *= execObj.size[k]
var changedPoints = [];
for (var k = 0; k < tot;k++)
{
if (execObj.data[k] > 0)
execObj.data[k] = 0;
else
execObj.data[k] = 1;
changedPoints.push(k);
}
self.postMessage({msg:'done',execObj:{buf:execObj.buf,changedPoints:changedPoints}},[execObj.buf]);
}
else if (execObj.func == 'clean')
{
var tot = 1
for (var k = 0; k < execObj.size.length;k++)
tot *= execObj.size[k]
for (var k = 0; k < tot;k++)
if (execObj.data[k] > 0)
execObj.data[k] = 1;
self.postMessage({msg:'done',execObj:{buf:execObj.buf,changedPoints:[]}},[execObj.buf]);
}
else if (execObj.func == 'erode')
{
var changedPoints = erode(execObj.data,execObj.size)
self.postMessage({msg:'done',execObj:{buf:execObj.buf,changedPoints:changedPoints}},[execObj.buf]);
}
else if (execObj.func == 'dilate')
{
var changedPoints = dilate(execObj.data,execObj.size)
self.postMessage({msg:'done',execObj:{buf:execObj.buf,changedPoints:changedPoints}},[execObj.buf]);
}
else if (execObj.func == 'opening')
{
var A = erode(execObj.data, execObj.size);
var B = dilate(execObj.data, execObj.size);
A = array_to_setObject(A)
B = array_to_setObject(B)
var C = diff(A,B)
C = union(C,diff(B,A))
var changedPoints = Object.keys(C);
self.postMessage({msg:'done',execObj:{buf:execObj.buf,changedPoints:changedPoints}},[execObj.buf]);
}
else if (execObj.func == 'closing')
{
var A = erode(execObj.data, execObj.size);
var B = dilate(execObj.data, execObj.size);
A = array_to_setObject(A)
B = array_to_setObject(B)
var C = diff(A,B)
C = union(C,diff(B,A))
var changedPoints = Object.keys(C);
self.postMessage({msg:'done',execObj:{buf:execObj.buf,changedPoints:changedPoints}},[execObj.buf]);
}
else if (execObj.func == 'conncomp')
{
var res = bwconncomp(execObj.data, execObj.size, function(x) { return x>0 }, execObj.clustthres)
self.postMessage({msg:'done',execObj:res},[res.labels.buffer]);
}
else if (execObj.func == 'warpimg')
{
warpImage(execObj.def,execObj.Adef,execObj.nii,execObj.interpType)
self.postMessage({msg:'done',execObj:execObj.nii.data},[]);
}
else if (execObj.func == 'renderwarp')
{
var data = renderWarp(execObj.matches,execObj.edges,execObj.sizes,execObj.def_ext)
self.postMessage({msg:'done',execObj:data},[]);
}
else if (execObj.func == 'renderpoly')
{
var polymask = createRectNiiFromPoly(execObj.points,execObj.inplaneres)
var changedPoints=rendersubRoiIntoRoi(execObj.nii,polymask)
self.postMessage({msg:'done',
execObj:{buf:execObj.nii.buffer,data:execObj.nii.data,changedPoints:changedPoints}},[execObj.nii.buffer]);
}
if (!execObj.keepOpen)
self.close();
}, false);
function renderWarp(matches,edges,sizes,def_ext)
{
var sigma = 0.5;
var sigma2 = 1/(sigma*sigma);
var matrix = [];
var target = [];
for (var j = 0; j < matches.length;j++)
{
var x = matches[j].r[0]
var y = matches[j].r[1]
var z = matches[j].r[2]
var row = [];
target.push(matches[j].dif)
for (var k = 0; k < matches.length;k++)
{
var s = [(x-matches[k].r[0]),(y-matches[k].r[1]),(z-matches[k].r[2])]
var d2 = s[0]*s[0]+s[1]*s[1]+s[2]*s[2];
row.push(Math.exp(-d2*sigma2*matches[k].s))
}
matrix.push(row)
}
if (target.length > 0)
{
target = math.Transpose(math.matrix(target))
matrix = math.matrix(matrix)
var dif = []
for(var i=0;i < 3;i++)
{
dif.push(math.mdiv(matrix,math.matrix(target._data[i]))._data)
}
dif = math.Transpose(math.matrix(dif))._data
}
var e = edges._data
var tot = sizes[0]*sizes[1]*sizes[2];
var data = new Float32Array(tot*3)
if (def_ext)
{
var tred = math.multiply(math.inv(def_ext.edges),edges)._data;
var defdef = def_ext;
var defdef_whd = def_ext.widheidep
}
var f = [];
var d = [];
for (var x=0;x < sizes[0];x++)
for (var y=0;y < sizes[1];y++)
for (var z=0;z < sizes[2];z++)
{
var idx = x+sizes[0]*y+sizes[0]*sizes[1]*z
for (var k = 0; k < matches.length;k++)
{
var s = [(x-matches[k].r[0]),(y-matches[k].r[1]),(z-matches[k].r[2])]
var d2 = s[0]*s[0]+s[1]*s[1]+s[2]*s[2];
d[k] = s;
f[k] = Math.exp(-d2*sigma2*matches[k].s)
}
for (var i = 0; i < 3; i++)
{
if (def_ext)
data[idx+i*tot] = trilinInterp(defdef,x,y,z,tred,defdef_whd*i);
else
data[idx+i*tot] = e[i][0]*x + e[i][1]*y + e[i][2]*z + e[i][3]
for (var k = 0;k < matches.length;k++)
{
if (matches[k].dif)
data[idx+i*tot] += f[k]*dif[k][i];
else
data[idx+i*tot] -= 1*f[k]*d[k][i]*voxsz[0];
}
}
}
return data;
}
function warpImage(def,Adef,nii,interpType)
{
var A = math.inv(nii.edges)._data
if (interpType == "roi")
getVal = function(nii,p,A,j)
{
return trilinInterp(nii,p[0],p[1],p[2],A,nii.widheidep*j)>0.5;
}
else if (interpType == "atlas")
getVal = function(nii,p,A,j)
{
return NNInterp(nii,p[0],p[1],p[2],A,nii.widheidep*j);
}
else
getVal = function(nii,p,A,j)
{
return trilinInterp(nii,p[0],p[1],p[2],A,nii.widheidep*j);
}
var data = nii.data
nii.data = data.slice();
var sz = nii.sizes;
for (var z=0;z<sz[2];z++)
for (var y=0;y<sz[1];y++)
for (var x=0;x<sz[0];x++)
{
var p = [trilinInterp(def,x,y,z,Adef,def.widheidep*0),
trilinInterp(def,x,y,z,Adef,def.widheidep*1),
trilinInterp(def,x,y,z,Adef,def.widheidep*2)]
var idx = x+nii.wid*y+nii.widhei*z;
for (var j=0;j<sz[3];j++)
data[idx + j*nii.widheidep] = getVal(nii,p,A,j)
}
nii.data = data;
}
function removesalt(data, size, thres)
{
var res = bwconncomp(data, size , function (x) { return x>0;}, thres , data)
return res.changedPoints;
}
function dilate(data, size)
{
var changedPoints = [];
var tmp = new Uint8Array(data);
var w = size[0];
var h = size[1];
var wh = size[0] * size[1];
var whd = size[0] * size[1] * size[2];
for (var z = 0; z < size[2]; z++)
for (var y = 0; y < size[1]; y++)
for (var x = 0; x < size[0]; x++)
{
var idx = x + w * y + wh * z;
if (tmp[idx] > 0 | tmp[idx + 1] > 0 | tmp[idx - 1] > 0 | tmp[idx + w] > 0 | tmp[idx - w] > 0 | tmp[idx + wh] > 0 | tmp[idx - wh] > 0)
{
if (data[idx] == 0)
changedPoints.push(idx);
data[idx] = 1;
}
}
return changedPoints;
}
function erode(data, size)
{
var changedPoints = [];
var tmp = new Uint8Array(data);
var w = size[0];
var h = size[1];
var wh = size[0] * size[1];
var whd = size[0] * size[1] * size[2];
for (var z = 0; z < size[2]; z++)
for (var y = 0; y < size[1]; y++)
for (var x = 0; x < size[0]; x++)
{
var idx = x + w * y + wh * z;
if (!(tmp[idx] > 0 & (tmp[idx + 1] > 0 | x == size[0] - 1)
& (tmp[idx - 1] > 0 | x == 0)
& (tmp[idx + w] > 0 | y == size[1] - 1)
& (tmp[idx - w] > 0 | y == 0)
& (tmp[idx + wh] > 0 | z == size[2] - 1)
& (tmp[idx - wh] > 0 | z == 0)))
{
if (data[idx] == 1)
changedPoints.push(idx);
data[idx] = 0;
}
}
return changedPoints;
}
function fillholes(data,size)
{
var tmp = new Uint8Array(size[0] * size[1] * size[2]);
floodfill(data, tmp, size, [0, 0, 0]);
changedPoints = [];
for (var k = 0; k < size[0] * size[1] * size[2]; k++)
{
var newval = tmp[k] <= 0;
if (newval != data[k])
{
data[k] = newval;
changedPoints.push(k);
}
}
return {data:data,changedPoints:changedPoints}
}
function floodfill(data, vol, size, seed, v)
{
var mystacksize = size[0] * size[1] * size[2];
var mystack = new Uint32Array(mystacksize);
var changedPoints = [];
var cnt = 0;
function push(x)
{
if (cnt < mystacksize)
{
mystack[cnt] = x;
vol[x] = 1;
changedPoints.push(x);
cnt++;
}
}
function pop()
{
cnt--;
return mystack[cnt];
}
if (v == undefined)
v = 0;
var w = size[0];
var h = size[1];
var wh = size[0] * size[1];
var whd = size[0] * size[1] * size[2];
push(seed[0] + w * seed[1] + wh * seed[2]);
function comp(a,b)
{
return (a>0 && b>0) | (a==b);
}
while (cnt > 0)
{
var idx = pop();
if (idx >= 0 & idx < whd)
{
if (comp(v,data[idx + 1]) & vol[idx + 1] == 0)
push(idx + 1);
if (comp(v,data[idx - 1]) & vol[idx - 1] == 0)
push(idx - 1);
if (comp(v,data[idx + w]) & vol[idx + w] == 0)
push(idx + w);
if (comp(v,data[idx - w]) & vol[idx - w] == 0)
push(idx - w);
if (comp(v,data[idx + wh]) & vol[idx + wh] == 0)
push(idx + wh);
if (comp(v,data[idx - wh]) & vol[idx - wh] == 0)
push(idx - wh);
}
}
return changedPoints;
}
function atlasToRoi(data,progress,done)
{
var sizes = data.img.sizes;
if (typeof data.key == "object")
{
{
var w2key = KAtlasTool.updateGetPixelFun(data.atlas,data.img,undefined,undefined,data.deffield)
for (var z = 0; z < sizes[2];z++)
for (var y = 0; y < sizes[1];y++)
for (var x = 0; x < sizes[0];x++)
{
if (data.key[w2key(x,y,z)])
data.img.data[x+sizes[0]*y + sizes[0]*sizes[1]*z] = 1;
else
data.img.data[x+sizes[0]*y + sizes[0]*sizes[1]*z] = 0;
}
}
}
else
{
if (sameGeometry(data.img,data.atlas) && data.deffield == undefined)
{
var im = data.img.data;
var tsz = data.img.widheidep;
var k = data.key;
var src = data.atlas.data;
for (var i = 0;i < tsz;i++)
if (src[i]==k)
im[i] = 1;
else
im[i] = 0;
}
else
{
var w2key = KAtlasTool.updateGetPixelFun(data.atlas,data.img,data.key,undefined,data.deffield)
for (var z = 0; z < sizes[2];z++)
for (var y = 0; y < sizes[1];y++)
for (var x = 0; x < sizes[0];x++)
{
if (w2key(x,y,z)>0)
data.img.data[x+sizes[0]*y + sizes[0]*sizes[1]*z] = 1;
else
data.img.data[x+sizes[0]*y + sizes[0]*sizes[1]*z] = 0;
}
}
}
}
function RoiToAtlas(data,progress,done)
{
var sizes = data.img.sizes;
if (data.typ == 'block')
{
for (var z = 0; z < sizes[2];z++)
for (var y = 0; y < sizes[1];y++)
for (var x = 0; x < sizes[0];x++)
{
var idx = x+sizes[0]*y + sizes[0]*sizes[1]*z;
if (data.img.data[idx] > 0)
{
if (data.atlas.data[idx]<=0)
data.atlas.data[idx] = data.key;
}
else if (data.atlas.data[idx] == data.key)
data.atlas.data[idx] = 0;
}
}
else
{
for (var z = 0; z < sizes[2];z++)
for (var y = 0; y < sizes[1];y++)
for (var x = 0; x < sizes[0];x++)
{
var idx = x+sizes[0]*y + sizes[0]*sizes[1]*z;
if (data.img.data[idx] > 0)
data.atlas.data[idx] = data.key;
else if (data.atlas.data[idx] == data.key)
data.atlas.data[idx] = 0;
}
}
}
function createFiberVisitMap(data,progress)
{
var lines = data.lines;
var subset = data.subset;
var buffer = data.data;
var size = data.size;
var edges = data.edges;
var ep = data.endpoints;
buffer.fill(0);
var subsetlen;
var wholebrain;
if (subset)
{
subsetlen = subset.length;
wholebrain = false;
}
else
{
subsetlen = lines.length;
wholebrain = true;
}
var osamp = 3;
var invedges = edges;
var wh = size[0]*size[1];
var w = size[0];
var steps = math.floor(subsetlen/20);
function render(points)
{
var plen = points.length/3-1;
var last0 = points[0];
var last1 = points[1];
var last2 = points[2];
for (var index = 0; index < plen; index++) {
for (var k = 0 ; k < osamp ; k++)
{
var lam = k/osamp;
var v0 = points[3*index ]*(1-lam)+points[3*(index+1) ]*lam;
var v1 = points[3*index+1]*(1-lam)+points[3*(index+1)+1]*lam;
var v2 = points[3*index+2]*(1-lam)+points[3*(index+1)+2]*lam;
var weight = Math.sqrt( (last0-v0)*(last0-v0) + (last1-v1)*(last1-v1) + (last2-v2)*(last2-v2) );
last0 = v0;
last1 = v1;
last2 = v2;
var p0 = invedges[0][0]*v0 +invedges[0][1]*v1+invedges[0][2]*v2 +invedges[0][3];
var p1 = invedges[1][0]*v0 +invedges[1][1]*v1+invedges[1][2]*v2 +invedges[1][3];
var p2 = invedges[2][0]*v0 +invedges[2][1]*v1+invedges[2][2]*v2 +invedges[2][3];
var i0 = math.floor(p0);
var i1 = math.floor(p1);
var i2 = math.floor(p2);
var f0 = p0-i0;
var f1 = p1-i1;
var f2 = p2-i2;
var idx = wh*i2 + w*i1 + i0;
buffer[idx] += (1-f0)*(1-f1)*(1-f2)*weight;
buffer[idx+1] += (f0)*(1-f1)*(1-f2)*weight;
buffer[idx+w] += (1-f0)*(f1)*(1-f2)*weight;
buffer[idx+wh] += (1-f0)*(1-f1)*(f2)*weight;
buffer[idx+w+wh] += (1-f0)*(f1)*(f2)*weight;
buffer[idx+1+wh] += (f0)*(1-f1)*(f2)*weight;
buffer[idx+1+w] += (f0)*(f1)*(1-f2)*weight;
buffer[idx+1+w+wh] += (f0)*(f1)*(f2)*weight;
}
}
}
var terms
if (ep > 0)
terms = [];
for (var l = 0; l < subsetlen; l++) {
if (l%steps == 0)
progress(l/subsetlen);
var points = wholebrain?lines[l]:lines[subset[l]];
if (ep > 0)
{
var L = points.length/3;
var d0 = points[3] - points[0];
var d1 = points[3+1] - points[1];
var d2 = points[3+2] - points[2];
for (var k = 0;k < ep;k++)
{
terms[3*k] = points[0]+d0*(k-ep);
terms[3*k+1] = points[1]+d1*(k-ep);
terms[3*k+2] = points[2]+d2*(k-ep);
//terms[3*(ep+k)] = points[3*k];
//terms[3*(ep+k)+1] = points[3*k+1];
//terms[3*(ep+k)+2] = points[3*k+2];
}
render(terms);
var t0 = points[3*(L-2)] - points[3*(L-1)+0];
var t1 = points[3*(L-2)+1] - points[3*(L-1)+1];
var t2 = points[3*(L-2)+2] - points[3*(L-1)+2];
for (var k = 0;k < ep;k++)
{
terms[3*k] = points[3*(L-1)+0]+t0*(k-ep);
terms[3*k+1] = points[3*(L-1)+1]+t1*(k-ep);
terms[3*k+2] = points[3*(L-1)+2]+t2*(k-ep);
//terms[3*(ep+k)] = points[3*(L-k-1)];
//terms[3*(ep+k)+1] = points[3*(L-k-1)+1];
//terms[3*(ep+k)+2] = points[3*(L-k-1)+2];
}
render(terms);
}
else
render(points);
}
}
function computeIsoSurf(nii)
{
var data = nii.data;
var sizes = nii.sizes;
var detsign = nii.detsign;
var label = nii.label;
var timepoint = nii.currentTimePoint;
var w = sizes[0];
var h = sizes[1];
var d = sizes[2];
var wh = sizes[0]*sizes[1];
var whd = sizes[0]*sizes[1]*sizes[2];
var offset = 0;
if (timepoint != undefined)
offset = timepoint.t*whd;
var cnt = 0;
var vertsIDX = [];
function addVert(i)
{
if (vertsIDX[i] == undefined)
{
vertsIDX[i] = cnt;
cnt++;
return cnt-1;
}
else
return vertsIDX[i];
}
var trigs = [];
var addTrigs;
if (detsign == -1)
addTrigs = function( i00,i10,i11,i01)
{
trigs.push(i00,i10,i11,i00,i11,i01);
}
else
addTrigs = function( i00,i10,i11,i01)
{
trigs.push(i00,i01,i11,i00,i11,i10);
}
var compfun = function(x) { return x>0 }
var negcompfun = function(x) { return x==0 }
if (label)
{
if (label.threshold)
{
compfun = function(x) {
return x>label.threshold;
}
negcompfun = function(x) {
return x<=label.threshold;
}
}
else
{
compfun = function(x) {
return x==label;
}
negcompfun = function(x) {
return x!=label;
}
}
}
var dataAt;
/*if (sizes[3] == 3)
dataAt = function(idx)
{
return Math.sqrt(data[idx]*data[idx]+data[idx+whd]*data[idx+whd]+data[idx+2*whd]*data[idx+2*whd]);
}
else*/
dataAt = function(idx)
{
return data[idx+offset];
}
for (var z = 1; z < sizes[2]-1;z++)
for (var y = 1; y < sizes[1]-1;y++)
for (var x = 1; x < sizes[0]-1;x++)
{
var idx = z*wh + w*y + x;
if (compfun(dataAt(idx)))
{
var i00,i10,i01,i11;
if (negcompfun(dataAt(idx-1)))
{
i00 = addVert(idx);
i10 = addVert(idx+w);
i11 = addVert(idx+w+wh);
i01 = addVert(idx+wh);
addTrigs(i00,i01,i11,i10);
}
if (negcompfun(dataAt(idx+1)))
{
i00 = addVert(idx+1);
i10 = addVert(idx+1+wh);
i11 = addVert(idx+1+w+wh);
i01 = addVert(idx+1+w);
addTrigs(i00,i01,i11,i10);
}
if (negcompfun(dataAt(idx-w)))
{
i00 = addVert(idx);
i10 = addVert(idx+1);
i11 = addVert(idx+1+wh);
i01 = addVert(idx+wh);
addTrigs(i00,i10,i11,i01);
}
if (negcompfun(dataAt(idx+w)))
{
i00 = addVert(idx+w);
i10 = addVert(idx+w+wh);
i11 = addVert(idx+w+1+wh);
i01 = addVert(idx+w+1);
addTrigs(i00,i10,i11,i01);
}
if (negcompfun(dataAt(idx-wh)))
{
i00 = addVert(idx);
i10 = addVert(idx+w);
i11 = addVert(idx+w+1);
i01 = addVert(idx+1);
addTrigs(i00,i10,i11,i01);
}
if (negcompfun(dataAt(idx+wh)))
{
i00 = addVert(idx+wh);
i10 = addVert(idx+wh+1);
i11 = addVert(idx+wh+w+1);
i01 = addVert(idx+wh+w);
addTrigs(i00,i10,i11,i01);
}
}
}
var pts = Object.keys(vertsIDX);
var verts = new Float32Array(pts.length*3);
for (var k = 0; k < pts.length;k++)
{
var x = pts[k]%w -0.5;
var y = math.floor(pts[k]/w)%h -0.5;
var z = math.floor(pts[k]/wh) -0.5;
var v = math.multiply(nii.edges,[x,y,z,1])._data;
var i = vertsIDX[pts[k]];
verts[3*i] = v[0];
verts[3*i+1] = v[1];
verts[3*i+2] = v[2];
}
verts = smooth(verts,trigs);
verts = smooth(verts,trigs);
verts = smooth(verts,trigs);
var normal = new Float32Array(pts.length*3);
var area = 0;
for (var k=0;k<trigs.length/3;k++)
{
var t1 = trigs[3*k];
var t2 = trigs[3*k+1];
var t3 = trigs[3*k+2];
var a = [verts[3*t1+0],verts[3*t1+1],verts[3*t1+2]]
var b = [verts[3*t2+0],verts[3*t2+1],verts[3*t2+2]]
var c = [verts[3*t3+0],verts[3*t3+1],verts[3*t3+2]]
var n = [(b[1]-a[1])*(c[2]-a[2]) - (b[2]-a[2])*(c[1]-a[1]),
(b[2]-a[2])*(c[0]-a[0]) - (b[0]-a[0])*(c[2]-a[2]),
(b[0]-a[0])*(c[1]-a[1]) - (b[1]-a[1])*(c[0]-a[0])];
for (var j=0;j<3;j++)
{
normal[3*trigs[3*k+j]+0] -= n[0];
normal[3*trigs[3*k+j]+1] -= n[1];
normal[3*trigs[3*k+j]+2] -= n[2];
}
area += Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);
}
var c = {}
c.points = verts;
c.indices = trigs;
c.normals = normal;
c.area = area*0.5;
return c;
function smooth(verts,trigs)
{
var verts_sm = new Float32Array(verts.length);
var ncnt = new Int32Array(verts.length/3);
for (var k=0;k<trigs.length/3;k++)
{
var v = [0,0,0];
for (var j = 0; j < 3;j++)
{
for (var i = 0; i < 3; i++)
v[i] += verts[3*trigs[3*k+j]+i]*0.3333;
}
for (var j = 0; j < 3;j++)
{
for (var i = 0; i < 3; i++)
verts_sm[3*trigs[3*k+j]+i] += v[i];
ncnt[trigs[3*k+j]]++;
}
}
for (var k = 0; k < verts.length/3;k++)
{
verts_sm[3*k] /= ncnt[k];
verts_sm[3*k+1] /= ncnt[k];
verts_sm[3*k+2] /= ncnt[k];
}
return verts_sm;
}
}
/* bwconncomp - perform a connected component analsis
input: data - mask array or sontinous array to decomposed
size - size of the array [w,h,d]
comp - function defining whether data[i] is on or off, e.g. function (x) { return x>0;} for masks
clustthres - only clusters above this threshold are considered (number of voxels)
target - optional, array of same size as data, if given, all values that do not belong to the labeling are set to zero in target.
output: obj = {labels,clusterSize , changedPoints}
labels - label array of same size as data
clusterSize - sizes array of clusters
changedPoints - if target given the voxels that switched to zero */
function bwconncomp(data, size, comp, clustthres, target)
{
var changedPoints = [];
var labels = new Uint32Array(data);
var w = size[0];
var h = size[1];
var d = size[2];
var wh = size[0] * size[1];
var whd = size[0] * size[1] * size[2];
labels.fill(0);
var map = {}
var max = 2147483648
var cur_label = max;
for (var z = 0; z < size[2]; z++)
{
var wh_ = wh;
if (z==0)
wh_ = 0;
for (var y = 0; y < size[1]; y++)
{
var w_ = w;
if (y==0)
w_ = 0;
for (var x = 0; x < size[0]; x++)
{
var idx = x+w*y+wh*z;
if (comp(data[idx]))
{
var e_ = 1;
if (x==0)
e_ = 0;
if (labels[idx - e_] == 0 && labels[idx - w_] == 0 && labels[idx - wh_] == 0)
{
labels[idx] = cur_label--;
continue;
}
if (!mmi(e_,w_,wh_))
if (!mmi(w_,e_,wh_))
mmi(wh_,e_,w_)
function mmi(a,b,c)
{
var A = labels[idx - a]
var B = labels[idx - b]
var C = labels[idx - c]
if (A >= B && A >= C)
{
labels[idx] = A;
if (B > 0 && A != labels[idx - b])
{
setref(B,A)
labels[idx - b] = A;
}
if (C > 0 && A != C)
{
setref(C,A)
labels[idx - c] = A;
}
return true;
}
}
function setref(x,y)
{
if (map[x] == undefined)
map[x] = y;
else
{
if (map[x] == y)
return;
// x < y
var r = map[x];
if (y < r) // x < y < r
{
map[x] = y;
setref(y,r)
}
else
{
if (x > r) // r<x<y
{
setref(r,x)
map[x] = y;
}
else // x<r<y
{
setref(x,r)
setref(r,y)
}
}
}
}
}
}
}
}
var keys = Object.keys(map);
function look(a)
{
if (map[a] == undefined)
return a;
else
{
var x = look(map[a]);
map[a] = x;
return x;
}
}
for (var k = 0; k < keys.length;k++)
map[keys[k]] = look(keys[k]);
var clustsize = {};
var cog = {};
var firstpoint = {};
for (var z = 0; z < whd; z++)
if (comp(data[z]))
{
var k = map[labels[z]];
if (k == undefined)
k = labels[z];
labels[z] = k;
if (clustsize[k] == undefined)
{
clustsize[k] = 1;
cog[k] = [z%w,Math.floor(z/w)%h,Math.floor(z/wh)%d]
firstpoint[k] = [z%w,Math.floor(z/w)%h,Math.floor(z/wh)%d]
}
else
{
clustsize[k]++;
cog[k][0] += z%w
cog[k][1] += Math.floor(z/w)%h
cog[k][2] += Math.floor(z/wh)%d;
}
}
var clusts = Object.keys(clustsize);
var indic = {};
var clusterSize = {};
var centerOfGrav ={}
var firstPoint ={}
var cnum = 1;
for (var k = 0 ; k < clusts.length;k++)
{
if (clustsize[clusts[k]] > clustthres)
{
clusterSize[cnum] = clustsize[clusts[k]];
centerOfGrav[cnum] = [ cog[clusts[k]][0]/clustsize[clusts[k]],
cog[clusts[k]][1]/clustsize[clusts[k]],
cog[clusts[k]][2]/clustsize[clusts[k]] ];
firstPoint[cnum] = [ firstpoint[clusts[k]][0],
firstpoint[clusts[k]][1],
firstpoint[clusts[k]][2] ];
indic[clusts[k]] = cnum;
cnum++;
}
}
for (var z = 0; z < whd; z++)
if (comp(data[z]))
{
if (!indic[labels[z]])
{
if (target != undefined)
target[z] = 0;
labels[z] = 0;
changedPoints.push(z);
}
else
labels[z] = indic[labels[z]];
}
else
if (target != undefined)
target[z] = 0;
return {firstpoint:firstPoint,centerOfGrav:centerOfGrav,changedPoints:changedPoints,labels:labels,clusterSize:clusterSize};
}