diff --git a/typescript/packages/subsurface-viewer/src/SubsurfaceViewer.tsx b/typescript/packages/subsurface-viewer/src/SubsurfaceViewer.tsx index 93aba2bb7..d7c5f6a8c 100644 --- a/typescript/packages/subsurface-viewer/src/SubsurfaceViewer.tsx +++ b/typescript/packages/subsurface-viewer/src/SubsurfaceViewer.tsx @@ -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/"; @@ -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 = ({ @@ -150,6 +155,7 @@ const SubsurfaceViewer: React.FC = ({ triggerResetMultipleWells, lights, children, + typedArraySupport, }: SubsurfaceViewerProps) => { // Contains layers data received from map layers by user interaction const [layerEditedData, setLayerEditedData] = React.useState(editedData); @@ -162,6 +168,7 @@ const SubsurfaceViewer: React.FC = ({ return; } + //The layers have been already created externally. if (layers?.[0] instanceof Layer) { setLayerInstances(layers as LayersList); return; @@ -171,13 +178,24 @@ const SubsurfaceViewer: React.FC = ({ if (resources) enumerations.push({ resources: resources }); if (editedData) enumerations.push({ editedData: editedData }); else enumerations.push({ editedData: {} }); - const layersList = jsonToObject( - layers as Record[], - 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[], + enumerations + ) as LayersList; + setLayerInstances(layersList); + } else { + const layersList = jsonToObject( + layers as Record[], + 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; diff --git a/typescript/packages/subsurface-viewer/src/components/Map.tsx b/typescript/packages/subsurface-viewer/src/components/Map.tsx index af782c215..95eb4b86d 100644 --- a/typescript/packages/subsurface-viewer/src/components/Map.tsx +++ b/typescript/packages/subsurface-viewer/src/components/Map.tsx @@ -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"; @@ -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 { @@ -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[], + enums: Record[] | 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[] | undefined = undefined +): JSONConfiguration { const configuration = new JSONConfiguration(JSON_CONVERTER_CONFIG); enums?.forEach((enumeration) => { if (enumeration) { @@ -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, + configuration: JSONConfiguration +): Layer | null { + const typeKey = configuration.typeKey; + const classes = configuration.classes as Record; + 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; } /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/typescript/packages/subsurface-viewer/src/layers/grid3d/grid3dLayer.stories.tsx b/typescript/packages/subsurface-viewer/src/layers/grid3d/grid3dLayer.stories.tsx index d433edcd1..633883fed 100644 --- a/typescript/packages/subsurface-viewer/src/layers/grid3d/grid3dLayer.stories.tsx +++ b/typescript/packages/subsurface-viewer/src/layers/grid3d/grid3dLayer.stories.tsx @@ -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, @@ -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", @@ -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; diff --git a/typescript/packages/subsurface-viewer/src/layers/map/mapLayer.stories.tsx b/typescript/packages/subsurface-viewer/src/layers/map/mapLayer.stories.tsx index 7f9ae6c68..51ac8d753 100644 --- a/typescript/packages/subsurface-viewer/src/layers/map/mapLayer.stories.tsx +++ b/typescript/packages/subsurface-viewer/src/layers/map/mapLayer.stories.tsx @@ -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: { @@ -573,8 +573,9 @@ const TypedArrayInputComponent: React.FC<{ const subsurfaceViewerArgs = { id: "map", layers: [ - // Can not use Record 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], @@ -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, @@ -599,7 +602,7 @@ const TypedArrayInputComponent: React.FC<{ args.dimension / 2, 60, ], - }), + }, ], cameraPosition: { rotationOrbit: 45, @@ -609,6 +612,7 @@ const TypedArrayInputComponent: React.FC<{ }, views: DEFAULT_VIEWS, triggerHome: args.triggerHome, + typedArraySupport: true, }; return ; }; diff --git a/typescript/packages/subsurface-viewer/src/layers/points/pointsLayer.stories.tsx b/typescript/packages/subsurface-viewer/src/layers/points/pointsLayer.stories.tsx index dd491e39b..58ad28366 100644 --- a/typescript/packages/subsurface-viewer/src/layers/points/pointsLayer.stories.tsx +++ b/typescript/packages/subsurface-viewer/src/layers/points/pointsLayer.stories.tsx @@ -16,21 +16,24 @@ const defaultParameters = { }, }; +/*eslint-disable */ +const smallPointsData = [ + 0, 0, 5, // Vertex 1, x, y, z + 10, 0, 5, // Vertex 2, x, y, z + 10, 10, 5, // ... + 0, 10, 0, + 5, -5, 10, + 11, -4, 6, + 11, 0, 7, + 17, 0, 8 +]; +/*eslint-enable */ + // Small example using PointsLayer. const smallPointsLayer = { "@@type": "PointsLayer", id: "small_points_layer", - /*eslint-disable */ - pointsData: [ 0, 0, 5, // Vertex 1, x, y, z - 10, 0, 5, // Vertex 2, x, y, z - 10, 10, 5, // ... - 0, 10, 0, - 5, -5, 10, - 11, -4, 6, - 11, 0, 7, - 17, 0, 8 - ], - /*eslint-enable */ + pointsData: smallPointsData, color: [255, 0, 100], pointRadius: 10, radiusUnits: "pixels", @@ -48,7 +51,7 @@ export const SmallPointsLayer: StoryFn = (args) => { }; SmallPointsLayer.args = { - id: "small-points-map", + id: "small-points", layers: [smallAxesLayer, smallPointsLayer], bounds: [-20, -20, 20, 20], views: { @@ -66,7 +69,48 @@ SmallPointsLayer.parameters = { docs: { ...defaultParameters.docs, description: { - story: "Point coordinates are given as native javascript array.", + story: "Point coordinates are given as native JavaScript array.", + }, + }, +}; + +const smallPointsTypedDataLayer = { + "@@type": "PointsLayer", + id: "small_points_typed_data_layer", + pointsData: new Float32Array(smallPointsData), + color: [0, 100, 255], + pointRadius: 10, + radiusUnits: "pixels", + ZIncreasingDownwards: true, +}; + +export const SmallPointsLayerTypedArrayInput: StoryFn< + typeof SubsurfaceViewer +> = (args) => { + return ; +}; + +SmallPointsLayerTypedArrayInput.args = { + id: "small-points-typeddata", + layers: [smallAxesLayer, smallPointsTypedDataLayer], + bounds: [-20, -20, 20, 20], + views: { + layout: [1, 1], + viewports: [ + { + id: "view_1", + show3D: true, + }, + ], + }, + typedArraySupport: true, +}; + +SmallPointsLayerTypedArrayInput.parameters = { + docs: { + ...defaultParameters.docs, + description: { + story: "Point coordinates are given as JavaScript typed array.", }, }, }; @@ -133,7 +177,7 @@ HugePointsLayer.parameters = { docs: { ...defaultParameters.docs, description: { - story: "Point coordinates are randomly generated in runtime and given as native javascript array.", + story: "Point coordinates are randomly generated in runtime and given as native JavaScript array.", }, }, }; diff --git a/typescript/packages/subsurface-viewer/src/layers/points/pointsLayer.ts b/typescript/packages/subsurface-viewer/src/layers/points/pointsLayer.ts index ef3b257a4..9c622a7ec 100644 --- a/typescript/packages/subsurface-viewer/src/layers/points/pointsLayer.ts +++ b/typescript/packages/subsurface-viewer/src/layers/points/pointsLayer.ts @@ -16,7 +16,7 @@ export interface PointsLayerProps extends ExtendedLayerProps { /** * Point positions as [x, y, z, x, y, z....]. */ - pointsData: number[]; + pointsData: number[] | Float32Array; /** * Point color defined as RGB or RGBA array. Each component is in 0-255 range. @@ -70,6 +70,10 @@ interface IDataAttributes { } export default class PointsLayer extends CompositeLayer { + constructor(props: PointsLayerProps) { + super(props); + } + renderLayers(): [PrivatePointsLayer?] { const layer = new PrivatePointsLayer( this.getSubLayerProps({ @@ -152,6 +156,10 @@ export default class PointsLayer extends CompositeLayer { if (Array.isArray(this.props.pointsData)) { return new Float32Array(this.props.pointsData); } + if (this.props.pointsData instanceof Float32Array) { + return this.props.pointsData; + } + console.warn("pointsData is not array"); return new Float32Array(); } diff --git a/typescript/packages/subsurface-viewer/src/layers/triangle/triangleLayer.stories.tsx b/typescript/packages/subsurface-viewer/src/layers/triangle/triangleLayer.stories.tsx index 7e8f20f98..af5ca4cf6 100644 --- a/typescript/packages/subsurface-viewer/src/layers/triangle/triangleLayer.stories.tsx +++ b/typescript/packages/subsurface-viewer/src/layers/triangle/triangleLayer.stories.tsx @@ -27,30 +27,30 @@ const triangleLayer = { "@@type": "TriangleLayer", id: "triangle-layer", - /*eslint-disable */ - pointsData: [ 0, 0, 5, // Vertex 1, x, y, z - 10, 0, 5, // Vertex 2, x, y, z - 10, 10, 5, // ... - 0, 10, 0, - 5, -5, 10, - 11, -4, 6, - 11, 0, 7, - 17, 0, 8 - ], - - - triangleData: [2, 1, 0, // Indexs' to first triangle. - 3, 2, 0, // ... - 1, 4, 0, - 6, 7, 5], - - - color: [100, 100, 255], // Surface color. - gridLines: true, // If true will draw lines around triangles. - material: true, // If true will use triangle normals for shading. - smoothShading: true, // If true will use vertex calculated mean normals for shading. - ZIncreasingDownwards: true, - //contours: [0, 1], // If used will display contour lines. + /*eslint-disable */ + pointsData: [ 0, 0, 5, // Vertex 1, x, y, z + 10, 0, 5, // Vertex 2, x, y, z + 10, 10, 5, // ... + 0, 10, 0, + 5, -5, 10, + 11, -4, 6, + 11, 0, 7, + 17, 0, 8 + ], + + + triangleData: [2, 1, 0, // Indexs' to first triangle. + 3, 2, 0, // ... + 1, 4, 0, + 6, 7, 5], + + + color: [100, 100, 255], // Surface color. + gridLines: true, // If true will draw lines around triangles. + material: true, // If true will use triangle normals for shading. + smoothShading: true, // If true will use vertex calculated mean normals for shading. + ZIncreasingDownwards: true, + //contours: [0, 1], // If used will display contour lines. /*eslint-enable */ }; @@ -114,21 +114,21 @@ const upperSurfaceLayer = { "@@type": "TriangleLayer", id: "upper_surface_layer", - /*eslint-disable */ - pointsData: SurfacePoints.default, - triangleData: SurfaceTriangles.default, - - color: [100, 100, 255], // Surface color. - gridLines: true, // If true will draw lines around triangles. - material: { - ambient: 0.35, - diffuse: 0.6, - shininess: 100, - specularColor: [255, 255, 255] - }, // If true will use triangle normals for shading. - smoothShading: true, // If true will use vertex calculated mean normals for shading. - ZIncreasingDownwards: true, - debug: true + /*eslint-disable */ + pointsData: SurfacePoints.default, + triangleData: SurfaceTriangles.default, + + color: [100, 100, 255], // Surface color. + gridLines: true, // If true will draw lines around triangles. + material: { + ambient: 0.35, + diffuse: 0.6, + shininess: 100, + specularColor: [255, 255, 255] + }, // If true will use triangle normals for shading. + smoothShading: true, // If true will use vertex calculated mean normals for shading. + ZIncreasingDownwards: true, + debug: true /*eslint-enable */ }; @@ -136,21 +136,21 @@ const lowerSurfaceLayer = { "@@type": "TriangleLayer", id: "lowers_surface_layer", - /*eslint-disable */ - pointsData: shiftPointsByZ(SurfacePoints.default, 1000), - triangleData: flipOrientation(SurfaceTriangles.default), - - color: [100, 255, 100], // Surface color. - gridLines: true, // If true will draw lines around triangles. - material: { - ambient: 0.35, - diffuse: 0.6, - shininess: 100, - specularColor: [255, 255, 255] - }, // If true will use triangle normals for shading. - smoothShading: true, // If true will use vertex calculated mean normals for shading. - ZIncreasingDownwards: true, - debug: true + /*eslint-disable */ + pointsData: shiftPointsByZ(SurfacePoints.default, 1000), + triangleData: flipOrientation(SurfaceTriangles.default), + + color: [100, 255, 100], // Surface color. + gridLines: true, // If true will draw lines around triangles. + material: { + ambient: 0.35, + diffuse: 0.6, + shininess: 100, + specularColor: [255, 255, 255] + }, // If true will use triangle normals for shading. + smoothShading: true, // If true will use vertex calculated mean normals for shading. + ZIncreasingDownwards: true, + debug: true /*eslint-enable */ }; @@ -189,3 +189,55 @@ TwoSideLighting.parameters = { }, }, }; + +const typedDataSurfaceLayer = { + "@@type": "TriangleLayer", + id: "typedData_surface_layer", + + /*eslint-disable */ + pointsData: new Float32Array(SurfacePoints.default), + triangleData: new Uint32Array(SurfaceTriangles.default), + + color: [100, 100, 255], // Surface color. + gridLines: true, // If true will draw lines around triangles. + material: { + ambient: 0.35, + diffuse: 0.6, + shininess: 100, + specularColor: [255, 255, 255] + }, // If true will use triangle normals for shading. + smoothShading: true, // If true will use vertex calculated mean normals for shading. + ZIncreasingDownwards: true, + /*eslint-enable */ +}; + +export const TypedArrayInput: ComponentStory = ( + args +) => { + return ; +}; + +TypedArrayInput.args = { + id: "map", + layers: [surfaceAxesLayer, typedDataSurfaceLayer], + bounds: [-2000, -2000, 2500, 2000], + views: { + layout: [1, 1], + viewports: [ + { + id: "view_1", + show3D: true, + }, + ], + }, + typedArraySupport: true, +}; + +TypedArrayInput.parameters = { + docs: { + ...defaultParameters.docs, + description: { + story: "Surface data is provided as typed arrays.", + }, + }, +}; diff --git a/typescript/packages/subsurface-viewer/src/layers/triangle/triangleLayer.ts b/typescript/packages/subsurface-viewer/src/layers/triangle/triangleLayer.ts index 9146b262a..aa8a65c4a 100644 --- a/typescript/packages/subsurface-viewer/src/layers/triangle/triangleLayer.ts +++ b/typescript/packages/subsurface-viewer/src/layers/triangle/triangleLayer.ts @@ -16,8 +16,8 @@ export type Params = { }; async function loadData( - pointsData: string | number[], - triangleData: string | number[] + pointsData: string | number[] | Float32Array, + triangleData: string | number[] | Uint32Array ) { // Keep //const t0 = performance.now(); @@ -27,6 +27,8 @@ async function loadData( if (Array.isArray(pointsData)) { // Input data is native javascript array. vertexArray = new Float32Array(pointsData); + } else if (pointsData instanceof Float32Array) { + vertexArray = pointsData; } else { // Input data is an URL. const response_mesh = await fetch(pointsData); @@ -46,6 +48,8 @@ async function loadData( if (Array.isArray(triangleData)) { // Input data is native javascript array. indexArray = new Uint32Array(triangleData); + } else if (triangleData instanceof Uint32Array) { + indexArray = triangleData; } else { // Input data is an URL. const response_mesh = await fetch(triangleData); @@ -71,9 +75,9 @@ export interface TriangleLayerProps extends ExtendedLayerProps { /** Triangle vertexes. * Either an URL or an array of numbers. */ - pointsData: string | number[]; + pointsData: string | number[] | Float32Array; - triangleData: string | number[]; + triangleData: string | number[] | Uint32Array; color: [number, number, number]; @@ -197,14 +201,14 @@ export default class TriangleLayer extends CompositeLayer { let zmin = 99999999; for (let i = 0; i < vertexArray.length / 3; i++) { - xmax = vertexArray[3 * i + 0] > xmax ? vertexArray[3 * i + 0] : xmax; //eslint-disable-line - xmin = vertexArray[3 * i + 0] < xmin ? vertexArray[3 * i + 0] : xmin; //eslint-disable-line + xmax = vertexArray[3 * i + 0] > xmax ? vertexArray[3 * i + 0] : xmax; //eslint-disable-line + xmin = vertexArray[3 * i + 0] < xmin ? vertexArray[3 * i + 0] : xmin; //eslint-disable-line - ymax = vertexArray[3 * i + 1] > ymax ? vertexArray[3 * i + 1] : ymax; //eslint-disable-line - ymin = vertexArray[3 * i + 1] < ymin ? vertexArray[3 * i + 1] : ymin; //eslint-disable-line + ymax = vertexArray[3 * i + 1] > ymax ? vertexArray[3 * i + 1] : ymax; //eslint-disable-line + ymin = vertexArray[3 * i + 1] < ymin ? vertexArray[3 * i + 1] : ymin; //eslint-disable-line - zmax = vertexArray[3 * i + 2] > zmax ? vertexArray[3 * i + 2] : zmax; //eslint-disable-line - zmin = vertexArray[3 * i + 2] < zmin ? vertexArray[3 * i + 2] : zmin; //eslint-disable-line + zmax = vertexArray[3 * i + 2] > zmax ? vertexArray[3 * i + 2] : zmax; //eslint-disable-line + zmin = vertexArray[3 * i + 2] < zmin ? vertexArray[3 * i + 2] : zmin; //eslint-disable-line } if (this.props.ZIncreasingDownwards) {