@webviz/subsurface-viewer
Version:
3D visualization component for subsurface reservoir data
310 lines • 10.9 kB
JavaScript
import { Geometry, Model } from "@luma.gl/engine";
import { Layer, project, picking, UNIT } from "@deck.gl/core";
import { createPropertyData } from "../utils/layerTools";
import { utilities } from "../shader_modules";
import { precisionForTests } from "../shader_modules/test-precision/precisionForTests";
import fsShader from "./marker.fs.glsl";
import vsShader from "./marker.vs.glsl";
const normalizeColor = (color) => {
if (!color) {
return new Uint8Array([0, 0, 0, 255]);
}
if (color.length > 4) {
return new Uint8Array(color.slice(0, 4));
}
switch (color.length) {
case 0:
return new Uint8Array([0, 0, 0, 255]);
case 1:
return new Uint8Array([...color, 0, 0, 255]);
case 2:
return new Uint8Array([...color, 0, 255]);
case 3:
return new Uint8Array([...color, 255]);
default:
return color;
}
};
const defaultProps = {
"@@type": "WellMarkersLayer",
name: "Well Markers",
id: "well-markers",
shape: "circle",
sizeUnits: "meters",
visible: true,
ZIncreasingDownwards: true,
getPosition: {
type: "accessor",
value: (x) => {
return x.position;
},
},
getSize: {
type: "accessor",
value: (x) => {
return x.size;
},
},
getAzimuth: {
type: "accessor",
value: (x) => {
return x.azimuth;
},
},
getInclination: {
type: "accessor",
value: (x) => {
return x.inclination;
},
},
getColor: {
type: "accessor",
value: (x) => {
return normalizeColor(x.color);
},
},
getOutlineColor: {
type: "accessor",
value: (x) => {
return normalizeColor(x.outlineColor);
},
},
};
export default class WellMarkersLayer extends Layer {
constructor(props) {
super(props);
this.shapes = new Map();
this.initShapes();
}
initializeState() {
this.getAttributeManager().addInstanced({
instancePositions: {
size: 3,
type: "float64",
transition: true,
accessor: "getPosition",
},
instanceSizes: {
size: 1,
type: "float64",
transition: true,
accessor: "getSize",
defaultValue: 1.0,
},
instanceAzimuths: {
size: 1,
type: "float64",
transition: true,
accessor: "getAzimuth",
defaultValue: 0,
},
instanceInclinations: {
size: 1,
type: "float64",
transition: true,
accessor: "getInclination",
defaultValue: 0,
},
instanceColors: {
size: 4,
type: "uint8",
transition: true,
accessor: "getColor",
defaultValue: [255, 0, 0, 255],
},
instanceOutlineColors: {
size: 4,
type: "uint8",
transition: true,
accessor: "getOutlineColor",
defaultValue: [255, 0, 255, 255],
},
});
const models = this._createModels();
this.setState({ shapeModel: models[0], outlineModel: models[1] });
}
updateState(params) {
var _a, _b;
super.updateState(params);
if (params.changeFlags.extensionsChanged ||
params.changeFlags.propsChanged) {
const oldShapeModel = (_a = this.state) === null || _a === void 0 ? void 0 : _a["shapeModel"];
oldShapeModel.destroy();
const oldOutlineModel = (_b = this.state) === null || _b === void 0 ? void 0 : _b["outlineModel"];
oldOutlineModel.destroy();
const models = this._createModels();
this.setState(Object.assign(Object.assign({}, this.state), { shapeModel: models[0], outlineModel: models[1] }));
this.getAttributeManager().invalidateAll();
}
}
getModels() {
if (this.state["shapeModel"] && this.state["outlineModel"]) {
return [
this.state["shapeModel"],
this.state["outlineModel"],
];
}
return [];
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
draw(args) {
if (!this.state["shapeModel"]) {
return;
}
const models = this.getModels();
if (models.length && models.length < 2) {
return;
}
models[0].shaderInputs.setProps(Object.assign(Object.assign({}, args.uniforms), { wellMarkers: {
useOutlineColor: false,
sizeUnits: UNIT[this.props.sizeUnits],
ZIncreasingDownwards: this.props.ZIncreasingDownwards,
} }));
models[0].draw(args.context.renderPass);
models[1].shaderInputs.setProps(Object.assign(Object.assign({}, args.uniforms), { wellMarkers: {
useOutlineColor: true,
sizeUnits: UNIT[this.props.sizeUnits],
ZIncreasingDownwards: this.props.ZIncreasingDownwards,
} }));
models[1].draw(args.context.renderPass);
}
getPickingInfo({ info }) {
var _a;
if (!info.color) {
return info;
}
const layer_properties = [];
const markerIndex = this.decodePickingColor(info.color);
const markerData = this.props.data;
if (markerIndex >= 0 && markerIndex < markerData.length) {
layer_properties.push(createPropertyData("Azimuth", markerData[markerIndex].azimuth));
layer_properties.push(createPropertyData("Inclination", markerData[markerIndex].inclination));
}
if (typeof ((_a = info.coordinate) === null || _a === void 0 ? void 0 : _a[2]) !== "undefined") {
let depth = info.coordinate[2];
depth = this.props.ZIncreasingDownwards ? -depth : depth;
layer_properties.push(createPropertyData("Depth", depth));
}
return Object.assign(Object.assign({}, info), { properties: layer_properties });
}
getShaders() {
return super.getShaders({
vs: vsShader,
fs: fsShader,
modules: [
project,
picking,
utilities,
wellMarkersUniforms,
precisionForTests,
],
});
}
initShapes() {
const triangle_positions = [
-1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 0.0, 1.0, 0.0,
];
const triangle_outline = [
-1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 0.0, 1.0, 0.0, -1.0, -1.0, 0.0,
];
const circle_positions = [];
const circle_outline = [];
const N = 32;
const R = 1.0;
for (let i = 0; i <= N; ++i) {
const angle = ((2.0 * Math.PI) / N) * i;
circle_positions.push(R * Math.cos(angle));
circle_positions.push(R * Math.sin(angle));
circle_positions.push(0.0);
// push center again to represent a circle with a triangle strip
circle_positions.push(0.0);
circle_positions.push(0.0);
circle_positions.push(0.0);
circle_outline.push(R * Math.cos(angle));
circle_outline.push(R * Math.sin(angle));
circle_outline.push(0.0);
}
const square_positions = [
-1.0, 1.0, 0.0, 1.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0,
];
const square_outline = [
-1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, -1.0, 0.0, -1.0, -1.0, 0.0,
-1.0, 1.0, 0.0,
];
this.shapes.set("triangle", {
drawMode: "triangle-list",
positions: new Float32Array(triangle_positions),
outline: new Float32Array(triangle_outline),
});
this.shapes.set("circle", {
drawMode: "triangle-strip",
positions: new Float32Array(circle_positions),
outline: new Float32Array(circle_outline),
});
this.shapes.set("square", {
drawMode: "triangle-strip",
positions: new Float32Array(square_positions),
outline: new Float32Array(square_outline),
});
}
_createModels() {
const device = this.context.device;
const shape = this.shapes.get(this.props.shape);
if (!shape) {
return this._createEmptyModels();
}
const shaders = this.getShaders();
const shapeModel = new Model(device, Object.assign(Object.assign({ id: `${this.props.id}-mesh` }, shaders), { bufferLayout: this.getAttributeManager().getBufferLayouts(), geometry: new Geometry({
topology: shape.drawMode,
attributes: {
positions: { size: 3, value: shape.positions },
},
}), isInstanced: true, instanceCount: this.getNumInstances() }));
const outlineModel = new Model(device, Object.assign(Object.assign({ id: `${this.props.id}-outline` }, shaders), { bufferLayout: this.getAttributeManager().getBufferLayouts(), geometry: new Geometry({
topology: "line-strip",
attributes: {
positions: { size: 3, value: shape.outline },
},
}), isInstanced: true, instanceCount: this.getNumInstances() }));
return [shapeModel, outlineModel];
}
_createEmptyModels() {
return [
new Model(this.context.device, {
id: `${this.props.id}-empty-mesh`,
vs: vsShader,
fs: fsShader,
isInstanced: true,
instanceCount: 0,
}),
new Model(this.context.device, {
id: `${this.props.id}-empty-outline`,
vs: vsShader,
fs: fsShader,
isInstanced: true,
instanceCount: 0,
}),
];
}
}
const wellMarkersUniformsBlock = /*glsl*/ `\
uniform wellMarkersUniforms {
int sizeUnits;
bool useOutlineColor;
bool ZIncreasingDownwards;
} wellMarkers;
`;
// NOTE: this must exactly the same name than in the uniform block
const wellMarkersUniforms = {
name: "wellMarkers",
vs: wellMarkersUniformsBlock,
fs: undefined,
uniformTypes: {
sizeUnits: "f32",
useOutlineColor: "u32",
ZIncreasingDownwards: "u32",
},
};
WellMarkersLayer.layerName = "WellMarkersLayer";
WellMarkersLayer.defaultProps = defaultProps;
//# sourceMappingURL=wellMarkersLayer.js.map