UNPKG

scichart

Version:

Fast WebGL JavaScript Charting Library and Framework

704 lines (703 loc) 40.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.hitTestHelpers = void 0; var Deleter_1 = require("../../../../Core/Deleter"); var Point_1 = require("../../../../Core/Point"); var ErrorDirection_1 = require("../../../../types/ErrorDirection"); var pointUtil_1 = require("../../../../utils/pointUtil"); var ColumnMode_1 = require("../../../../types/ColumnMode"); var HitTestInfo_1 = require("./HitTestInfo"); var TriangleSeriesDrawMode_1 = require("../../../../types/TriangleSeriesDrawMode"); var interpolateLinear = function (x, x1, y1, x2, y2) { return y1 + ((y2 - y1) * (x - x1)) / (x2 - x1); }; // TODO: take isVertical property into account /** * * @param renderableSeries * @param xCoordinateCalculator * @param yCoordinateCalculator * @param isVerticalChart * @param dataSeries * @param xNativeValues * @param yNativeValues * @param xHitCoord the X coordinate on the screen relative to seriesViewRect, X and Y swapped for vertical charts * @param yHitCoord the Y coordinate on the screen relative to seriesViewRect, X and Y swapped for vertical charts * @param nearestPointIndex * @param hitTestRadius */ var createHitTestInfo = function (renderableSeries, xCoordinateCalculator, yCoordinateCalculator, isVerticalChart, dataSeries, xNativeValues, yNativeValues, xHitCoord, yHitCoord, nearestPointIndex, hitTestRadius, distance, dataCoordWidth) { var isCategoryAxis = xCoordinateCalculator.isCategoryCoordinateCalculator; var hitTestInfo = new HitTestInfo_1.HitTestInfo(renderableSeries); hitTestInfo.seriesName = renderableSeries.seriesName; hitTestInfo.dataSeriesType = dataSeries.type; hitTestInfo.hitTestPoint = new Point_1.Point(xHitCoord, yHitCoord); var hitTestPointXValue = xCoordinateCalculator.getDataValue(xHitCoord); var hitTestPointYValue = yCoordinateCalculator.getDataValue(yHitCoord); hitTestInfo.hitTestPointValues = new Point_1.Point(hitTestPointXValue, hitTestPointYValue); hitTestInfo.dataSeriesIndex = nearestPointIndex; hitTestInfo.hitTestRadius = hitTestRadius; hitTestInfo.isCategoryAxis = isCategoryAxis; hitTestInfo.distance = distance; // If there is no data, don't attempt to access it. if (nearestPointIndex >= 0) { var yValueByName = dataSeries.valueNames.reduce(function (map, name) { map[name] = dataSeries.getNativeValue(dataSeries.getYValuesByName(name), nearestPointIndex); return map; }, {}); var xValue = isCategoryAxis ? nearestPointIndex : dataSeries.getNativeValue(xNativeValues, nearestPointIndex); var yValue = dataSeries.getNativeValue(yNativeValues, nearestPointIndex); hitTestInfo.xCoord = xCoordinateCalculator.getCoordinate(xValue); hitTestInfo.yCoord = yCoordinateCalculator.getCoordinate(yValue); // TODO: It might be worth to flip them to make the API better // if (isVerticalChart) { // const temp = hitTestInfo.xCoord; // hitTestInfo.xCoord = hitTestInfo.yCoord; // hitTestInfo.yCoord = temp; // } hitTestInfo.xValue = xValue; if (isCategoryAxis) { hitTestInfo.xCategoryValue = xNativeValues.get(nearestPointIndex); } hitTestInfo.yValueByName = yValueByName; hitTestInfo.yValue = yValue; // let xFirstValue = isCategoryAxis ? 0 : dataSeries.getNativeValue(xNativeValues, 0); // let xLastValue = isCategoryAxis // ? xNativeValues.size() - 1 // : dataSeries.getNativeValue(xNativeValues, xNativeValues.size() - 1); var xRange = isCategoryAxis ? dataSeries.getIndicesRange(dataSeries.getXRange()) : dataSeries.getXRange(); var dataWidth = dataCoordWidth !== undefined ? xCoordinateCalculator.getDataWidth(dataCoordWidth) : 0; hitTestInfo.isWithinDataBounds = (0, pointUtil_1.testIsInInterval)(hitTestPointXValue, xRange.min, xRange.max, dataWidth / 2); hitTestInfo.metadata = dataSeries.getMetadataAt(nearestPointIndex); } else { hitTestInfo.isWithinDataBounds = false; } hitTestInfo.isHit = undefined; return hitTestInfo; }; var getNearestXPoint = function (webAssemblyContext, xCoordinateCalculator, dataSeries, xHitCoord, isSorted) { var result = getNearestXyPoint(webAssemblyContext, xCoordinateCalculator, xCoordinateCalculator, dataSeries, xHitCoord, 0, 0); return result.nearestPointIndex; }; var getNearestXyPoint = function (webassemblyContext, xCoordinateCalculator, yCoordinateCalculator, dataSeries, xHitCoord, yHitCoord, hitTestRadius) { var isCategoryAxis = xCoordinateCalculator.isCategoryCoordinateCalculator; var dataX = isCategoryAxis ? dataSeries.getNativeIndexes() : dataSeries.getNativeXValues(); var dataY = dataSeries.getNativeYValues(); return getNearestPoint(webassemblyContext, xCoordinateCalculator, yCoordinateCalculator, dataX, dataY, dataSeries.dataDistributionCalculator.isSortedAscending, xHitCoord, yHitCoord, hitTestRadius); }; var getNearestPoint = function (webassemblyContext, xCoordinateCalculator, yCoordinateCalculator, xValues, yValues, isSorted, xHitCoord, yHitCoord, hitTestRadius) { var result; try { result = webassemblyContext.SCRTHitTestHelper.GetNearestXyPoint(xCoordinateCalculator.nativeCalculator, yCoordinateCalculator.nativeCalculator, xValues, yValues, isSorted, xHitCoord, yHitCoord, hitTestRadius !== null && hitTestRadius !== void 0 ? hitTestRadius : 1 // Default to 1 here so unsorted data will get nearest by x and y ); return { nearestPointIndex: result.minD, distance: result.maxD }; } finally { (0, Deleter_1.deleteSafe)(result); } }; var getNearestXyyPoint = function (webassemblyContext, xCoordinateCalculator, yCoordinateCalculator, dataSeries, xHitCoord, yHitCoord, hitTestRadius) { var isCategoryAxis = xCoordinateCalculator.isCategoryCoordinateCalculator; var dataX = isCategoryAxis ? dataSeries.getNativeIndexes() : dataSeries.getNativeXValues(); var dataY = dataSeries.getNativeYValues(); var result; var nearestY; var distanceY; try { result = webassemblyContext.SCRTHitTestHelper.GetNearestXyPoint(xCoordinateCalculator.nativeCalculator, yCoordinateCalculator.nativeCalculator, dataX, dataY, dataSeries.dataDistributionCalculator.isSortedAscending, xHitCoord, yHitCoord, hitTestRadius !== null && hitTestRadius !== void 0 ? hitTestRadius : 1); nearestY = result.minD; distanceY = result.maxD; } finally { (0, Deleter_1.deleteSafe)(result); } try { result = webassemblyContext.SCRTHitTestHelper.GetNearestXyPoint(xCoordinateCalculator.nativeCalculator, yCoordinateCalculator.nativeCalculator, dataX, dataSeries.getNativeY1Values(), dataSeries.dataDistributionCalculator.isSortedAscending, xHitCoord, yHitCoord, hitTestRadius !== null && hitTestRadius !== void 0 ? hitTestRadius : 1); if (distanceY < result.maxD) { // Y is nearer return { nearestPointIndex: nearestY, distance: distanceY }; } else { return { nearestPointIndex: result.minD, distance: result.maxD }; } } finally { (0, Deleter_1.deleteSafe)(result); } }; var getNearestUniformHeatmapPoint = function (xCoordinateCalculator, yCoordinateCalculator, heatmapDataSeries, xHitCoord, yHitCoord) { var xHitValue = xCoordinateCalculator.getDataValue(xHitCoord); var yHitValue = yCoordinateCalculator.getDataValue(yHitCoord); var xIndex = Math.floor((xHitValue - heatmapDataSeries.xStart) / heatmapDataSeries.xStep); var yIndex = Math.floor((yHitValue - heatmapDataSeries.yStart) / heatmapDataSeries.yStep); if (xIndex < 0 || xIndex >= heatmapDataSeries.arrayWidth || yIndex < 0 || yIndex >= heatmapDataSeries.arrayHeight) { return { xIndex: -1, yIndex: -1, zValue: undefined }; } var zValue = heatmapDataSeries.getZValue(yIndex, xIndex); return { xIndex: xIndex, yIndex: yIndex, zValue: zValue }; }; var getNearestNonUniformHeatmapPoint = function (xCoordinateCalculator, yCoordinateCalculator, heatmapDataSeries, xHitCoord, yHitCoord) { var xHitValue = xCoordinateCalculator.getDataValue(xHitCoord); var yHitValue = yCoordinateCalculator.getDataValue(yHitCoord); var xCellOffsets = heatmapDataSeries.xCellOffsets, yCellOffsets = heatmapDataSeries.yCellOffsets; var xIndex = -1; if (xHitValue >= xCellOffsets[0] && xHitValue <= xCellOffsets[xCellOffsets.length - 1]) { for (var i = 0; i < xCellOffsets.length; i++) { var isWithinCellOffsets = xHitValue >= xCellOffsets[i] && xHitValue <= xCellOffsets[i + 1]; if (isWithinCellOffsets) { xIndex = i; break; } } } var yIndex = -1; if (yHitValue >= yCellOffsets[0] && yHitValue <= yCellOffsets[yCellOffsets.length - 1]) { for (var i = 0; i < yCellOffsets.length - 1; i++) { var isWithinCellOffsets = yHitValue >= yCellOffsets[i] && yHitValue <= yCellOffsets[i + 1]; if (isWithinCellOffsets) { yIndex = i; break; } } } // TODO shouldn't we return a nearest cell anyway? if (xIndex < 0 || xIndex >= heatmapDataSeries.arrayWidth || yIndex < 0 || yIndex >= heatmapDataSeries.arrayHeight) { return { xIndex: -1, yIndex: -1, zValue: undefined }; } var zValue = heatmapDataSeries.getZValue(yIndex, xIndex); return { xIndex: xIndex, yIndex: yIndex, zValue: zValue }; }; /** * Finds a Triangle that has been hit and return indices of the first and the last vertex of this triangle, * If there is no hit return -1, -1 */ var getNearestTriangle = function (wasmContext, xCoordinateCalculator, yCoordinateCalculator, xValues, yValues, xHitCoord, yHitCoord, drawMode, polygonVertices) { var isPolygon = drawMode === TriangleSeriesDrawMode_1.ETriangleSeriesDrawMode.Polygon || drawMode === TriangleSeriesDrawMode_1.ETriangleSeriesDrawMode.List; var vertices = drawMode === TriangleSeriesDrawMode_1.ETriangleSeriesDrawMode.List ? 3 : polygonVertices; var res = wasmContext.SCRTHitTestHelper.GetNearestTriangle(isPolygon, vertices, xCoordinateCalculator.nativeCalculator, yCoordinateCalculator.nativeCalculator, xValues, yValues, xHitCoord, yHitCoord); return { nearestPointIndex: res.minD, nearestPointIndex2: res.maxD, isHit: res.minD >= 0 }; }; var getNearestLineSegment = function (xCoordinateCalculator, yCoordinateCalculator, numberOfSegments, getXFn, getYFn, getX1Fn, getY1Fn, xHitCoord, yHitCoord, hitTestRadius) { var minX = Number.MAX_VALUE; var maxX = Number.NEGATIVE_INFINITY; var minY = Number.MAX_VALUE; var maxY = Number.NEGATIVE_INFINITY; var updateMinMaxFn = function (getXFn$, getYFn$) { for (var i = 0; i < numberOfSegments; i++) { var x = getXFn$(i); var y = getYFn$(i); minX = x < minX ? x : minX; maxX = x > maxX ? x : maxX; minY = y < minY ? y : minY; maxY = y > maxY ? y : maxY; } }; updateMinMaxFn(getXFn, getYFn); updateMinMaxFn(getX1Fn, getY1Fn); var xHitValue = xCoordinateCalculator.getDataValue(xHitCoord); var yHitValue = yCoordinateCalculator.getDataValue(yHitCoord); var isWithinDataBounds = minX <= xHitValue && xHitValue <= maxX && minY <= yHitValue && yHitValue <= maxY; // Test for lines var minDistance = Number.MAX_VALUE; var minDistanceIndex = -1; for (var i = 0; i < numberOfSegments; i++) { var x = getXFn(i); var y = getYFn(i); var x1 = getX1Fn(i); var y1 = getY1Fn(i); var xCoord = xCoordinateCalculator.getCoordinate(x); var yCoord = yCoordinateCalculator.getCoordinate(y); var x1Coord = xCoordinateCalculator.getCoordinate(x1); var y1Coord = yCoordinateCalculator.getCoordinate(y1); var lineSegmentLength = (0, pointUtil_1.calcDistance)(xCoord, yCoord, x1Coord, y1Coord); var distanceToPoint = (0, pointUtil_1.calcDistance)(xCoord, yCoord, xHitCoord, yHitCoord); var distanceToPoint1 = (0, pointUtil_1.calcDistance)(x1Coord, y1Coord, xHitCoord, yHitCoord); if (distanceToPoint <= lineSegmentLength + hitTestRadius && distanceToPoint1 <= lineSegmentLength + hitTestRadius) { var distanceToLine = (0, pointUtil_1.calcDistanceFromLine)(xHitCoord, yHitCoord, xCoord, yCoord, x1Coord, y1Coord); if (distanceToLine <= minDistance) { minDistance = distanceToLine; minDistanceIndex = i; } } } return { isHit: minDistance <= hitTestRadius, isWithinDataBounds: isWithinDataBounds, nearestPointIndex: minDistanceIndex, nearestDistance: minDistanceIndex >= 0 ? minDistance : undefined }; }; var testIsHitForPoint = function (xCoordinateCalculator, yCoordinateCalculator, xValues, yValues, pointIndex, xHitCoord, yHitCoord, hitTestRadius, dataSeries) { var isCategoryAxis = xCoordinateCalculator.isCategoryCoordinateCalculator; var xValue = isCategoryAxis ? pointIndex : dataSeries.getNativeValue(xValues, pointIndex); var yValue = dataSeries.getNativeValue(yValues, pointIndex); var dataXCoord = xCoordinateCalculator.getCoordinate(xValue); var dataYCoord = yCoordinateCalculator.getCoordinate(yValue); var distance = (0, pointUtil_1.calcDistance)(xHitCoord, yHitCoord, dataXCoord, dataYCoord); return distance < hitTestRadius; }; /** * * @param xCoordinateCalculator * @param yCoordinateCalculator * @param xValues * @param yValues * @param pointIndex * @param xHitCoord The X coordinate, isVertical property is already taken into account * @param yHitCoord The Y coordinate, isVertical property is already taken into account * @param hitTestRadius */ var testIsHitForLine = function (xCoordinateCalculator, yCoordinateCalculator, xValues, yValues, pointIndex, xHitCoord, yHitCoord, hitTestRadius, dataSeries) { var isHit; var secondPointIndex; var xLeft, xRight, yLeft, yRight; var isCategoryAxis = xCoordinateCalculator.isCategoryCoordinateCalculator; var xValue = isCategoryAxis ? pointIndex : dataSeries.getNativeValue(xValues, pointIndex); var yValue = dataSeries.getNativeValue(yValues, pointIndex); var xHitValue = xCoordinateCalculator.getDataValue(xHitCoord); if (xValue <= xHitValue) { xLeft = xValue; yLeft = yValue; xRight = isCategoryAxis ? pointIndex + 1 : dataSeries.getNativeValue(xValues, pointIndex + 1); yRight = dataSeries.getNativeValue(yValues, pointIndex + 1); secondPointIndex = pointIndex + 1; } else { xLeft = isCategoryAxis ? pointIndex - 1 : dataSeries.getNativeValue(xValues, pointIndex - 1); yLeft = dataSeries.getNativeValue(yValues, pointIndex - 1); secondPointIndex = pointIndex - 1; xRight = xValue; yRight = yValue; } var xLeftCoord = xCoordinateCalculator.getCoordinate(xLeft); var xRightCoord = xCoordinateCalculator.getCoordinate(xRight); var yLeftCoord = yCoordinateCalculator.getCoordinate(yLeft); var yRightCoord = yCoordinateCalculator.getCoordinate(yRight); var lineSegmentLength = (0, pointUtil_1.calcDistance)(xLeftCoord, yLeftCoord, xRightCoord, yRightCoord); var distanceToLeftPoint = (0, pointUtil_1.calcDistance)(xLeftCoord, yLeftCoord, xHitCoord, yHitCoord); var distanceToRightPoint = (0, pointUtil_1.calcDistance)(xRightCoord, yRightCoord, xHitCoord, yHitCoord); // Because the line that goes through two points is infinite it could happen that mouse click is near this line // but far away from the line segment, especially if the segment is almost a vertical line // in this case we set isHit = false if (distanceToLeftPoint > lineSegmentLength + hitTestRadius || distanceToRightPoint > lineSegmentLength + hitTestRadius) { isHit = false; } else { isHit = (0, pointUtil_1.calcDistanceFromLine)(xHitCoord, yHitCoord, xLeftCoord, yLeftCoord, xRightCoord, yRightCoord) < hitTestRadius; } return { isHit: isHit, secondPointIndex: secondPointIndex }; }; var testIsHitForBand = function (isDigitalLine, xCoordinateCalculator, yCoordinateCalculator, xValues, getYValue, getY1Value, pointIndex, xHitCoord, yHitCoord, dataSeries) { var isHit; var xHitValue = xCoordinateCalculator.getDataValue(xHitCoord); var isCategoryAxis = xCoordinateCalculator.isCategoryCoordinateCalculator; var xValue = isCategoryAxis ? pointIndex : dataSeries.getNativeValue(xValues, pointIndex); var isLeftHit = xValue <= xHitValue; var secondPointIndex = isLeftHit ? pointIndex + 1 : pointIndex - 1; if (secondPointIndex < 0 || secondPointIndex >= xValues.size()) { return { isHit: false, secondPointIndex: undefined }; } var secondPointXValue = isCategoryAxis ? secondPointIndex : dataSeries.getNativeValue(xValues, secondPointIndex); var xLeft = isLeftHit ? xValue : secondPointXValue; var yLeft = isLeftHit ? getYValue(pointIndex) : getYValue(secondPointIndex); var y1Left = isLeftHit ? getY1Value(pointIndex) : getY1Value(secondPointIndex); var xRight = isLeftHit ? secondPointXValue : xValue; var yRight = isLeftHit ? getYValue(secondPointIndex) : getYValue(pointIndex); var y1Right = isLeftHit ? getY1Value(secondPointIndex) : getY1Value(pointIndex); var xLeftCoord = xCoordinateCalculator.getCoordinate(xLeft); var xRightCoord = xCoordinateCalculator.getCoordinate(xRight); var yLeftCoord = yCoordinateCalculator.getCoordinate(yLeft); var yRightCoord = yCoordinateCalculator.getCoordinate(yRight); var y1LeftCoord = yCoordinateCalculator.getCoordinate(y1Left); var y1RightCoord = yCoordinateCalculator.getCoordinate(y1Right); if (isDigitalLine) { if (yLeftCoord < y1LeftCoord) { isHit = yHitCoord >= yLeftCoord && yHitCoord <= y1LeftCoord; } else { isHit = yHitCoord >= y1LeftCoord && yHitCoord <= yLeftCoord; } } else { var interpolatedLineY = interpolateLinear(xHitCoord, xLeftCoord, yLeftCoord, xRightCoord, yRightCoord); var interpolatedLineY1 = interpolateLinear(xHitCoord, xLeftCoord, y1LeftCoord, xRightCoord, y1RightCoord); if (interpolatedLineY < interpolatedLineY1) { isHit = yHitCoord >= interpolatedLineY && yHitCoord <= interpolatedLineY1; } else { isHit = yHitCoord >= interpolatedLineY1 && yHitCoord <= interpolatedLineY; } } return { isHit: isHit, secondPointIndex: secondPointIndex }; }; var testIsHitForColumn = function (xCoordinateCalculator, yCoordinateCalculator, columnWidth, zeroLineY, dataSeries, xValues, yValues, pointIndex, xHitCoord, yHitCoord, distance) { if (distance === void 0) { distance = undefined; } var isCategoryAxis = xCoordinateCalculator.isCategoryCoordinateCalculator; var xValue = isCategoryAxis ? pointIndex : dataSeries.getNativeValue(xValues, pointIndex); var yValue = dataSeries.getNativeValue(yValues, pointIndex); var xCoord = xCoordinateCalculator.getCoordinate(xValue); var yCoord = yCoordinateCalculator.getCoordinate(yValue); var zeroLineYCoord = yCoordinateCalculator.getCoordinate(zeroLineY); var halfWidth = columnWidth / 2; var topColumnSide = zeroLineYCoord > yCoord ? zeroLineYCoord : yCoord; var bottomColumnSide = zeroLineYCoord > yCoord ? yCoord : zeroLineYCoord; if (distance !== undefined) { var isXHit = distance <= halfWidth; var isYHit = yHitCoord <= topColumnSide && yHitCoord >= bottomColumnSide; return isXHit && isYHit; } else { return (0, pointUtil_1.testIsInBounds)(xHitCoord, yHitCoord, xCoord - halfWidth, topColumnSide, xCoord + halfWidth, bottomColumnSide); } }; var testIsHitForBoxPlot = function (xCoordinateCalculator, yCoordinateCalculator, columnWidth, dataSeries, xValues, yMinValues, yMaxValues, pointIndex, xHitCoord, yHitCoord) { var isCategoryAxis = xCoordinateCalculator.isCategoryCoordinateCalculator; var xValue = isCategoryAxis ? pointIndex : dataSeries.getNativeValue(xValues, pointIndex); var yMinValue = dataSeries.getNativeValue(yMinValues, pointIndex); var yMaxValue = dataSeries.getNativeValue(yMaxValues, pointIndex); var xCoord = xCoordinateCalculator.getCoordinate(xValue); var yCoord = yCoordinateCalculator.getCoordinate(yMinValue); var y1Coord = yCoordinateCalculator.getCoordinate(yMaxValue); var halfWidth = columnWidth / 2; var topColumnSide = y1Coord > yCoord ? y1Coord : yCoord; var bottomColumnSide = y1Coord > yCoord ? yCoord : y1Coord; return (0, pointUtil_1.testIsInBounds)(xHitCoord, yHitCoord, xCoord - halfWidth, topColumnSide, xCoord + halfWidth, bottomColumnSide); }; var testIsHitForErrorBars = function (xCoordinateCalculator, yCoordinateCalculator, renderableSeries, xValues, yValues, pointIndex, xHitCoord, yHitCoord) { var getDataPointWidth = renderableSeries.getDataPointWidth, dataPointWidth = renderableSeries.dataPointWidth, errorDirection = renderableSeries.errorDirection, dataSeries = renderableSeries.dataSeries, dataPointWidthMode = renderableSeries.dataPointWidthMode; var isCategoryAxis = xCoordinateCalculator.isCategoryCoordinateCalculator; var isVerticalDirection = errorDirection === ErrorDirection_1.EErrorDirection.Vertical; var xValue = isCategoryAxis ? pointIndex : dataSeries.getNativeValue(xValues, pointIndex); var yValue = dataSeries.getNativeValue(yValues, pointIndex); var highValue = dataSeries.getNativeValue(dataSeries.getNativeHighValues(), pointIndex); var lowValue = dataSeries.getNativeValue(dataSeries.getNativeLowValues(), pointIndex); if (isNaN(highValue)) highValue = yValue; if (isNaN(lowValue)) lowValue = yValue; var xCoord = xCoordinateCalculator.getCoordinate(xValue); var yCoord = yCoordinateCalculator.getCoordinate(yValue); var highCoord = isVerticalDirection ? yCoordinateCalculator.getCoordinate(highValue) : xCoordinateCalculator.getCoordinate(highValue); var lowCoord = isVerticalDirection ? yCoordinateCalculator.getCoordinate(lowValue) : xCoordinateCalculator.getCoordinate(lowValue); var columnWidth = getDataPointWidth(isVerticalDirection ? xCoordinateCalculator : yCoordinateCalculator, dataPointWidth, dataPointWidthMode); var halfWidth = columnWidth / 2; var isHit = false; var upperErrorBoundary = highCoord > lowCoord ? highCoord : lowCoord; var lowerErrorBoundary = highCoord > lowCoord ? lowCoord : highCoord; if (isVerticalDirection) { isHit = (0, pointUtil_1.testIsInBounds)(xHitCoord, yHitCoord, xCoord - halfWidth, upperErrorBoundary, xCoord + halfWidth, lowerErrorBoundary); } else { isHit = (0, pointUtil_1.testIsInBounds)(xHitCoord, yHitCoord, lowerErrorBoundary, yCoord + halfWidth, upperErrorBoundary, yCoord - halfWidth); } return { isHit: isHit, highValue: highValue, lowValue: lowValue }; }; var testIsHitForImpulse = function (xCoordinateCalculator, yCoordinateCalculator, renderableSeries, xValues, yValues, pointIndex, xHitCoord, yHitCoord, hitTestRadius) { var zeroLineY = renderableSeries.zeroLineY, dataSeries = renderableSeries.dataSeries; var isCategoryAxis = xCoordinateCalculator.isCategoryCoordinateCalculator; var xValue = isCategoryAxis ? pointIndex : dataSeries.getNativeValue(xValues, pointIndex); var yValue = dataSeries.getNativeValue(yValues, pointIndex); var xCoord = xCoordinateCalculator.getCoordinate(xValue); var yCoord = yCoordinateCalculator.getCoordinate(yValue); var zeroLineYCoord = yCoordinateCalculator.getCoordinate(zeroLineY); var topColumnSide = zeroLineYCoord > yCoord ? zeroLineYCoord : yCoord; var bottomColumnSide = zeroLineYCoord > yCoord ? yCoord : zeroLineYCoord; return (0, pointUtil_1.testIsInBounds)(xHitCoord, yHitCoord, xCoord, topColumnSide, xCoord, bottomColumnSide, hitTestRadius); }; var testIsHitForOHLC = function (xCoordinateCalculator, yCoordinateCalculator, renderableSeries, dataSeries, pointIndex, xHitCoord, yHitCoord, hitTestRadius) { var getDataPointWidth = renderableSeries.getDataPointWidth, dataPointWidth = renderableSeries.dataPointWidth, dataPointWidthMode = renderableSeries.dataPointWidthMode; var isCategoryAxis = xCoordinateCalculator.isCategoryCoordinateCalculator; var xValue = isCategoryAxis ? pointIndex : dataSeries.getNativeValue(dataSeries.getNativeXValues(), pointIndex); var xCoord = xCoordinateCalculator.getCoordinate(xValue); var openValue = dataSeries.getNativeValue(dataSeries.getNativeOpenValues(), pointIndex); var openCoord = yCoordinateCalculator.getCoordinate(openValue); var highValue = dataSeries.getNativeValue(dataSeries.getNativeHighValues(), pointIndex); var highCoord = yCoordinateCalculator.getCoordinate(highValue); var lowValue = dataSeries.getNativeValue(dataSeries.getNativeLowValues(), pointIndex); var lowCoord = yCoordinateCalculator.getCoordinate(lowValue); var closeValue = dataSeries.getNativeValue(dataSeries.getNativeCloseValues(), pointIndex); var closeCoord = yCoordinateCalculator.getCoordinate(closeValue); var columnWidth = getDataPointWidth(xCoordinateCalculator, dataPointWidth, dataPointWidthMode); var halfWidth = columnWidth / 2; var topSide = closeCoord > openCoord ? closeCoord : openCoord; var bottomSide = closeCoord > openCoord ? openCoord : closeCoord; // test candle body var isCandleBodyHit = (0, pointUtil_1.testIsInBounds)(xHitCoord, yHitCoord, xCoord - halfWidth, topSide, xCoord + halfWidth, bottomSide); // test candle wicks var distanceToWicks = (0, pointUtil_1.calcDistanceFromLineSegment)(xHitCoord, yHitCoord, xCoord, highCoord, xCoord, lowCoord); var isHit = isCandleBodyHit || distanceToWicks < hitTestRadius; return { isHit: isHit, openValue: openValue, highValue: highValue, lowValue: lowValue, closeValue: closeValue }; }; var testIsHitForMountain = function (isDigitalLine, xCoordinateCalculator, yCoordinateCalculator, dataSeries, zeroLineY, pointIndex, xHitCoord, yHitCoord) { var isHit; var xValues = dataSeries.getNativeXValues(); var isCategoryAxis = xCoordinateCalculator.isCategoryCoordinateCalculator; var xValue = isCategoryAxis ? pointIndex : dataSeries.getNativeValue(xValues, pointIndex); var yValues = dataSeries.getNativeYValues(); var xHitValue = xCoordinateCalculator.getDataValue(xHitCoord); var isLeftHit = xValue <= xHitValue; var secondPointIndex = isLeftHit ? pointIndex + 1 : pointIndex - 1; if (secondPointIndex < 0 || secondPointIndex >= dataSeries.count()) { return { isHit: false, secondPointIndex: undefined }; } var secondPointXValue = isCategoryAxis ? secondPointIndex : xValues.get(secondPointIndex); var xLeft = isLeftHit ? xValue : secondPointXValue; var yLeft = isLeftHit ? dataSeries.getNativeValue(yValues, pointIndex) : dataSeries.getNativeValue(yValues, secondPointIndex); var xRight = isLeftHit ? secondPointXValue : xValue; var yRight = isLeftHit ? dataSeries.getNativeValue(yValues, secondPointIndex) : dataSeries.getNativeValue(yValues, pointIndex); var xLeftCoord = xCoordinateCalculator.getCoordinate(xLeft); var xRightCoord = xCoordinateCalculator.getCoordinate(xRight); var yLeftCoord = yCoordinateCalculator.getCoordinate(yLeft); var yRightCoord = yCoordinateCalculator.getCoordinate(yRight); var zeroLineYCoord = yCoordinateCalculator.getCoordinate(zeroLineY); if (isDigitalLine) { if (yLeftCoord < zeroLineYCoord) { isHit = yHitCoord >= yLeftCoord && yHitCoord <= zeroLineYCoord; } else { isHit = yHitCoord >= zeroLineYCoord && yHitCoord <= yLeftCoord; } } else { var interpolatedLineY = interpolateLinear(xHitCoord, xLeftCoord, yLeftCoord, xRightCoord, yRightCoord); if (interpolatedLineY < zeroLineYCoord) { isHit = yHitCoord >= interpolatedLineY && yHitCoord <= zeroLineYCoord; } else { isHit = yHitCoord >= zeroLineYCoord && yHitCoord <= interpolatedLineY; } } return { isHit: isHit, secondPointIndex: secondPointIndex }; }; var testPolarIsHitForColumn = function (wasmContext, xCoordinateCalculator, yCoordinateCalculator, xValues, x1Values, yValues, xHitCoord, yHitCoord, zeroLineY, columnWidth, polarColumnMode, isVertical, isSorted) { // TODO: for unsorted data // if (!isSorted) { // console.error("not implemented"); // return { isHit: false, nearestPointIndex: -1 }; // } var xHitValue = xCoordinateCalculator.getDataValue(xHitCoord); var xHitCoordMinus2Pi = xHitCoord - 2 * Math.PI; var xHitValueMinus2Pi = xCoordinateCalculator.getDataValue(xHitCoordMinus2Pi); var xHitCoordPlus2Pi = xHitCoord + 2 * Math.PI; var xHitValuePlus2Pi = xCoordinateCalculator.getDataValue(xHitCoordPlus2Pi); var testIsYHit = function (resPointIndex$) { var yValue$ = yValues.get(resPointIndex$); var yCoord$ = yCoordinateCalculator.getCoordinate(yValue$); var zeroLineYCoord$ = yCoordinateCalculator.getCoordinate(zeroLineY); var maxYValueForColumn$ = zeroLineYCoord$ > yCoord$ ? zeroLineYCoord$ : yCoord$; var minYValueForColumn$ = zeroLineYCoord$ > yCoord$ ? yCoord$ : zeroLineYCoord$; return minYValueForColumn$ <= yHitCoord && yHitCoord <= maxYValueForColumn$; }; if (polarColumnMode === ColumnMode_1.EColumnMode.Mid || polarColumnMode === ColumnMode_1.EColumnMode.Start) { var xOffset = polarColumnMode === ColumnMode_1.EColumnMode.Mid ? 0 : columnWidth / 2; var xIndex = wasmContext.NumberUtil.FindIndex(xValues, xHitValue - xOffset, wasmContext.SCRTFindIndexSearchMode.Nearest, true); var xIndexMinus2Pi = wasmContext.NumberUtil.FindIndex(xValues, xHitValueMinus2Pi - xOffset, wasmContext.SCRTFindIndexSearchMode.Nearest, true); var xIndexPlus2Pi = wasmContext.NumberUtil.FindIndex(xValues, xHitValuePlus2Pi - xOffset, wasmContext.SCRTFindIndexSearchMode.Nearest, true); if (xIndex < 0 || xIndexMinus2Pi < 0 || xIndexPlus2Pi < 0) { return { isHit: false, nearestPointIndex: -1, isWithinDataBounds: false }; } else { var resPointIndex = xIndex; var xCoord = xCoordinateCalculator.getCoordinate(xValues.get(xIndex)); var resDist = Math.abs(xCoord + xOffset - xHitCoord); var xCoordMinus2Pi = xCoordinateCalculator.getCoordinate(xValues.get(xIndexMinus2Pi)); var xDistMinus2Pi = Math.abs(xCoordMinus2Pi + xOffset - xHitCoordMinus2Pi); if (xDistMinus2Pi < resDist) { resPointIndex = xIndexMinus2Pi; resDist = xDistMinus2Pi; } var xCoordPlus2Pi = xCoordinateCalculator.getCoordinate(xValues.get(xIndexPlus2Pi)); var xDistPlus2Pi = Math.abs(xCoordPlus2Pi + xOffset - xHitCoordPlus2Pi); if (xDistPlus2Pi < resDist) { resPointIndex = xIndexPlus2Pi; resDist = xDistPlus2Pi; } var isXHit = resDist <= columnWidth / 2; var isYHit = testIsYHit(resPointIndex); return { nearestPointIndex: resPointIndex, isHit: isXHit && isYHit, isWithinDataBounds: isXHit }; } } var xVectorSize = xValues.size(); // if (polarColumnMode === EColumnMode.Width) { // const xSumCoords: SCRTDoubleVector = new wasmContext.SCRTDoubleVector(); // if (xVectorSize > 0) { // let cur = 0; // xSumCoords.push_back(cur); // for (let i = 0; i < xVectorSize; i++) { // const xValue = xValues.get(i); // const xCoord = xCoordinateCalculator.getCoordinate(xValue); // cur += xCoord; // xSumCoords.push_back(cur); // } // const getNearestXIndexFn = ( // xHitCoord$: number // ): { // index: number; // dist: number; // isHitCoordGreater: boolean; // } => { // const xIndex$ = wasmContext.NumberUtil.FindIndex( // xSumCoords, // xHitCoord$, // wasmContext.SCRTFindIndexSearchMode.Nearest, // true // ); // const xCoord$ = xSumCoords.get(xIndex$); // return { // index: xIndex$, // dist: Math.abs(xHitCoord$ - xCoord$), // isHitCoordGreater: xHitCoord$ >= xCoord$ // }; // }; // let xNearestIndex = -1; // let xDist = Number.MAX_VALUE; // let isHitCoordGreater = false; // [xHitCoord, xHitCoordMinus2Pi, xHitCoordPlus2Pi].forEach(c => { // const r = getNearestXIndexFn(c); // if (r.dist < xDist) { // xNearestIndex = r.index; // xDist = r.dist; // isHitCoordGreater = r.isHitCoordGreater; // } // }); // if (xNearestIndex === -1) { // return { nearestPointIndex: -1, isHit: false, isWithinDataBounds: false }; // } else if (xNearestIndex === 0) { // return { // nearestPointIndex: 0, // isHit: isHitCoordGreater ? testIsYHit(0) : false, // isWithinDataBounds: isHitCoordGreater // }; // } else if (xNearestIndex === xVectorSize) { // return { // nearestPointIndex: xVectorSize - 1, // isHit: isHitCoordGreater ? false : testIsYHit(xVectorSize - 1), // isWithinDataBounds: !isHitCoordGreater // }; // } else { // const xIndex = isHitCoordGreater ? xNearestIndex : xNearestIndex - 1; // return { nearestPointIndex: xIndex, isHit: testIsYHit(xIndex), isWithinDataBounds: true }; // } // } // xSumCoords.delete(); // return { isHit: false, nearestPointIndex: -1, isWithinDataBounds: false }; // } if ([ColumnMode_1.EColumnMode.MidWidth, ColumnMode_1.EColumnMode.StartWidth, ColumnMode_1.EColumnMode.StartEnd].includes(polarColumnMode)) { var xCenterValuesVector_1; var widthVector_1; if (polarColumnMode === ColumnMode_1.EColumnMode.StartWidth) { xCenterValuesVector_1 = new wasmContext.SCRTDoubleVector(); widthVector_1 = x1Values; for (var i = 0; i < xVectorSize; i++) xCenterValuesVector_1.push_back(xValues.get(i) + x1Values.get(i) / 2); } else if (polarColumnMode === ColumnMode_1.EColumnMode.StartEnd) { xCenterValuesVector_1 = new wasmContext.SCRTDoubleVector(); widthVector_1 = new wasmContext.SCRTDoubleVector(); for (var i = 0; i < xVectorSize; i++) { var startVal = xValues.get(i); var endVal = x1Values.get(i); var width = endVal - startVal; xCenterValuesVector_1.push_back(startVal + width / 2); widthVector_1.push_back(width); } } else { xCenterValuesVector_1 = xValues; widthVector_1 = x1Values; } var getNearestXIndexFn_1 = function (xHitValue$) { var xIndex$ = wasmContext.NumberUtil.FindIndex(xCenterValuesVector_1, xHitValue$, wasmContext.SCRTFindIndexSearchMode.Nearest, true); var xValue$ = xCenterValuesVector_1.get(xIndex$); var columnWidth$ = widthVector_1.get(xIndex$); var dist$ = Math.max(Math.abs(xHitValue$ - xValue$) - columnWidth$ / 2, 0); if (dist$ === 0) { return { index: xIndex$, dist: 0, isWithinDataBounds: true }; } var isHitCoordGreater = xHitValue$ > xValue$; var xIndex2$ = isHitCoordGreater ? xIndex$ + 1 : xIndex$ - 1; var dist2$ = Number.MAX_VALUE; if (xIndex2$ >= 0 && xIndex2$ < xVectorSize) { var xValue2$ = xCenterValuesVector_1.get(xIndex2$); var columnWidth2$ = widthVector_1.get(xIndex2$); dist2$ = Math.max(Math.abs(xHitValue$ - xValue2$) - columnWidth2$ / 2, 0); } var xIndexRes$ = dist$ < dist2$ ? xIndex$ : xIndex2$; var distRes$ = dist$ < dist2$ ? dist$ : dist2$; var isWithinDataBounds$ = true; if (xIndexRes$ === 0 && xHitValue$ < xCenterValuesVector_1.get(0) && distRes$ > 0) { isWithinDataBounds$ = false; } if (xIndexRes$ === xVectorSize - 1 && xHitValue$ > xCenterValuesVector_1.get(xVectorSize - 1) && distRes$ > 0) { isWithinDataBounds$ = false; } return { index: xIndexRes$, dist: distRes$, isWithinDataBounds: isWithinDataBounds$ }; }; var xNearestIndex_1 = -1; var xDist_1 = Number.MAX_VALUE; var isWithinDataBounds_1 = false; [xHitValue, xHitValueMinus2Pi, xHitValuePlus2Pi].forEach(function (c) { var r = getNearestXIndexFn_1(c); if (r.dist < xDist_1) { xNearestIndex_1 = r.index; xDist_1 = r.dist; isWithinDataBounds_1 = r.isWithinDataBounds; } }); if (polarColumnMode === ColumnMode_1.EColumnMode.StartWidth) { xCenterValuesVector_1.delete(); } if (polarColumnMode === ColumnMode_1.EColumnMode.StartEnd) { widthVector_1.delete(); } return { isHit: xDist_1 === 0 && testIsYHit(xNearestIndex_1), nearestPointIndex: xNearestIndex_1, isWithinDataBounds: isWithinDataBounds_1 }; } return { isHit: false, nearestPointIndex: -1, isWithinDataBounds: false }; }; exports.hitTestHelpers = { createHitTestInfo: createHitTestInfo, getNearestPoint: getNearestPoint, getNearestXPoint: getNearestXPoint, getNearestXyPoint: getNearestXyPoint, getNearestXyyPoint: getNearestXyyPoint, getNearestUniformHeatmapPoint: getNearestUniformHeatmapPoint, getNearestNonUniformHeatmapPoint: getNearestNonUniformHeatmapPoint, getNearestTriangle: getNearestTriangle, getNearestLineSegment: getNearestLineSegment, testIsHitForPoint: testIsHitForPoint, testIsHitForLine: testIsHitForLine, testIsHitForBand: testIsHitForBand, testIsHitForColumn: testIsHitForColumn, testIsHitForOHLC: testIsHitForOHLC, testIsHitForMountain: testIsHitForMountain, testIsHitForErrorBars: testIsHitForErrorBars, testIsHitForImpulse: testIsHitForImpulse, testIsHitForBoxPlot: testIsHitForBoxPlot };