UNPKG

react-plot

Version:

Library of React components to render SVG 2D plots.

161 lines 6.13 kB
import { euclidean } from 'ml-distance-euclidean'; import { useLegend } from './contexts/legendContext.js'; import { usePlotContext } from './contexts/plotContext.js'; import { validateAxis } from './utils.js'; export function usePosition(config) { const { axisContext, plotWidth, plotHeight } = usePlotContext(); const { x, y, xAxis, yAxis } = config; const [xScale, yScale] = validateAxis(axisContext, xAxis, yAxis, { onlyOrthogonal: true, }); return { x: convertValue(x, plotWidth, xScale), y: convertValue(y, plotHeight, yScale), }; } export function usePointsPosition(config) { const { axisContext, plotWidth, plotHeight } = usePlotContext(); const { points, xAxis, yAxis } = config; const [xScale, yScale] = validateAxis(axisContext, xAxis, yAxis, { onlyOrthogonal: true, }); return points .map((point) => `${convertValue(point.x, plotWidth, xScale)},${convertValue(point.y, plotHeight, yScale)}`) .join(' '); } export function useRectanglePosition(config) { const { axisContext, plotWidth, plotHeight } = usePlotContext(); const { x1, y1, x2, y2, xAxis, yAxis } = config; const [xScale, yScale] = validateAxis(axisContext, xAxis, yAxis, { onlyOrthogonal: true, }); return { x: convertMinValue(x1, x2, plotWidth, xScale), y: convertMinValue(y1, y2, plotHeight, yScale), width: convertDimensions(x1, x2, plotWidth, xScale), height: convertDimensions(y1, y2, plotHeight, yScale), }; } export function useEllipsePosition(config) { const { axisContext, plotWidth, plotHeight } = usePlotContext(); const { cx, cy, rx, ry, xAxis, yAxis } = config; const [xScale, yScale] = validateAxis(axisContext, xAxis, yAxis, { onlyOrthogonal: true, }); return { cx: convertValue(cx, plotWidth, xScale), cy: convertValue(cy, plotHeight, yScale), rx: convertValueAbs(rx, plotWidth, xScale), ry: convertValueAbs(ry, plotHeight, yScale), }; } export function useBoxPlotPosition(config) { const { axisContext, plotWidth, plotHeight } = usePlotContext(); const { min, max, q1, median, q3, width, y, xAxis, yAxis } = config; const [xScale, yScale] = validateAxis(axisContext, xAxis, yAxis, { onlyOrthogonal: true, }); const horizontal = ['top', 'bottom'].includes(axisContext[xAxis]?.position); return { min: convertValue(min, plotWidth, xScale), max: convertValue(max, plotWidth, xScale), q1: convertValue(q1, plotWidth, xScale), median: convertValue(median, plotWidth, xScale), q3: convertValue(q3, plotWidth, xScale), y: convertValue(y, plotHeight, yScale), width: convertValueAbs(width, plotHeight, yScale), horizontal, }; } export function useDirectedEllipsePosition(config) { const { axisContext, plotWidth, plotHeight } = usePlotContext(); const { x1: oldX1, y1: oldY1, x2: oldX2, y2: oldY2, width, xAxis, yAxis, } = config; const [xScale, yScale] = validateAxis(axisContext, xAxis, yAxis, { onlyOrthogonal: true, }); const { x1, y1, x2, y2 } = { x1: convertValue(oldX1, plotWidth, xScale), x2: convertValue(oldX2, plotWidth, xScale), y1: convertValue(oldY1, plotWidth, yScale), y2: convertValue(oldY2, plotWidth, yScale), }; const { cx, cy } = { cx: (x1 + x2) / 2, cy: (y1 + y2) / 2, }; const rotation = (y1 > y2 ? -1 : 1) * (x1 > x2 ? -1 : 1) * Math.asin(euclidean([x1, y1], [x1, cy]) / euclidean([x1, y1], [cx, cy])); const { widthX, widthY } = { widthX: (Math.sin(rotation) * convertValueAbs(width, plotHeight, xScale)) / 2, widthY: (Math.cos(rotation) * convertValueAbs(width, plotHeight, yScale)) / 2, }; return { cx, cy, rx: euclidean([x1, y1], [x2, y2]) / 2, ry: euclidean([0, 0], [widthX, widthY]), rotation: radsToDegs(rotation), }; } function radsToDegs(rad) { return (rad * 180) / Math.PI; } // convert functions function convertString(value, total) { return value.endsWith('%') ? (Number(value.slice(0, -1)) * total) / 100 : Number(value); } function convertValue(value, total, scale) { if (scale === undefined) return 0; return typeof value === 'number' ? scale(value) : convertString(value, total); } function convertMinValue(value1, value2, total, scale) { if (scale === undefined) return 0; return Math.min(typeof value2 === 'number' ? scale(value2) : convertString(value2, total), typeof value1 === 'number' ? scale(value1) : convertString(value1, total)); } function convertValueAbs(value, total, scale) { if (scale === undefined) return 0; return typeof value === 'number' ? Math.abs(scale(0) - scale(value)) : Math.abs(convertString(value, total)); } function convertToPx(value, total, scale) { if (scale === undefined) return 0; return typeof value === 'number' ? scale(value) - scale(0) : convertString(value, total); } function convertDimensions(value1, value2, total, scale) { if (scale === undefined) return 0; return Math.abs((typeof value2 === 'number' ? scale(value2) : convertString(value2, total)) - (typeof value1 === 'number' ? scale(value1) : convertString(value1, total))); } // other hooks export function useIsSeriesVisible(id) { const [legendState] = useLegend(); const value = legendState.labels.find((label) => label.id === id); return value ? value.isVisible : true; } export function useShift(options) { const { axisContext, plotWidth, plotHeight } = usePlotContext(); const { xAxis, yAxis, xShift, yShift } = options; const [xScale, yScale] = validateAxis(axisContext, xAxis, yAxis, { onlyOrthogonal: true, }); return { xShift: convertToPx(xShift, plotWidth, xScale), yShift: convertToPx(yShift, plotHeight, yScale), }; } //# sourceMappingURL=hooks.js.map