UNPKG

@deck.gl/layers

Version:
217 lines (216 loc) 8.66 kB
// Copyright (c) 2015 - 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import { Layer, project32, gouraudLighting, phongLighting, picking, UNIT } from '@deck.gl/core'; import GL from '@luma.gl/constants'; import { Model, isWebGL2, hasFeature, FEATURES } from '@luma.gl/core'; import ColumnGeometry from './column-geometry'; import vs from './column-layer-vertex.glsl'; import fs from './column-layer-fragment.glsl'; const DEFAULT_COLOR = [0, 0, 0, 255]; const defaultProps = { diskResolution: { type: 'number', min: 4, value: 20 }, vertices: null, radius: { type: 'number', min: 0, value: 1000 }, angle: { type: 'number', value: 0 }, offset: { type: 'array', value: [0, 0] }, coverage: { type: 'number', min: 0, max: 1, value: 1 }, elevationScale: { type: 'number', min: 0, value: 1 }, radiusUnits: 'meters', lineWidthUnits: 'meters', lineWidthScale: 1, lineWidthMinPixels: 0, lineWidthMaxPixels: Number.MAX_SAFE_INTEGER, extruded: true, wireframe: false, filled: true, stroked: false, getPosition: { type: 'accessor', value: x => x.position }, getFillColor: { type: 'accessor', value: DEFAULT_COLOR }, getLineColor: { type: 'accessor', value: DEFAULT_COLOR }, getLineWidth: { type: 'accessor', value: 1 }, getElevation: { type: 'accessor', value: 1000 }, material: true, getColor: { deprecatedFor: ['getFillColor', 'getLineColor'] } }; export default class ColumnLayer extends Layer { getShaders() { const { gl } = this.context; const transpileToGLSL100 = !isWebGL2(gl); const defines = {}; const useDerivatives = this.props.flatShading && hasFeature(gl, FEATURES.GLSL_DERIVATIVES); if (useDerivatives) { defines.FLAT_SHADING = 1; } return super.getShaders({ vs, fs, defines, transpileToGLSL100, modules: [project32, useDerivatives ? phongLighting : gouraudLighting, picking] }); } /** * DeckGL calls initializeState when GL context is available * Essentially a deferred constructor */ initializeState() { const attributeManager = this.getAttributeManager(); /* eslint-disable max-len */ attributeManager.addInstanced({ instancePositions: { size: 3, type: GL.DOUBLE, fp64: this.use64bitPositions(), transition: true, accessor: 'getPosition' }, instanceElevations: { size: 1, transition: true, accessor: 'getElevation' }, instanceFillColors: { size: this.props.colorFormat.length, type: GL.UNSIGNED_BYTE, normalized: true, transition: true, accessor: 'getFillColor', defaultValue: DEFAULT_COLOR }, instanceLineColors: { size: this.props.colorFormat.length, type: GL.UNSIGNED_BYTE, normalized: true, transition: true, accessor: 'getLineColor', defaultValue: DEFAULT_COLOR }, instanceStrokeWidths: { size: 1, accessor: 'getLineWidth', transition: true } }); /* eslint-enable max-len */ } updateState(params) { super.updateState(params); const { props, oldProps, changeFlags } = params; const regenerateModels = changeFlags.extensionsChanged || props.flatShading !== oldProps.flatShading; if (regenerateModels) { const { gl } = this.context; this.state.model?.delete(); this.state.model = this._getModel(gl); this.getAttributeManager().invalidateAll(); } if (regenerateModels || props.diskResolution !== oldProps.diskResolution || props.vertices !== oldProps.vertices || (props.extruded || props.stroked) !== (oldProps.extruded || oldProps.stroked)) { this._updateGeometry(props); } } getGeometry(diskResolution, vertices, hasThinkness) { const geometry = new ColumnGeometry({ radius: 1, height: hasThinkness ? 2 : 0, vertices, nradial: diskResolution }); let meanVertexDistance = 0; if (vertices) { for (let i = 0; i < diskResolution; i++) { const p = vertices[i]; const d = Math.sqrt(p[0] * p[0] + p[1] * p[1]); meanVertexDistance += d / diskResolution; } } else { meanVertexDistance = 1; } this.setState({ edgeDistance: Math.cos(Math.PI / diskResolution) * meanVertexDistance }); return geometry; } _getModel(gl) { return new Model(gl, { ...this.getShaders(), id: this.props.id, isInstanced: true }); } _updateGeometry({ diskResolution, vertices, extruded, stroked }) { const geometry = this.getGeometry(diskResolution, vertices, extruded || stroked); this.setState({ fillVertexCount: geometry.attributes.POSITION.value.length / 3, wireframeVertexCount: geometry.indices.value.length }); this.state.model.setProps({ geometry }); } draw({ uniforms }) { const { lineWidthUnits, lineWidthScale, lineWidthMinPixels, lineWidthMaxPixels, radiusUnits, elevationScale, extruded, filled, stroked, wireframe, offset, coverage, radius, angle } = this.props; const { model, fillVertexCount, wireframeVertexCount, edgeDistance } = this.state; model.setUniforms(uniforms).setUniforms({ radius, angle: (angle / 180) * Math.PI, offset, extruded, coverage, elevationScale, edgeDistance, radiusUnits: UNIT[radiusUnits], widthUnits: UNIT[lineWidthUnits], widthScale: lineWidthScale, widthMinPixels: lineWidthMinPixels, widthMaxPixels: lineWidthMaxPixels }); // When drawing 3d: draw wireframe first so it doesn't get occluded by depth test if (extruded && wireframe) { model.setProps({ isIndexed: true }); model .setVertexCount(wireframeVertexCount) .setDrawMode(GL.LINES) .setUniforms({ isStroke: true }) .draw(); } if (filled) { model.setProps({ isIndexed: false }); model .setVertexCount(fillVertexCount) .setDrawMode(GL.TRIANGLE_STRIP) .setUniforms({ isStroke: false }) .draw(); } // When drawing 2d: draw fill before stroke so that the outline is always on top if (!extruded && stroked) { model.setProps({ isIndexed: false }); // The width of the stroke is achieved by flattening the side of the cylinder. // Skip the last 1/3 of the vertices which is the top. model .setVertexCount((fillVertexCount * 2) / 3) .setDrawMode(GL.TRIANGLE_STRIP) .setUniforms({ isStroke: true }) .draw(); } } } ColumnLayer.layerName = 'ColumnLayer'; ColumnLayer.defaultProps = defaultProps;