diff --git a/src/style-spec/expression/stops.js b/src/style-spec/expression/stops.js index 2c178ddfdcb..22941ff20bb 100644 --- a/src/style-spec/expression/stops.js +++ b/src/style-spec/expression/stops.js @@ -11,19 +11,22 @@ export type Stops = Array<[number, Expression]>; * @private */ export function findStopLessThanOrEqualTo(stops: Array, input: number) { - const n = stops.length; + const lastIndex = stops.length - 1; let lowerIndex = 0; - let upperIndex = n - 1; + let upperIndex = lastIndex; let currentIndex = 0; - let currentValue, upperValue; + let currentValue, nextValue; while (lowerIndex <= upperIndex) { currentIndex = Math.floor((lowerIndex + upperIndex) / 2); currentValue = stops[currentIndex]; - upperValue = stops[currentIndex + 1]; - if (input === currentValue || input > currentValue && input < upperValue) { // Search complete - return currentIndex; - } else if (currentValue < input) { + nextValue = stops[currentIndex + 1]; + + if (currentValue <= input) { + if (currentIndex === lastIndex || input < nextValue) { // Search complete + return currentIndex; + } + lowerIndex = currentIndex + 1; } else if (currentValue > input) { upperIndex = currentIndex - 1; @@ -32,5 +35,5 @@ export function findStopLessThanOrEqualTo(stops: Array, input: number) { } } - return Math.max(currentIndex - 1, 0); + return 0; } diff --git a/src/style-spec/function/index.js b/src/style-spec/function/index.js index 2779f780872..0fbd0e9b74f 100644 --- a/src/style-spec/function/index.js +++ b/src/style-spec/function/index.js @@ -7,6 +7,7 @@ import * as interpolate from '../util/interpolate'; import Interpolate from '../expression/definitions/interpolate'; import Formatted from '../expression/types/formatted'; import { supportsInterpolation } from '../util/properties'; +import { findStopLessThanOrEqualTo } from '../expression/stops'; export function isFunction(value) { return typeof value === 'object' && value !== null && !Array.isArray(value); @@ -145,7 +146,7 @@ function evaluateIntervalFunction(parameters, propertySpec, input) { if (input <= parameters.stops[0][0]) return parameters.stops[0][1]; if (input >= parameters.stops[n - 1][0]) return parameters.stops[n - 1][1]; - const index = findStopLessThanOrEqualTo(parameters.stops, input); + const index = findStopLessThanOrEqualTo(parameters.stops.map((stop) => stop[0]), input); return parameters.stops[index][1]; } @@ -160,7 +161,7 @@ function evaluateExponentialFunction(parameters, propertySpec, input) { if (input <= parameters.stops[0][0]) return parameters.stops[0][1]; if (input >= parameters.stops[n - 1][0]) return parameters.stops[n - 1][1]; - const index = findStopLessThanOrEqualTo(parameters.stops, input); + const index = findStopLessThanOrEqualTo(parameters.stops.map((stop) => stop[0]), input); const t = interpolationFactor( input, base, parameters.stops[index][0], @@ -203,34 +204,6 @@ function evaluateIdentityFunction(parameters, propertySpec, input) { return coalesce(input, parameters.default, propertySpec.default); } -/** - * Returns the index of the last stop <= input, or 0 if it doesn't exist. - * - * @private - */ -function findStopLessThanOrEqualTo(stops, input) { - const n = stops.length; - let lowerIndex = 0; - let upperIndex = n - 1; - let currentIndex = 0; - let currentValue, upperValue; - - while (lowerIndex <= upperIndex) { - currentIndex = Math.floor((lowerIndex + upperIndex) / 2); - currentValue = stops[currentIndex][0]; - upperValue = stops[currentIndex + 1][0]; - if (input === currentValue || input > currentValue && input < upperValue) { // Search complete - return currentIndex; - } else if (currentValue < input) { - lowerIndex = currentIndex + 1; - } else if (currentValue > input) { - upperIndex = currentIndex - 1; - } - } - - return Math.max(currentIndex - 1, 0); -} - /** * Returns a ratio that can be used to interpolate between exponential function * stops. diff --git a/test/unit/style-spec/stops.test.js b/test/unit/style-spec/stops.test.js new file mode 100644 index 00000000000..c9f21021184 --- /dev/null +++ b/test/unit/style-spec/stops.test.js @@ -0,0 +1,27 @@ +import { test } from 'mapbox-gl-js-test'; +import { findStopLessThanOrEqualTo } from '../../../src/style-spec/expression/stops'; + +test('findStopLessThanOrEqualTo', (t) => { + test('When the input > all stops it returns the last stop.', (t) => { + const index = findStopLessThanOrEqualTo([0, 1, 2, 3, 4, 5, 6, 7], 8); + t.equal(index, 7); + t.end(); + }); + + test('When more than one stop has the same value it always returns the last stop', (t) => { + let index; + + index = findStopLessThanOrEqualTo([0.5, 0.5], 0.5); + t.equal(index, 1); + + index = findStopLessThanOrEqualTo([0.5, 0.5, 0.5], 0.5); + t.equal(index, 2); + + index = findStopLessThanOrEqualTo([0.4, 0.5, 0.5, 0.6, 0.7], 0.5); + t.equal(index, 2); + + t.end(); + }); + + t.end(); +});