javascript - Javascript从像素坐标计算数据索引
问题描述
无法从像素坐标计算数据索引。我所拥有的是NumberRange
存储0
(最小)和HTMLCanvasElement.width
(最大)的。
const EPSILON = 0.0001;
function IsRealNumber(number: number) {
return !isNaN(number) && isFinite(number) && number !== Number.MAX_VALUE && 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 && 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 && 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
解决方案
使用如下代码获取索引或实际数组值似乎相对简单:
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
,这似乎是合理的。
推荐阅读
- html - 如何在 onclick 事件后停止 CSS 面板相互重叠?
- python - Python - 从未检索到任务异常 & AttributeError: __aexit__
- azure - Azcopy 将数百万个小 Blob 从存储帐户复制到您的 VM
- ios - 如何在 Xcode 中用 Swift 语言实现浏览器中例行任务的自动化?
- java - 未返回与签名 jwt 令牌中的声明相同的到期日期
- git - 另一个 GitHub 帐户错误地推送到 GitHub
- php - php finfo 将 php 文件检测为 jpeg
- windows - makefile "C:\Program" 不被识别为内部或外部命令
- laravel - Laravel 8 社交名流登录重定向问题
- ffmpeg - FFMpeg - 将第一帧冻结 X 秒