#javascript #algorithm #math
Вопрос:
Возникли проблемы с поиском способа вычисления индексов данных по координатам пикселей. То, что у меня есть, — это a NumberRange
, в котором хранятся 0
(мин) и HTMLCanvasElement.width
(макс).
const EPSILON = 0.0001;
function IsRealNumber(number: number) {
return !isNaN(number) amp;amp; isFinite(number) amp;amp; number !== Number.MAX_VALUE amp;amp; number !== Number.MIN_VALUE;
}
/**
* Defines a number range with numeric min, max
*/
class NumberRange {
readonly min: number;
readonly max: number;
constructor(min?: number, max?: number) {
this.min = min ?? 0;
this.max = max ?? 10;
}
/**
* Grows a range by a min and max factor
* @remarks
* If the current range is [5,10] and the input range is [0.1, 0.1] the current range will be
* grown by 10%, so [4.5, 10.5]
* @param range The grow factor
* @param isLogarithmic When true, treats the growth factor as logarithmic not linear
*/
growBy(range: NumberRange, isLogarithmic?: boolean): NumberRange {
if (isLogarithmic === void 0) { isLogarithmic = false; }
var diff = this.max - this.min;
// If min == max, expand around the mid line
var min = this.min - range.min * (this.isZero() ? this.min : diff);
var max = this.max range.max * (this.isZero() ? this.max : diff);
// Swap if min > max (occurs when mid line is negative)
if (min > max) {
var temp = min;
min = max;
max = temp;
}
// If still zero, then expand around the zero line
if (Math.abs(max - min) <= EPSILON amp;amp; Math.abs(min) <= EPSILON) {
min = -1.0;
max = 1.0;
}
return new NumberRange(min, max);
}
/**
* Returns true if the range min === range max
*/
isZero(): boolean {
return this.min === this.max;
}
}
Что должно произойти, описано ниже.
// Simulate current canvas range (0, canvas.width)
const canvasPixelRange = new NumberRange(0, 1920)
// Simulate total data range
const totalDataRange = new NumberRange(0, 100)
// Default visible data range
let visibleDataRange;
visibleDataRange = totalDataRange.growBy(new NumberRange(0, 0))
console.log(visibleDataRange)
/**
* NumberRange: {
"min": 0,
"max": 100
}
*/
console.log(PixelCoordinateXToDataIndex(canvasPixelRange, visibleDataRange, 960)) // Should be 50
Смоделированное изменение
// Simualte mouse drag x event that will determine growByRange
visibleDataRange = totalDataRange.growBy(new NumberRange(-.15, -.25))
console.log(visibleDataRange)
/**
* NumberRange: {
"min": 15,
"max": 75
}
*/
console.log(PixelCoordinateXToDataIndex(canvasPixelRange, visibleDataRange, 960)) // Should be 45
Функция, которая вычисляет индекс данных
function PixelCoordinateXToDataIndex(canvasRange: NumberRange, dataRange: NumberRange, atCoord: number) {
// [0, 100], 100
const dataRangeValue = dataRange.max - dataRange.min;
// ceil(canvas.width: 1920 / 100), 20
const dataToPixelInterval = Math.ceil(canvasRange.max / dataRangeValue);
// (canvas.width: 1920 / 20), 96
const pixelBaseInterval = canvasRange.max / dataToPixelInterval;
const dataOptimalIntervals = 8;
// Traverse the canvas pixel width by the pixelBaseInterval (1920 / 96), 20 times
for (let currentPixelCoord = 0; currentPixelCoord < canvasRange.max; currentPixelCoord = pixelBaseInterval) {
if (currentPixelCoord !== 0) {
let previousPixelXCoord = currentPixelCoord - pixelBaseInterval;
if (atCoord > previousPixelXCoord amp;amp; atCoord <= currentPixelCoord) {
let scaledInterval = pixelBaseInterval / dataOptimalIntervals;
let intervals: number[][] = [[0, scaledInterval]]
for (let a = 2; a < dataRange.max 1; a = scaledInterval) {
intervals.push([Math.ceil(intervals[intervals.length - 1][1]), Math.ceil(a)])
}
return intervals[intervals.length - 5][1]
}
}
}
return 0;
}
Другие примеры
visibleDataRange = totalDataRange.growBy(new NumberRange(0, 0))
console.log(PixelCoordinateXToDataIndex(canvasPixelRange, visibleDataRange, 1920)) // Should be 99
console.log(PixelCoordinateXToDataIndex(canvasPixelRange, visibleDataRange, 0)) // Should be 0
Комментарии:
1. Вместо того, чтобы понижать голос, на самом деле дайте обратную связь о том, что не так с сообщением..
2. Почему пример 3 предполагает, что ответ
f
(timestamps[5]
), когда другие просто возвращают индекс?3. У вас также, похоже, есть некоторая избыточная информация.
interval
может быть рассчитан по длине массива и значениям минимального и максимального масштаба. Если нет, то что происходит, когда они не синхронизированы?
Ответ №1:
Кажется относительно простым получить индекс или фактическое значение массива с помощью такого кода:
const getIndex = (xs, min, max) => (x) =>
Math.round((xs .length - 1) * (x - min) / (max - min))
const getValue = (xs, min, max) => (x) =>
xs [Math.round((xs .length - 1) * (x - min) / (max - min))]
const tests = [
{timestamps: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'],
scale_min: .5, scale_max: 1.5, curr_interval: 1.5},
{timestamps: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm'],
scale_min: .5, scale_max: 1.5, curr_interval: 1.5},
{timestamps: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm'],
scale_min: .5, scale_max: 1.5, curr_interval: 1},
{timestamps: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'],
scale_min: .5, scale_max: 1.5, curr_interval: 1},
]
tests .forEach (({timestamps, scale_min, scale_max, curr_interval}) =>
console .log (`${timestamps .join ('')} - [${scale_min} - ${scale_max}] - ${curr_interval} --> '${
getValue(timestamps, scale_min, scale_max) (curr_interval)
}' (index ${getIndex(timestamps, scale_min, scale_max) (curr_interval)})`)
)
Но я обеспокоен тем, что в этом может чего-то не хватать, поскольку вы предоставляете curr_interval
строку и включаете полностью выводимое interval
(которое я игнорирую). Чего не хватает этому решению?
Это действительно зависит от min
того, чтобы быть меньше max
и curr_interval
падать в этом диапазоне. Но если что-то из этого не получится, оно должно просто вернуться undefined
, что кажется разумным.