diff --git a/src/ol/render/webgl/utils.js b/src/ol/render/webgl/utils.js new file mode 100644 index 0000000000..3ddd7ebac6 --- /dev/null +++ b/src/ol/render/webgl/utils.js @@ -0,0 +1,142 @@ +/** + * @module ol/render/webgl/utils + */ +const tmpArray_ = []; +const bufferPositions_ = {vertexPosition: 0, indexPosition: 0}; + +function writePointVertex(buffer, pos, x, y, index) { + buffer[pos + 0] = x; + buffer[pos + 1] = y; + buffer[pos + 2] = index; +} + +/** + * An object holding positions both in an index and a vertex buffer. + * @typedef {Object} BufferPositions + * @property {number} vertexPosition Position in the vertex buffer + * @property {number} indexPosition Position in the index buffer + */ + +/** + * Pushes a quad (two triangles) based on a point geometry + * @param {Float32Array} instructions Array of render instructions for points. + * @param {number} elementIndex Index from which render instructions will be read. + * @param {Float32Array} vertexBuffer Buffer in the form of a typed array. + * @param {Uint32Array} indexBuffer Buffer in the form of a typed array. + * @param {number} customAttributesCount Amount of custom attributes for each element. + * @param {BufferPositions} [bufferPositions] Buffer write positions; if not specified, positions will be set at 0. + * @return {BufferPositions} New buffer positions where to write next + * @property {number} vertexPosition New position in the vertex buffer where future writes should start. + * @property {number} indexPosition New position in the index buffer where future writes should start. + * @private + */ +export function writePointFeatureToBuffers( + instructions, + elementIndex, + vertexBuffer, + indexBuffer, + customAttributesCount, + bufferPositions +) { + // This is for x, y and index + const baseVertexAttrsCount = 3; + const baseInstructionsCount = 2; + const stride = baseVertexAttrsCount + customAttributesCount; + + const x = instructions[elementIndex + 0]; + const y = instructions[elementIndex + 1]; + + // read custom numerical attributes on the feature + const customAttrs = tmpArray_; + customAttrs.length = customAttributesCount; + for (let i = 0; i < customAttrs.length; i++) { + customAttrs[i] = instructions[elementIndex + baseInstructionsCount + i]; + } + + let vPos = bufferPositions ? bufferPositions.vertexPosition : 0; + let iPos = bufferPositions ? bufferPositions.indexPosition : 0; + const baseIndex = vPos / stride; + + // push vertices for each of the four quad corners (first standard then custom attributes) + writePointVertex(vertexBuffer, vPos, x, y, 0); + customAttrs.length && + vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount); + vPos += stride; + + writePointVertex(vertexBuffer, vPos, x, y, 1); + customAttrs.length && + vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount); + vPos += stride; + + writePointVertex(vertexBuffer, vPos, x, y, 2); + customAttrs.length && + vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount); + vPos += stride; + + writePointVertex(vertexBuffer, vPos, x, y, 3); + customAttrs.length && + vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount); + vPos += stride; + + indexBuffer[iPos++] = baseIndex; + indexBuffer[iPos++] = baseIndex + 1; + indexBuffer[iPos++] = baseIndex + 3; + indexBuffer[iPos++] = baseIndex + 1; + indexBuffer[iPos++] = baseIndex + 2; + indexBuffer[iPos++] = baseIndex + 3; + + bufferPositions_.vertexPosition = vPos; + bufferPositions_.indexPosition = iPos; + + return bufferPositions_; +} + +/** + * Returns a texture of 1x1 pixel, white + * @private + * @return {ImageData} Image data. + */ +export function getBlankImageData() { + const canvas = document.createElement('canvas'); + const image = canvas.getContext('2d').createImageData(1, 1); + image.data[0] = 255; + image.data[1] = 255; + image.data[2] = 255; + image.data[3] = 255; + return image; +} + +/** + * Generates a color array based on a numerical id + * Note: the range for each component is 0 to 1 with 256 steps + * @param {number} id Id + * @param {Array} [opt_array] Reusable array + * @return {Array} Color array containing the encoded id + */ +export function colorEncodeId(id, opt_array) { + const array = opt_array || []; + const radix = 256; + const divide = radix - 1; + array[0] = Math.floor(id / radix / radix / radix) / divide; + array[1] = (Math.floor(id / radix / radix) % radix) / divide; + array[2] = (Math.floor(id / radix) % radix) / divide; + array[3] = (id % radix) / divide; + return array; +} + +/** + * Reads an id from a color-encoded array + * Note: the expected range for each component is 0 to 1 with 256 steps. + * @param {Array} color Color array containing the encoded id + * @return {number} Decoded id + */ +export function colorDecodeId(color) { + let id = 0; + const radix = 256; + const mult = radix - 1; + id += Math.round(color[0] * radix * radix * radix * mult); + id += Math.round(color[1] * radix * radix * mult); + id += Math.round(color[2] * radix * mult); + id += Math.round(color[3] * mult); + return id; +} diff --git a/src/ol/renderer/webgl/Layer.js b/src/ol/renderer/webgl/Layer.js index a6d9b1c1b4..1587e8c9d8 100644 --- a/src/ol/renderer/webgl/Layer.js +++ b/src/ol/renderer/webgl/Layer.js @@ -13,26 +13,6 @@ import { } from '../../transform.js'; import {containsCoordinate} from '../../extent.js'; -/** - * @enum {string} - */ -export const WebGLWorkerMessageType = { - GENERATE_BUFFERS: 'GENERATE_BUFFERS', -}; - -/** - * @typedef {Object} WebGLWorkerGenerateBuffersMessage - * This message will trigger the generation of a vertex and an index buffer based on the given render instructions. - * When the buffers are generated, the worked will send a message of the same type to the main thread, with - * the generated buffers in it. - * Note that any addition properties present in the message *will* be sent back to the main thread. - * @property {WebGLWorkerMessageType} type Message type - * @property {ArrayBuffer} renderInstructions Render instructions raw binary buffer. - * @property {ArrayBuffer} [vertexBuffer] Vertices array raw binary buffer (sent by the worker). - * @property {ArrayBuffer} [indexBuffer] Indices array raw binary buffer (sent by the worker). - * @property {number} [customAttributesCount] Amount of custom attributes count in the render instructions. - */ - /** * @typedef {Object} PostProcessesOptions * @property {number} [scaleRatio] Scale ratio; if < 1, the post process will render to a texture smaller than @@ -343,144 +323,4 @@ class WebGLLayerRenderer extends LayerRenderer { } } -const tmpArray_ = []; -const bufferPositions_ = {vertexPosition: 0, indexPosition: 0}; - -function writePointVertex(buffer, pos, x, y, index) { - buffer[pos + 0] = x; - buffer[pos + 1] = y; - buffer[pos + 2] = index; -} - -/** - * An object holding positions both in an index and a vertex buffer. - * @typedef {Object} BufferPositions - * @property {number} vertexPosition Position in the vertex buffer - * @property {number} indexPosition Position in the index buffer - */ - -/** - * Pushes a quad (two triangles) based on a point geometry - * @param {Float32Array} instructions Array of render instructions for points. - * @param {number} elementIndex Index from which render instructions will be read. - * @param {Float32Array} vertexBuffer Buffer in the form of a typed array. - * @param {Uint32Array} indexBuffer Buffer in the form of a typed array. - * @param {number} customAttributesCount Amount of custom attributes for each element. - * @param {BufferPositions} [bufferPositions] Buffer write positions; if not specified, positions will be set at 0. - * @return {BufferPositions} New buffer positions where to write next - * @property {number} vertexPosition New position in the vertex buffer where future writes should start. - * @property {number} indexPosition New position in the index buffer where future writes should start. - * @private - */ -export function writePointFeatureToBuffers( - instructions, - elementIndex, - vertexBuffer, - indexBuffer, - customAttributesCount, - bufferPositions -) { - // This is for x, y and index - const baseVertexAttrsCount = 3; - const baseInstructionsCount = 2; - const stride = baseVertexAttrsCount + customAttributesCount; - - const x = instructions[elementIndex + 0]; - const y = instructions[elementIndex + 1]; - - // read custom numerical attributes on the feature - const customAttrs = tmpArray_; - customAttrs.length = customAttributesCount; - for (let i = 0; i < customAttrs.length; i++) { - customAttrs[i] = instructions[elementIndex + baseInstructionsCount + i]; - } - - let vPos = bufferPositions ? bufferPositions.vertexPosition : 0; - let iPos = bufferPositions ? bufferPositions.indexPosition : 0; - const baseIndex = vPos / stride; - - // push vertices for each of the four quad corners (first standard then custom attributes) - writePointVertex(vertexBuffer, vPos, x, y, 0); - customAttrs.length && - vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount); - vPos += stride; - - writePointVertex(vertexBuffer, vPos, x, y, 1); - customAttrs.length && - vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount); - vPos += stride; - - writePointVertex(vertexBuffer, vPos, x, y, 2); - customAttrs.length && - vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount); - vPos += stride; - - writePointVertex(vertexBuffer, vPos, x, y, 3); - customAttrs.length && - vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount); - vPos += stride; - - indexBuffer[iPos++] = baseIndex; - indexBuffer[iPos++] = baseIndex + 1; - indexBuffer[iPos++] = baseIndex + 3; - indexBuffer[iPos++] = baseIndex + 1; - indexBuffer[iPos++] = baseIndex + 2; - indexBuffer[iPos++] = baseIndex + 3; - - bufferPositions_.vertexPosition = vPos; - bufferPositions_.indexPosition = iPos; - - return bufferPositions_; -} - -/** - * Returns a texture of 1x1 pixel, white - * @private - * @return {ImageData} Image data. - */ -export function getBlankImageData() { - const canvas = document.createElement('canvas'); - const image = canvas.getContext('2d').createImageData(1, 1); - image.data[0] = 255; - image.data[1] = 255; - image.data[2] = 255; - image.data[3] = 255; - return image; -} - -/** - * Generates a color array based on a numerical id - * Note: the range for each component is 0 to 1 with 256 steps - * @param {number} id Id - * @param {Array} [opt_array] Reusable array - * @return {Array} Color array containing the encoded id - */ -export function colorEncodeId(id, opt_array) { - const array = opt_array || []; - const radix = 256; - const divide = radix - 1; - array[0] = Math.floor(id / radix / radix / radix) / divide; - array[1] = (Math.floor(id / radix / radix) % radix) / divide; - array[2] = (Math.floor(id / radix) % radix) / divide; - array[3] = (id % radix) / divide; - return array; -} - -/** - * Reads an id from a color-encoded array - * Note: the expected range for each component is 0 to 1 with 256 steps. - * @param {Array} color Color array containing the encoded id - * @return {number} Decoded id - */ -export function colorDecodeId(color) { - let id = 0; - const radix = 256; - const mult = radix - 1; - id += Math.round(color[0] * radix * radix * radix * mult); - id += Math.round(color[1] * radix * radix * mult); - id += Math.round(color[2] * radix * mult); - id += Math.round(color[3] * mult); - return id; -} - export default WebGLLayerRenderer; diff --git a/test/browser/spec/ol/render/webgl/utils.test.js b/test/browser/spec/ol/render/webgl/utils.test.js new file mode 100644 index 0000000000..9adac57c38 --- /dev/null +++ b/test/browser/spec/ol/render/webgl/utils.test.js @@ -0,0 +1,207 @@ +import { + colorDecodeId, + colorEncodeId, + getBlankImageData, + writePointFeatureToBuffers, +} from '../../../../../../src/ol/render/webgl/utils.js'; + +describe('webgl render utils', function () { + describe('writePointFeatureToBuffers', function () { + let vertexBuffer, indexBuffer, instructions; + + beforeEach(function () { + vertexBuffer = new Float32Array(100); + indexBuffer = new Uint32Array(100); + instructions = new Float32Array(100); + + instructions.set([0, 0, 0, 0, 10, 11]); + }); + + it('writes correctly to the buffers (without custom attributes)', function () { + const stride = 3; + const positions = writePointFeatureToBuffers( + instructions, + 4, + vertexBuffer, + indexBuffer, + 0 + ); + + expect(vertexBuffer[0]).to.eql(10); + expect(vertexBuffer[1]).to.eql(11); + expect(vertexBuffer[2]).to.eql(0); + + expect(vertexBuffer[stride + 0]).to.eql(10); + expect(vertexBuffer[stride + 1]).to.eql(11); + expect(vertexBuffer[stride + 2]).to.eql(1); + + expect(vertexBuffer[stride * 2 + 0]).to.eql(10); + expect(vertexBuffer[stride * 2 + 1]).to.eql(11); + expect(vertexBuffer[stride * 2 + 2]).to.eql(2); + + expect(vertexBuffer[stride * 3 + 0]).to.eql(10); + expect(vertexBuffer[stride * 3 + 1]).to.eql(11); + expect(vertexBuffer[stride * 3 + 2]).to.eql(3); + + expect(indexBuffer[0]).to.eql(0); + expect(indexBuffer[1]).to.eql(1); + expect(indexBuffer[2]).to.eql(3); + expect(indexBuffer[3]).to.eql(1); + expect(indexBuffer[4]).to.eql(2); + expect(indexBuffer[5]).to.eql(3); + + expect(positions.indexPosition).to.eql(6); + expect(positions.vertexPosition).to.eql(stride * 4); + }); + + it('writes correctly to the buffers (with 2 custom attributes)', function () { + instructions.set([0, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13]); + const stride = 5; + const positions = writePointFeatureToBuffers( + instructions, + 8, + vertexBuffer, + indexBuffer, + 2 + ); + + expect(vertexBuffer[0]).to.eql(10); + expect(vertexBuffer[1]).to.eql(11); + expect(vertexBuffer[2]).to.eql(0); + expect(vertexBuffer[3]).to.eql(12); + expect(vertexBuffer[4]).to.eql(13); + + expect(vertexBuffer[stride + 0]).to.eql(10); + expect(vertexBuffer[stride + 1]).to.eql(11); + expect(vertexBuffer[stride + 2]).to.eql(1); + expect(vertexBuffer[stride + 3]).to.eql(12); + expect(vertexBuffer[stride + 4]).to.eql(13); + + expect(vertexBuffer[stride * 2 + 0]).to.eql(10); + expect(vertexBuffer[stride * 2 + 1]).to.eql(11); + expect(vertexBuffer[stride * 2 + 2]).to.eql(2); + expect(vertexBuffer[stride * 2 + 3]).to.eql(12); + expect(vertexBuffer[stride * 2 + 4]).to.eql(13); + + expect(vertexBuffer[stride * 3 + 0]).to.eql(10); + expect(vertexBuffer[stride * 3 + 1]).to.eql(11); + expect(vertexBuffer[stride * 3 + 2]).to.eql(3); + expect(vertexBuffer[stride * 3 + 3]).to.eql(12); + expect(vertexBuffer[stride * 3 + 4]).to.eql(13); + + expect(indexBuffer[0]).to.eql(0); + expect(indexBuffer[1]).to.eql(1); + expect(indexBuffer[2]).to.eql(3); + expect(indexBuffer[3]).to.eql(1); + expect(indexBuffer[4]).to.eql(2); + expect(indexBuffer[5]).to.eql(3); + + expect(positions.indexPosition).to.eql(6); + expect(positions.vertexPosition).to.eql(stride * 4); + }); + + it('correctly chains buffer writes', function () { + instructions.set([10, 11, 20, 21, 30, 31]); + const stride = 3; + let positions = writePointFeatureToBuffers( + instructions, + 0, + vertexBuffer, + indexBuffer, + 0 + ); + positions = writePointFeatureToBuffers( + instructions, + 2, + vertexBuffer, + indexBuffer, + 0, + positions + ); + positions = writePointFeatureToBuffers( + instructions, + 4, + vertexBuffer, + indexBuffer, + 0, + positions + ); + + expect(vertexBuffer[0]).to.eql(10); + expect(vertexBuffer[1]).to.eql(11); + expect(vertexBuffer[2]).to.eql(0); + + expect(vertexBuffer[stride * 4 + 0]).to.eql(20); + expect(vertexBuffer[stride * 4 + 1]).to.eql(21); + expect(vertexBuffer[stride * 4 + 2]).to.eql(0); + + expect(vertexBuffer[stride * 8 + 0]).to.eql(30); + expect(vertexBuffer[stride * 8 + 1]).to.eql(31); + expect(vertexBuffer[stride * 8 + 2]).to.eql(0); + + expect(indexBuffer[6 + 0]).to.eql(4); + expect(indexBuffer[6 + 1]).to.eql(5); + expect(indexBuffer[6 + 2]).to.eql(7); + expect(indexBuffer[6 + 3]).to.eql(5); + expect(indexBuffer[6 + 4]).to.eql(6); + expect(indexBuffer[6 + 5]).to.eql(7); + + expect(indexBuffer[6 * 2 + 0]).to.eql(8); + expect(indexBuffer[6 * 2 + 1]).to.eql(9); + expect(indexBuffer[6 * 2 + 2]).to.eql(11); + expect(indexBuffer[6 * 2 + 3]).to.eql(9); + expect(indexBuffer[6 * 2 + 4]).to.eql(10); + expect(indexBuffer[6 * 2 + 5]).to.eql(11); + + expect(positions.indexPosition).to.eql(6 * 3); + expect(positions.vertexPosition).to.eql(stride * 4 * 3); + }); + }); + + describe('getBlankImageData', function () { + it('creates a 1x1 white texture', function () { + const texture = getBlankImageData(); + expect(texture.height).to.eql(1); + expect(texture.width).to.eql(1); + expect(texture.data[0]).to.eql(255); + expect(texture.data[1]).to.eql(255); + expect(texture.data[2]).to.eql(255); + expect(texture.data[3]).to.eql(255); + }); + }); + + describe('colorEncodeId and colorDecodeId', function () { + it('correctly encodes and decodes ids', function () { + expect(colorDecodeId(colorEncodeId(0))).to.eql(0); + expect(colorDecodeId(colorEncodeId(1))).to.eql(1); + expect(colorDecodeId(colorEncodeId(123))).to.eql(123); + expect(colorDecodeId(colorEncodeId(12345))).to.eql(12345); + expect(colorDecodeId(colorEncodeId(123456))).to.eql(123456); + expect(colorDecodeId(colorEncodeId(91612))).to.eql(91612); + expect(colorDecodeId(colorEncodeId(1234567890))).to.eql(1234567890); + }); + + it('correctly reuses array', function () { + const arr = []; + expect(colorEncodeId(123, arr)).to.be(arr); + }); + + it('is compatible with Uint8Array storage', function () { + const encoded = colorEncodeId(91612); + const typed = Uint8Array.of( + encoded[0] * 255, + encoded[1] * 255, + encoded[2] * 255, + encoded[3] * 255 + ); + const arr = [ + typed[0] / 255, + typed[1] / 255, + typed[2] / 255, + typed[3] / 255, + ]; + const decoded = colorDecodeId(arr); + expect(decoded).to.eql(91612); + }); + }); +}); diff --git a/test/browser/spec/ol/renderer/webgl/Layer.test.js b/test/browser/spec/ol/renderer/webgl/Layer.test.js index 908c5dfd2a..e50d7ecfec 100644 --- a/test/browser/spec/ol/renderer/webgl/Layer.test.js +++ b/test/browser/spec/ol/renderer/webgl/Layer.test.js @@ -6,13 +6,14 @@ import TileLayer from '../../../../../../src/ol/layer/WebGLTile.js'; import VectorLayer from '../../../../../../src/ol/layer/Vector.js'; import VectorSource from '../../../../../../src/ol/source/Vector.js'; import View from '../../../../../../src/ol/View.js'; -import WebGLLayerRenderer, { +import WebGLLayerRenderer from '../../../../../../src/ol/renderer/webgl/Layer.js'; +import {getUid} from '../../../../../../src/ol/util.js'; +import { colorDecodeId, colorEncodeId, getBlankImageData, writePointFeatureToBuffers, -} from '../../../../../../src/ol/renderer/webgl/Layer.js'; -import {getUid} from '../../../../../../src/ol/util.js'; +} from '../../../../../../src/ol/render/webgl/utils.js'; describe('ol/renderer/webgl/Layer', function () { describe('constructor', function () { @@ -36,205 +37,6 @@ describe('ol/renderer/webgl/Layer', function () { }); }); - describe('writePointFeatureToBuffers', function () { - let vertexBuffer, indexBuffer, instructions; - - beforeEach(function () { - vertexBuffer = new Float32Array(100); - indexBuffer = new Uint32Array(100); - instructions = new Float32Array(100); - - instructions.set([0, 0, 0, 0, 10, 11]); - }); - - it('writes correctly to the buffers (without custom attributes)', function () { - const stride = 3; - const positions = writePointFeatureToBuffers( - instructions, - 4, - vertexBuffer, - indexBuffer, - 0 - ); - - expect(vertexBuffer[0]).to.eql(10); - expect(vertexBuffer[1]).to.eql(11); - expect(vertexBuffer[2]).to.eql(0); - - expect(vertexBuffer[stride + 0]).to.eql(10); - expect(vertexBuffer[stride + 1]).to.eql(11); - expect(vertexBuffer[stride + 2]).to.eql(1); - - expect(vertexBuffer[stride * 2 + 0]).to.eql(10); - expect(vertexBuffer[stride * 2 + 1]).to.eql(11); - expect(vertexBuffer[stride * 2 + 2]).to.eql(2); - - expect(vertexBuffer[stride * 3 + 0]).to.eql(10); - expect(vertexBuffer[stride * 3 + 1]).to.eql(11); - expect(vertexBuffer[stride * 3 + 2]).to.eql(3); - - expect(indexBuffer[0]).to.eql(0); - expect(indexBuffer[1]).to.eql(1); - expect(indexBuffer[2]).to.eql(3); - expect(indexBuffer[3]).to.eql(1); - expect(indexBuffer[4]).to.eql(2); - expect(indexBuffer[5]).to.eql(3); - - expect(positions.indexPosition).to.eql(6); - expect(positions.vertexPosition).to.eql(stride * 4); - }); - - it('writes correctly to the buffers (with 2 custom attributes)', function () { - instructions.set([0, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13]); - const stride = 5; - const positions = writePointFeatureToBuffers( - instructions, - 8, - vertexBuffer, - indexBuffer, - 2 - ); - - expect(vertexBuffer[0]).to.eql(10); - expect(vertexBuffer[1]).to.eql(11); - expect(vertexBuffer[2]).to.eql(0); - expect(vertexBuffer[3]).to.eql(12); - expect(vertexBuffer[4]).to.eql(13); - - expect(vertexBuffer[stride + 0]).to.eql(10); - expect(vertexBuffer[stride + 1]).to.eql(11); - expect(vertexBuffer[stride + 2]).to.eql(1); - expect(vertexBuffer[stride + 3]).to.eql(12); - expect(vertexBuffer[stride + 4]).to.eql(13); - - expect(vertexBuffer[stride * 2 + 0]).to.eql(10); - expect(vertexBuffer[stride * 2 + 1]).to.eql(11); - expect(vertexBuffer[stride * 2 + 2]).to.eql(2); - expect(vertexBuffer[stride * 2 + 3]).to.eql(12); - expect(vertexBuffer[stride * 2 + 4]).to.eql(13); - - expect(vertexBuffer[stride * 3 + 0]).to.eql(10); - expect(vertexBuffer[stride * 3 + 1]).to.eql(11); - expect(vertexBuffer[stride * 3 + 2]).to.eql(3); - expect(vertexBuffer[stride * 3 + 3]).to.eql(12); - expect(vertexBuffer[stride * 3 + 4]).to.eql(13); - - expect(indexBuffer[0]).to.eql(0); - expect(indexBuffer[1]).to.eql(1); - expect(indexBuffer[2]).to.eql(3); - expect(indexBuffer[3]).to.eql(1); - expect(indexBuffer[4]).to.eql(2); - expect(indexBuffer[5]).to.eql(3); - - expect(positions.indexPosition).to.eql(6); - expect(positions.vertexPosition).to.eql(stride * 4); - }); - - it('correctly chains buffer writes', function () { - instructions.set([10, 11, 20, 21, 30, 31]); - const stride = 3; - let positions = writePointFeatureToBuffers( - instructions, - 0, - vertexBuffer, - indexBuffer, - 0 - ); - positions = writePointFeatureToBuffers( - instructions, - 2, - vertexBuffer, - indexBuffer, - 0, - positions - ); - positions = writePointFeatureToBuffers( - instructions, - 4, - vertexBuffer, - indexBuffer, - 0, - positions - ); - - expect(vertexBuffer[0]).to.eql(10); - expect(vertexBuffer[1]).to.eql(11); - expect(vertexBuffer[2]).to.eql(0); - - expect(vertexBuffer[stride * 4 + 0]).to.eql(20); - expect(vertexBuffer[stride * 4 + 1]).to.eql(21); - expect(vertexBuffer[stride * 4 + 2]).to.eql(0); - - expect(vertexBuffer[stride * 8 + 0]).to.eql(30); - expect(vertexBuffer[stride * 8 + 1]).to.eql(31); - expect(vertexBuffer[stride * 8 + 2]).to.eql(0); - - expect(indexBuffer[6 + 0]).to.eql(4); - expect(indexBuffer[6 + 1]).to.eql(5); - expect(indexBuffer[6 + 2]).to.eql(7); - expect(indexBuffer[6 + 3]).to.eql(5); - expect(indexBuffer[6 + 4]).to.eql(6); - expect(indexBuffer[6 + 5]).to.eql(7); - - expect(indexBuffer[6 * 2 + 0]).to.eql(8); - expect(indexBuffer[6 * 2 + 1]).to.eql(9); - expect(indexBuffer[6 * 2 + 2]).to.eql(11); - expect(indexBuffer[6 * 2 + 3]).to.eql(9); - expect(indexBuffer[6 * 2 + 4]).to.eql(10); - expect(indexBuffer[6 * 2 + 5]).to.eql(11); - - expect(positions.indexPosition).to.eql(6 * 3); - expect(positions.vertexPosition).to.eql(stride * 4 * 3); - }); - }); - - describe('getBlankImageData', function () { - it('creates a 1x1 white texture', function () { - const texture = getBlankImageData(); - expect(texture.height).to.eql(1); - expect(texture.width).to.eql(1); - expect(texture.data[0]).to.eql(255); - expect(texture.data[1]).to.eql(255); - expect(texture.data[2]).to.eql(255); - expect(texture.data[3]).to.eql(255); - }); - }); - - describe('colorEncodeId and colorDecodeId', function () { - it('correctly encodes and decodes ids', function () { - expect(colorDecodeId(colorEncodeId(0))).to.eql(0); - expect(colorDecodeId(colorEncodeId(1))).to.eql(1); - expect(colorDecodeId(colorEncodeId(123))).to.eql(123); - expect(colorDecodeId(colorEncodeId(12345))).to.eql(12345); - expect(colorDecodeId(colorEncodeId(123456))).to.eql(123456); - expect(colorDecodeId(colorEncodeId(91612))).to.eql(91612); - expect(colorDecodeId(colorEncodeId(1234567890))).to.eql(1234567890); - }); - - it('correctly reuses array', function () { - const arr = []; - expect(colorEncodeId(123, arr)).to.be(arr); - }); - - it('is compatible with Uint8Array storage', function () { - const encoded = colorEncodeId(91612); - const typed = Uint8Array.of( - encoded[0] * 255, - encoded[1] * 255, - encoded[2] * 255, - encoded[3] * 255 - ); - const arr = [ - typed[0] / 255, - typed[1] / 255, - typed[2] / 255, - typed[3] / 255, - ]; - const decoded = colorDecodeId(arr); - expect(decoded).to.eql(91612); - }); - }); - describe('context sharing', () => { let target; beforeEach(() => {