Skip to content

Commit

Permalink
feat(SubsurfaceViewer): Record-based definition of layers that suppor…
Browse files Browse the repository at this point in the history
…ts TS Objects implemented. (#1890)

1) typedArraySupport optional flag is added to SubsurfaceViewer API. If
set to true records with layer definitions are used for layer creation
bypassing JSONConverter.
2) PointsLayer, TriangleLayer API are updated to accept typed arrays as
input data skipping extra copying. The corresponding stories are added.
3) MapLayer and Grid3DLayer already support typed array inputs. The
stories updated.

PolylinesLayer has an internal algo for making polylines closed when
requested so its modification is postponed for a while.

Known issue: in the story book "typed array stories" don't work if any
property of layer is changed in Controls Tab. It happens because the
story book deserializes input arrays from JSON represented in Controls
loosing typeinfos.

---------

Co-authored-by: leonid.polukhin <leonid.polukhin@aspentech.com>
  • Loading branch information
LeonidPolukhin and leonid.polukhin committed Jan 30, 2024
1 parent e103adc commit 73df3da
Show file tree
Hide file tree
Showing 8 changed files with 355 additions and 126 deletions.
30 changes: 24 additions & 6 deletions typescript/packages/subsurface-viewer/src/SubsurfaceViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type {

import { TGrid3DColoringMode } from "./layers/grid3d/grid3dLayer";

import Map, { jsonToObject } from "./components/Map";
import Map, { jsonToObject, createLayers } from "./components/Map";
import React from "react";
import PropTypes from "prop-types";
import type { colorTablesArray } from "@emerson-eps/color-tables/";
Expand Down Expand Up @@ -123,6 +123,11 @@ export interface SubsurfaceViewerProps {
lights?: LightsType;

children?: React.ReactNode;

/**
* If set to true allows to use typed arrays in layer description JS objects.
*/
typedArraySupport?: boolean;
}

const SubsurfaceViewer: React.FC<SubsurfaceViewerProps> = ({
Expand Down Expand Up @@ -150,6 +155,7 @@ const SubsurfaceViewer: React.FC<SubsurfaceViewerProps> = ({
triggerResetMultipleWells,
lights,
children,
typedArraySupport,
}: SubsurfaceViewerProps) => {
// Contains layers data received from map layers by user interaction
const [layerEditedData, setLayerEditedData] = React.useState(editedData);
Expand All @@ -162,6 +168,7 @@ const SubsurfaceViewer: React.FC<SubsurfaceViewerProps> = ({
return;
}

//The layers have been already created externally.
if (layers?.[0] instanceof Layer) {
setLayerInstances(layers as LayersList);
return;
Expand All @@ -171,13 +178,24 @@ const SubsurfaceViewer: React.FC<SubsurfaceViewerProps> = ({
if (resources) enumerations.push({ resources: resources });
if (editedData) enumerations.push({ editedData: editedData });
else enumerations.push({ editedData: {} });
const layersList = jsonToObject(
layers as Record<string, unknown>[],
enumerations
) as LayersList;
setLayerInstances(layersList);

//Bypass conversion of layer props through JSON and use them as is.
if (typedArraySupport) {
const layersList = createLayers(
layers as Record<string, unknown>[],
enumerations
) as LayersList;
setLayerInstances(layersList);
} else {
const layersList = jsonToObject(
layers as Record<string, unknown>[],
enumerations
) as LayersList;
setLayerInstances(layersList);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [layers]); // Note. Fixing this dependency list may cause infinite recursion.

React.useEffect(() => {
if (!editedData) return;

Expand Down
55 changes: 51 additions & 4 deletions typescript/packages/subsurface-viewer/src/components/Map.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { useEffect, useState, useCallback, useMemo } from "react";

import type { Feature, FeatureCollection } from "geojson";
Expand All @@ -10,6 +11,7 @@ import type {
} from "mjolnir.js";

import { JSONConfiguration, JSONConverter } from "@deck.gl/json/typed";

import type { DeckGLRef } from "@deck.gl/react/typed";
import DeckGL from "@deck.gl/react/typed";
import type {
Expand Down Expand Up @@ -977,6 +979,35 @@ export function jsonToObject(
): LayersList | View[] {
if (!data) return [];

const configuration = createConfiguration(enums);
const jsonConverter = new JSONConverter({ configuration });

// remove empty data/layer object
const filtered_data = data.filter((value) => !isEmpty(value));
return jsonConverter.convert(filtered_data);
}

export function createLayers(
data: Record<string, unknown>[],
enums: Record<string, unknown>[] | undefined = undefined
): LayersList {
const configuration = createConfiguration(enums);
const layersList: Layer[] = [];

// remove empty data/layer object
const filtered_data = data.filter((value) => !isEmpty(value));
for (const layerData of filtered_data) {
const layer = createLayer(layerData, configuration);
if (layer) {
layersList.push(layer);
}
}
return layersList;
}

function createConfiguration(
enums: Record<string, unknown>[] | undefined = undefined
): JSONConfiguration {
const configuration = new JSONConfiguration(JSON_CONVERTER_CONFIG);
enums?.forEach((enumeration) => {
if (enumeration) {
Expand All @@ -987,11 +1018,27 @@ export function jsonToObject(
});
}
});
const jsonConverter = new JSONConverter({ configuration });
return configuration;
}

// remove empty data/layer object
const filtered_data = data.filter((value) => !isEmpty(value));
return jsonConverter.convert(filtered_data);
function createLayer(
layerData: Record<string, unknown>,
configuration: JSONConfiguration
): Layer | null {
const typeKey = configuration.typeKey;
const classes = configuration.classes as Record<string, any>;
if (layerData[typeKey]) {
const type = layerData[typeKey] as string;
if (type in classes) {
const Class = classes[type];

// Prepare a props object
const props = { ...layerData };
delete props[typeKey];
return new Class(props);
}
}
return null;
}

///////////////////////////////////////////////////////////////////////////////////////////
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,18 @@ Simgrid8xIJonly.parameters = parameters;
const math = create(all, { randomSeed: "1984" });
const randomFunc = math?.random ? math.random : Math.random;

const snubCubePoints = SnubCubePoints.map((v) => 10 * v);
const snubCubeProperties = Array(SnubCubeVertexCount)
.fill(0)
.map(() => randomFunc() * 50);

const toroidPoints = ToroidPoints.map((v) => 10 * v).map((v, index) =>
index % 3 === 0 ? v + 30 : v
);
const toroidProperties = Array(ToroidVertexCount)
.fill(0)
.map(() => randomFunc() * 10);

export const PolyhedralCells = Template.bind({});
PolyhedralCells.args = {
bounds: [-25, -25, 50, 30] as NumberQuad,
Expand All @@ -185,11 +197,9 @@ PolyhedralCells.args = {
id: "polyhedral1",
coloringMode: TGrid3DColoringMode.Y,
pickable: true,
pointsData: SnubCubePoints.map((v) => 10 * v),
pointsData: snubCubePoints,
polysData: SnubCubeFaces,
propertiesData: Array(SnubCubeVertexCount)
.fill(0)
.map(() => randomFunc() * 50),
propertiesData: snubCubeProperties,
colorMapRange: [-8, 8],
colorMapClampColor: [200, 200, 200],
colorMapName: "Porosity",
Expand All @@ -198,15 +208,57 @@ PolyhedralCells.args = {
...grid3dLayer,
id: "polyhedral2",
pickable: true,
pointsData: ToroidPoints.map((v) => 10 * v).map((v, index) =>
index % 3 === 0 ? v + 30 : v
),
pointsData: toroidPoints,
polysData: ToroidFaces,
propertiesData: Array(ToroidVertexCount)
.fill(0)
.map(() => randomFunc() * 10),
propertiesData: toroidProperties,
coloringMode: TGrid3DColoringMode.Property,
},
],
};
PolyhedralCells.parameters = parameters;

export const PolyhedralCellsTypedArrayInput = Template.bind({});

PolyhedralCellsTypedArrayInput.args = {
bounds: [-25, -25, 50, 30] as NumberQuad,
views: {
layout: [1, 1] as [number, number],
viewports: [
{
id: "view_1",
show3D: true,
},
],
},
id: "grid-3d-polyhedral-cell-typed-input",
layers: [
{
...axes,
id: "polyhedral-cells-axes-typed-input",
bounds: [-15, -15, -15, 40, 20, 15],
},
{
...grid3dLayer,
id: "polyhedral1-typed-input",
coloringMode: TGrid3DColoringMode.X,
pickable: true,
pointsData: new Float32Array(snubCubePoints),
polysData: new Uint32Array(SnubCubeFaces),
propertiesData: new Float32Array(snubCubeProperties),
colorMapRange: [-8, 8],
colorMapClampColor: [200, 200, 200],
colorMapName: "Rainbow",
},
{
...grid3dLayer,
id: "polyhedral2-typed-input",
pickable: true,
pointsData: new Float32Array(toroidPoints),
polysData: new Uint32Array(ToroidFaces),
propertiesData: new Float32Array(toroidProperties),
coloringMode: TGrid3DColoringMode.Property,
},
],
typedArraySupport: true,
};
PolyhedralCells.parameters = parameters;
Original file line number Diff line number Diff line change
Expand Up @@ -142,28 +142,28 @@ const cellCenteredPropertiesLayer = {
"@@type": "MapLayer",
id: "cell-centered-layer",

/*eslint-disable */
// One depth pr node
meshData: [
1.6, 1.7, 1.8, 1.9, 1.2, 1.3, 1.4, 1.5, 0.8, 0.9, 1.0, 1.1, 0.4, 0.5,
0.6, 0.7, 0.0, 0.1, 0.2, 0.3,
],

// One property pr cell.
propertiesData: [
0.9,
1.0,
1.1,
0.6,
undefined,
0.8,
0.3,
0.4,
0.5,
0.0,
0.1,
0.2,
],
/*eslint-disable */
// One depth pr node
meshData: [
1.6, 1.7, 1.8, 1.9, 1.2, 1.3, 1.4, 1.5, 0.8, 0.9, 1.0, 1.1, 0.4, 0.5,
0.6, 0.7, 0.0, 0.1, 0.2, 0.3,
],

// One property pr cell.
propertiesData: [
0.9,
1.0,
1.1,
0.6,
undefined,
0.8,
0.3,
0.4,
0.5,
0.0,
0.1,
0.2,
],
/*eslint-enable */

frame: {
Expand Down Expand Up @@ -573,8 +573,9 @@ const TypedArrayInputComponent: React.FC<{
const subsurfaceViewerArgs = {
id: "map",
layers: [
// Can not use Record<string, unknown> because makeData() is not supported
new MapLayer({
{
"@@type": "MapLayer",
id: "mesh-layer-typed-input",
frame: {
origin: [-args.dimension / 2, -args.dimension / 2],
count: [args.dimension, args.dimension],
Expand All @@ -588,8 +589,10 @@ const TypedArrayInputComponent: React.FC<{
ZIncreasingDownwards: false,
contours: [0, 5],
colorMapFunction: nearestColorMap as colorMapFunctionType,
}),
new AxesLayer({
},
{
"@@type": "AxesLayer",
id: "axes-typed-input",
ZIncreasingDownwards: false,
bounds: [
-args.dimension / 2,
Expand All @@ -599,7 +602,7 @@ const TypedArrayInputComponent: React.FC<{
args.dimension / 2,
60,
],
}),
},
],
cameraPosition: {
rotationOrbit: 45,
Expand All @@ -609,6 +612,7 @@ const TypedArrayInputComponent: React.FC<{
},
views: DEFAULT_VIEWS,
triggerHome: args.triggerHome,
typedArraySupport: true,
};
return <SubsurfaceViewer {...subsurfaceViewerArgs} />;
};
Expand Down

0 comments on commit 73df3da

Please sign in to comment.