@webviz/subsurface-viewer
Version:
3D visualization component for subsurface reservoir data
309 lines (306 loc) • 13.9 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { project32 } from "@deck.gl/core";
import { BitmapLayer } from "@deck.gl/layers";
import * as png from "@vivaxy/png";
import { getModelMatrix } from "../utils/layerTools";
import { getImageData, } from "../utils/colormapTools";
import { decodeRGB } from "../utils/propertyMapTools";
import fsColormap from "./colormap.fs.glsl";
import { precisionForTests } from "../shader_modules/test-precision/precisionForTests";
const defaultProps = {
"@@type": "ColormapLayer",
name: "Property map",
id: "colormap-layer",
pickable: true,
visible: true,
valueRange: { type: "array", value: [0, 1] },
colorMapRange: { type: "array" },
valueDecoder: {
rgbScaler: [1, 1, 1],
// By default, scale the [0, 256*256*256-1] decoded values to [0, 1]
floatScaler: 1.0 / (256.0 * 256.0 * 256.0 - 1.0),
offset: 0,
step: 0,
},
rotDeg: 0,
colorMapName: "Rainbow",
contours: false,
hillshading: false,
contourReferencePoint: 0,
contourInterval: 50, // 50 meter by default
heightMapUrl: "",
};
export default class ColormapLayer extends BitmapLayer {
initializeState() {
super.initializeState();
const createPropertyTexture = () => __awaiter(this, void 0, void 0, function* () {
const response = yield fetch(this.props.heightMapUrl);
if (!response.ok) {
console.error("Could not load ", this.props.heightMapUrl);
}
else {
const blob = yield response.blob();
const contentType = response.headers.get("content-type");
const isPng = contentType === "image/png";
if (isPng) {
const heightMapTexture = yield new Promise((resolve) => {
const fileReader = new FileReader();
fileReader.readAsArrayBuffer(blob);
fileReader.onload = () => {
const arrayBuffer = fileReader.result;
const imgData = png.decode(arrayBuffer);
const w = imgData.width;
const h = imgData.height;
const data = imgData.data; // array of int's
const n = data.length;
const buffer = new ArrayBuffer(n);
const view = new DataView(buffer);
for (let i = 0; i < n; i++) {
view.setUint8(i, data[i]);
}
const array = new Uint8Array(buffer);
const propertyTexture = this.context.device.createTexture({
sampler: {
addressModeU: "clamp-to-edge",
addressModeV: "clamp-to-edge",
minFilter: "linear",
magFilter: "linear",
},
width: w,
height: h,
format: "rgba8unorm",
data: array,
});
resolve(propertyTexture);
};
});
this.setState(Object.assign(Object.assign({}, this.state), { heightMapTexture }));
}
}
});
createPropertyTexture();
}
setShaderModuleProps(...props) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
if (!this.isLoaded) {
if (typeof this.props.reportBoundingBox !== "undefined") {
const xMin = this.props.bounds[0];
const yMin = this.props.bounds[1];
const zMin = 1;
const xMax = this.props.bounds[2];
const yMax = this.props.bounds[3];
const zMax = -1;
this.props.reportBoundingBox({
layerBoundingBox: [xMin, yMin, zMin, xMax, yMax, zMax],
});
}
}
// Set property for modelMatrix.
const m = getModelMatrix(this.props.rotDeg, this.props.bounds[0], // Rotate around upper left corner of bounds
this.props.bounds[3]);
for (const model of this.getModels()) {
const isDefined = ((_b = (_a = props[0]) === null || _a === void 0 ? void 0 : _a.project) === null || _b === void 0 ? void 0 : _b.modelMatrix) !== undefined;
if (isDefined) {
props[0]["project"].modelMatrix =
m;
}
model.shaderInputs.setProps(...props);
}
// Properties for local shader module.
const valueRangeMin = (_c = this.props.valueRange[0]) !== null && _c !== void 0 ? _c : 0.0;
const valueRangeMax = (_d = this.props.valueRange[1]) !== null && _d !== void 0 ? _d : 1.0;
// If specified, color map will extend from colormapRangeMin to colormapRangeMax.
// Otherwise it will extend from valueRangeMin to valueRangeMax.
const colormapRangeMin = (_f = (_e = this.props.colorMapRange) === null || _e === void 0 ? void 0 : _e[0]) !== null && _f !== void 0 ? _f : valueRangeMin;
const colormapRangeMax = (_h = (_g = this.props.colorMapRange) === null || _g === void 0 ? void 0 : _g[1]) !== null && _h !== void 0 ? _h : valueRangeMax;
const colormapTexture = this.context.device.createTexture({
width: 256,
height: 1,
format: "rgb8unorm-webgl",
data: getImageData((_j = this.props.colorMapFunction) !== null && _j !== void 0 ? _j : {
colormapName: this.props.colorMapName,
colorTables: this.context.userData
.colorTables,
}),
});
// eslint-disable-next-line
// @ts-ignore
let heightMapTexture = this.state.heightMapTexture;
const isHeightMapTextureDefined = typeof heightMapTexture !== "undefined";
if (!isHeightMapTextureDefined) {
// Create a dummy texture
heightMapTexture = this.context.device.createTexture({
sampler: {
addressModeU: "clamp-to-edge",
addressModeV: "clamp-to-edge",
minFilter: "linear",
magFilter: "linear",
},
width: 1,
height: 1,
format: "rgba8unorm",
data: new Uint8Array([1, 1, 1, 1]),
});
}
const heightValueRangeMin = (_l = (_k = this.props.heightValueRange) === null || _k === void 0 ? void 0 : _k[0]) !== null && _l !== void 0 ? _l : valueRangeMin;
const heightValueRangeMax = (_o = (_m = this.props.heightValueRange) === null || _m === void 0 ? void 0 : _m[1]) !== null && _o !== void 0 ? _o : valueRangeMax;
(_p = this.state.model) === null || _p === void 0 ? void 0 : _p.setBindings({ colormapTexture, heightMapTexture });
const bitmapResolution = this.props.image
? [
this.props.image.width,
this.props.image.height,
]
: [1, 1];
const contours = this.props.contours;
const hillshading = this.props.hillshading;
const contourReferencePoint = this.props.contourReferencePoint;
const contourInterval = this.props.contourInterval;
const isClampColor = this.props.colorMapClampColor !== undefined &&
this.props.colorMapClampColor !== true &&
this.props.colorMapClampColor !== false;
let colormapClampColor = isClampColor
? this.props.colorMapClampColor
: [0, 0, 0];
colormapClampColor = colormapClampColor.map((x) => (x !== null && x !== void 0 ? x : 0) / 255);
const isColormapClampColorTransparent = this.props.colorMapClampColor === false;
super.setShaderModuleProps({
map: {
valueRangeMin,
valueRangeMax,
colormapRangeMin,
colormapRangeMax,
contours,
hillshading,
bitmapResolution,
contourReferencePoint,
contourInterval,
isClampColor,
colormapClampColor,
isColormapClampColorTransparent,
isHeightMapTextureDefined,
heightValueRangeMin,
heightValueRangeMax,
rgbScaler: this.props.valueDecoder.rgbScaler,
floatScaler: this.props.valueDecoder.floatScaler,
offset: this.props.valueDecoder.offset,
step: this.props.valueDecoder.step,
},
});
}
getShaders() {
const parentShaders = super.getShaders();
// use object.assign to make sure we don't overwrite existing fields like `vs`, `modules`...
return Object.assign({}, parentShaders, {
fs: fsColormap,
modules: [
...parentShaders.modules,
project32,
map2DUniforms,
precisionForTests,
],
});
}
getPickingInfo({ info, }) {
if (!info.color) {
return info;
}
const mergedDecoder = Object.assign(Object.assign({}, defaultProps.valueDecoder), this.props.valueDecoder);
// The picked color is the one in raw image, not the one after colormapping.
// We just need to decode that RGB color into a property float value.
const val = decodeRGB(info.color, mergedDecoder, this.props.valueRange);
if (info.coordinate) {
info.coordinate[2] = NaN; // disable z value in 2D map;
}
return Object.assign(Object.assign({}, info), {
// Picking color doesn't represent object index in this layer.
// For more details, see https://deck.gl/docs/developer-guide/custom-layers/picking
index: 0, propertyValue: val });
}
getLegendData() {
var _a, _b, _c, _d, _e, _f;
const valueRangeMin = (_a = this.props.valueRange[0]) !== null && _a !== void 0 ? _a : 0.0;
const valueRangeMax = (_b = this.props.valueRange[1]) !== null && _b !== void 0 ? _b : 1.0;
// If specified color map will extend from colormapRangeMin to colormapRangeMax.
// Otherwise it will extend from valueRangeMin to valueRangeMax.
const min = (_d = (_c = this.props.colorMapRange) === null || _c === void 0 ? void 0 : _c[0]) !== null && _d !== void 0 ? _d : valueRangeMin;
const max = (_f = (_e = this.props.colorMapRange) === null || _e === void 0 ? void 0 : _e[1]) !== null && _f !== void 0 ? _f : valueRangeMax;
return {
discrete: false,
valueRange: [min, max],
colorName: this.props.colorMapName,
title: "PropertyMapLayer",
colorMapFunction: this.props.colorMapFunction,
};
}
}
ColormapLayer.layerName = "ColormapLayer";
ColormapLayer.defaultProps = defaultProps;
// local shader module for the uniforms
const map2DUniformsBlock = /*glsl*/ `\
uniform mapUniforms {
float valueRangeMin;
float valueRangeMax;
float colormapRangeMin;
float colormapRangeMax;
vec2 bitmapResolution;
bool contours;
bool hillshading;
float contourReferencePoint;
float contourInterval;
bool isClampColor;
vec3 colormapClampColor;
bool isColormapClampColorTransparent;
bool isHeightMapTextureDefined;
float heightValueRangeMin;
float heightValueRangeMax;
vec3 rgbScaler; // r, g and b multipliers
float floatScaler; // value multiplier
float offset; // translation of the r, g, b sum
float step; // discretize the value in a number of
} map;
float decode_rgb2float(vec3 rgb) {
rgb *= map.rgbScaler * vec3(16711680.0, 65280.0, 255.0); //255*256*256, 255*256, 255
float value = (rgb.r + rgb.g + rgb.b + map.offset) * map.floatScaler;
// Value must be in [0, 1] and step in (0, 1]
if (map.step > 0.0) {
value = floor(value / map.step + 0.5) * map.step;
}
return value;
}
`;
// NOTE: this must exactly the same name as in the uniform block
const map2DUniforms = {
name: "map",
vs: map2DUniformsBlock,
fs: map2DUniformsBlock,
uniformTypes: {
valueRangeMin: "f32",
valueRangeMax: "f32",
colormapRangeMin: "f32",
colormapRangeMax: "f32",
bitmapResolution: "vec2<f32>",
contours: "u32",
hillshading: "u32",
contourReferencePoint: "f32",
contourInterval: "f32",
isClampColor: "u32",
colormapClampColor: "vec3<f32>",
isColormapClampColorTransparent: "u32",
isHeightMapTextureDefined: "u32",
heightValueRangeMin: "f32",
heightValueRangeMax: "f32",
rgbScaler: "vec3<f32>",
floatScaler: "f32",
offset: "f32",
step: "f32",
},
};
//# sourceMappingURL=colormapLayer.js.map