@webviz/subsurface-viewer
Version:
3D visualization component for subsurface reservoir data
419 lines • 18.1 kB
JavaScript
/** Given the input data will build and return the attributes (vertices and indices for triangles and lines)
* that is used by WebGl. Using indice, lines and triangles share common vertices to save memory.
*/
export function makeFullMesh(e) {
const [inputMeshData, inputPropertiesData, isMesh, frame, smoothShading, gridLines,] = e.data;
function getFloat32ArrayMinMax(data) {
let max = -99999999;
let min = 99999999;
for (let i = 0; i < data.length; i++) {
max = data[i] > max ? data[i] : max;
min = data[i] < min ? data[i] : min;
}
return [min, max];
}
function crossProduct(a, b) {
const c = [
a[1] * b[2] - a[2] * b[1],
a[2] * b[0] - a[0] * b[2],
a[0] * b[1] - a[1] * b[0],
];
return c;
}
function isDefined(x) {
return typeof x === "number" && !isNaN(x);
}
function normalize(a) {
const L = Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);
a[0] /= L;
a[1] /= L;
a[2] /= L;
}
function calcNormal(w, h, nx, ny, isMesh, smoothShading, meshData, ox, oy, dx, dy) {
if (!smoothShading) {
return [1, 1, 1];
}
if (!isMesh) {
return [0, 0, 1];
}
const i0 = h * nx + w;
const i1 = h * nx + (w - 1);
const i2 = (h + 1) * nx + w;
const i3 = h * nx + (w + 1);
const i4 = (h - 1) * nx + w;
const i0_act = isDefined(meshData[i0]); // eslint-disable-line
const i1_act = w - 1 >= 0 && isDefined(meshData[i1]); // eslint-disable-line
const i2_act = h + 1 < ny && isDefined(meshData[i2]); // eslint-disable-line
const i3_act = w + 1 < nx && isDefined(meshData[i3]); // eslint-disable-line
const i4_act = h - 1 >= 0 && isDefined(meshData[i4]); // eslint-disable-line
const noNormal = [0, 0, 0]; // signals a normal could not be calculated.
if (!i0_act) {
return noNormal;
}
const hh = ny - 1 - h; // Note use hh for h for getting y values.
const p0 = [ox + w * dx, oy + hh * dy, i0_act ? meshData[i0] : 0]; // eslint-disable-line
const p1 = [ox + (w - 1) * dx, oy + hh * dy, i1_act ? meshData[i1] : 0]; // eslint-disable-line
const p2 = [ox + w * dx, oy + (hh + 1) * dy, i2_act ? meshData[i2] : 0]; // eslint-disable-line
const p3 = [ox + (w + 1) * dx, oy + hh * dy, i3_act ? meshData[i3] : 0]; // eslint-disable-line
const p4 = [ox + w * dx, oy + (hh - 1) * dy, i4_act ? meshData[i4] : 0]; // eslint-disable-line
const v1 = [p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]];
const v2 = [p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2]];
const v3 = [p3[0] - p0[0], p3[1] - p0[1], p3[2] - p0[2]];
const v4 = [p4[0] - p0[0], p4[1] - p0[1], p4[2] - p0[2]];
// Estimating a normal vector at p0:
// Take cross product of vectors v1, v2,
// Do this for all 4 quadrants.
// The resulting normal will be the mean of these four normals.
// p2
// |
// p1 - p0 - p3
// |
// p4
const normals = [];
if (i1_act && i2_act) {
const normal = crossProduct(v2, v1);
normals.push(normal);
}
if (i2_act && i3_act) {
const normal = crossProduct(v3, v2);
normals.push(normal);
}
if (i3_act && i4_act) {
const normal = crossProduct(v4, v3);
normals.push(normal);
}
if (i4_act && i1_act) {
const normal = crossProduct(v1, v4);
normals.push(normal);
}
if (normals.length === 0) {
return noNormal;
}
const mean = normals[0];
for (let i = 1; i < normals.length; i++) {
mean[0] += normals[i][0];
mean[1] += normals[i][1];
mean[2] += normals[i][2];
}
normalize(mean);
return mean;
}
const meshData = inputMeshData;
const propertiesData = (inputPropertiesData === null || inputPropertiesData === void 0 ? void 0 : inputPropertiesData.length)
? inputPropertiesData
: meshData;
// non mesh grids use z = 0 (see below)
const meshZValueRange = isMesh ? getFloat32ArrayMinMax(meshData) : [0, 0];
const propertyValueRange = getFloat32ArrayMinMax(propertiesData);
// Dimensions.
const ox = frame.origin[0];
const oy = frame.origin[1];
const dx = frame.increment[0];
const dy = frame.increment[1];
const nx = frame.count[0]; // number of nodes in x direction
const ny = frame.count[1];
const propLength = propertiesData.length;
const isCellCenteredProperties = propLength === (nx - 1) * (ny - 1);
if (propLength !== (nx - 1) * (ny - 1) && propLength !== nx * ny) {
console.error("There should be as many property values as nodes (nx*ny) OR as many as cells (nx - 1) * (ny - 1).");
}
const nNodes = nx * ny;
const nCells = (nx - 1) * (ny - 1);
const nTriangles = nCells * 2;
const positions = new Float32Array(isCellCenteredProperties ? nCells * 6 * 3 : nNodes * 3);
const normals = new Float32Array(isCellCenteredProperties || !smoothShading ? 0 : nNodes * 3);
const triangleIndices = new Uint32Array(nTriangles * 3);
const vertexProperties = new Float32Array(isCellCenteredProperties ? nCells * 6 : nNodes);
let nLineIndices = 0;
if (gridLines) {
nLineIndices = isCellCenteredProperties
? nTriangles * 2 * 2
: nCells * 4 + (nx - 1) * 2 + (ny - 1) * 2;
}
const lineIndices = new Uint32Array(nLineIndices);
// Note: Assumed layout of the incomming 2D array of data:
// First coloumn corresponds to lowest x value. Last column highest x value.
// First row corresponds to max y value. Last row corresponds to lowest y value.
// This must be taken into account when calculating vertex x,y values and texture coordinates.
if (!isCellCenteredProperties) {
// PROPERTIES IS SET INTERPOLATED OVER A CELL.
// Loop vertices.
let i = 0;
for (let h = 0; h < ny; h++) {
for (let w = 0; w < nx; w++) {
const i0 = h * nx + w;
const x0 = ox + w * dx;
const y0 = oy + (ny - 1 - h) * dy; // See note above.
const z = isMesh ? meshData[i0] : 0;
const propertyValue = propertiesData[i0];
positions[3 * i + 0] = x0;
positions[3 * i + 1] = y0;
positions[3 * i + 2] = z;
if (smoothShading) {
const normal = calcNormal(w, h, nx, ny, isMesh, smoothShading, meshData, ox, oy, dx, dy); // eslint-disable-line
normals[3 * i + 0] = normal[0];
normals[3 * i + 1] = normal[1];
normals[3 * i + 2] = normal[2];
}
vertexProperties[i] = propertyValue;
i++;
}
}
// Loop cells.
i = 0;
let j = 0;
for (let h = 0; h < ny - 1; h++) {
for (let w = 0; w < nx - 1; w++) {
const i0 = h * nx + w;
const i1 = h * nx + (w + 1);
const i2 = (h + 1) * nx + (w + 1);
const i3 = (h + 1) * nx + w;
const i0_act = !isMesh ||
(isDefined(meshData[i0]) && isDefined(propertiesData[i0])); // eslint-disable-line
const i1_act = !isMesh ||
(isDefined(meshData[i1]) && isDefined(propertiesData[i1])); // eslint-disable-line
const i2_act = !isMesh ||
(isDefined(meshData[i2]) && isDefined(propertiesData[i2])); // eslint-disable-line
const i3_act = !isMesh ||
(isDefined(meshData[i3]) && isDefined(propertiesData[i3])); // eslint-disable-line
// Triangles.
if (i1_act && i3_act) {
// diagonal i1, i3
if (i0_act) {
// t1 - i0 provoking index.
triangleIndices[i++] = i1;
triangleIndices[i++] = i3;
triangleIndices[i++] = i0;
}
if (i2_act) {
// t2 - i2 provoking index.
triangleIndices[i++] = i1;
triangleIndices[i++] = i3;
triangleIndices[i++] = i2;
}
}
else if (i0_act && i2_act) {
// diagonal i0, i2
if (i1_act) {
// t1 - i0 provoking index.
triangleIndices[i++] = i1;
triangleIndices[i++] = i2;
triangleIndices[i++] = i0;
}
if (i3_act) {
// t2 - i2 provoking index.
triangleIndices[i++] = i3;
triangleIndices[i++] = i0;
triangleIndices[i++] = i2;
}
}
// Lines.
if (gridLines) {
if (i0_act && i1_act) {
lineIndices[j++] = i0;
lineIndices[j++] = i1;
}
if (i0_act && i3_act) {
lineIndices[j++] = i0;
lineIndices[j++] = i3;
}
if (h == ny - 2 && i2_act && i3_act) {
lineIndices[j++] = i3;
lineIndices[j++] = i2;
}
if (w == nx - 2 && i1_act && i2_act) {
lineIndices[j++] = i1;
lineIndices[j++] = i2;
}
// diagonal
if ((i0_act && !i2_act) || (!i0_act && i2_act)) {
lineIndices[j++] = i1;
lineIndices[j++] = i3;
}
// diagonal
if ((i3_act && !i1_act) || (!i3_act && i1_act)) {
lineIndices[j++] = i0;
lineIndices[j++] = i2;
}
}
}
}
}
else {
// PROPERTIES IS SET CONSTANT OVER A CELL.
// Loop cells.
let i = 0;
let j = 0;
let k = 0;
let l = 0;
for (let h = 0; h < ny - 1; h++) {
for (let w = 0; w < nx - 1; w++) {
const hh = ny - 1 - h; // See note above.
const i0 = h * nx + w;
const i1 = h * nx + (w + 1);
const i2 = (h + 1) * nx + (w + 1);
const i3 = (h + 1) * nx + w;
const i0_act = !isMesh || isDefined(meshData[i0]); // eslint-disable-line
const i1_act = !isMesh || isDefined(meshData[i1]); // eslint-disable-line
const i2_act = !isMesh || isDefined(meshData[i2]); // eslint-disable-line
const i3_act = !isMesh || isDefined(meshData[i3]); // eslint-disable-line
const x0 = ox + w * dx;
const y0 = oy + hh * dy;
const z0 = isMesh ? meshData[i0] : 0;
const x1 = ox + (w + 1) * dx;
const y1 = oy + hh * dy;
const z1 = isMesh ? meshData[i1] : 0;
const x2 = ox + (w + 1) * dx;
const y2 = oy + (hh - 1) * dy; // Note hh - 1 here.
const z2 = isMesh ? meshData[i2] : 0;
const x3 = ox + w * dx;
const y3 = oy + (hh - 1) * dy; // Note hh - 1 here.
const z3 = isMesh ? meshData[i3] : 0;
const propertyIndex = h * (nx - 1) + w; // (nx - 1) -> the width of the property 2D array is one less than for the nodes in this case.
const propertyValue = propertiesData[propertyIndex];
if (!isDefined(propertyValue)) {
// Inactive cell, dont draw.
continue;
}
// Triangles.
if (i1_act && i3_act) {
// diagonal i1, i3
if (i0_act) {
// t1 - i0 provoking index.
triangleIndices[i] = i;
const L1 = i;
i++;
positions[j++] = x1;
positions[j++] = y1;
positions[j++] = z1;
triangleIndices[i] = i;
const L2 = i;
i++;
positions[j++] = x3;
positions[j++] = y3;
positions[j++] = z3;
triangleIndices[i] = i;
const L3 = i;
i++;
positions[j++] = x0;
positions[j++] = y0;
positions[j++] = z0;
if (gridLines) {
lineIndices[l++] = L3;
lineIndices[l++] = L1;
lineIndices[l++] = L3;
lineIndices[l++] = L2;
}
vertexProperties[k++] = propertyValue;
vertexProperties[k++] = propertyValue;
vertexProperties[k++] = propertyValue;
}
if (i2_act) {
// t2 - i2 provoking index.
triangleIndices[i] = i;
const L1 = i;
i++;
positions[j++] = x1;
positions[j++] = y1;
positions[j++] = z1;
triangleIndices[i] = i;
const L2 = i;
i++;
positions[j++] = x3;
positions[j++] = y3;
positions[j++] = z3;
triangleIndices[i] = i;
const L3 = i;
i++;
positions[j++] = x2;
positions[j++] = y2;
positions[j++] = z2;
if (gridLines) {
lineIndices[l++] = L1;
lineIndices[l++] = L3;
lineIndices[l++] = L2;
lineIndices[l++] = L3;
}
vertexProperties[k++] = propertyValue;
vertexProperties[k++] = propertyValue;
vertexProperties[k++] = propertyValue;
}
}
else if (i0_act && i2_act) {
// diagonal i0, i2
if (i1_act) {
//t1 - i0 provoking index.
triangleIndices[i] = i;
const L1 = i;
i++;
positions[j++] = x1;
positions[j++] = y1;
positions[j++] = z1;
triangleIndices[i] = i;
const L2 = i;
i++;
positions[j++] = x2;
positions[j++] = y2;
positions[j++] = z2;
triangleIndices[i] = i;
const L3 = i;
i++;
positions[j++] = x0;
positions[j++] = y0;
positions[j++] = z0;
if (gridLines) {
lineIndices[l++] = L1;
lineIndices[l++] = L3;
lineIndices[l++] = L1;
lineIndices[l++] = L2;
}
vertexProperties[k++] = propertyValue;
vertexProperties[k++] = propertyValue;
vertexProperties[k++] = propertyValue;
}
if (i3_act) {
// t2 - i2 provoking index.
triangleIndices[i] = i;
const L1 = i;
i++;
positions[j++] = x3;
positions[j++] = y3;
positions[j++] = z3;
triangleIndices[i] = i;
const L2 = i;
i++;
positions[j++] = x0;
positions[j++] = y0;
positions[j++] = z0;
triangleIndices[i] = i;
const L3 = i;
i++;
positions[j++] = x2;
positions[j++] = y2;
positions[j++] = z2;
if (gridLines) {
lineIndices[l++] = L1;
lineIndices[l++] = L2;
lineIndices[l++] = L1;
lineIndices[l++] = L3;
}
vertexProperties[k++] = propertyValue;
vertexProperties[k++] = propertyValue;
vertexProperties[k++] = propertyValue;
}
}
}
}
}
const webworkerReturnData = [
positions,
normals,
triangleIndices,
vertexProperties,
lineIndices,
meshZValueRange,
propertyValueRange,
];
return webworkerReturnData;
}
//# sourceMappingURL=webworker.js.map