UNPKG

@tanstack/charts

Version:

A chart grammar for TypeScript and JavaScript. Marks consume your data directly, channels describe visual encodings, and the engine compiles them into a renderer-neutral keyed scene. TanStack's compact scales cover common numeric and categorical mappings.

193 lines (192 loc) 4.89 kB
import { resolveRectCornerRadii } from "./renderer-rect.js"; import { rectCornerRadiiPath, resolveRectCornerRadii as resolveRectCornerRadii2 } from "./renderer-rect.js"; const fullTurn = Math.PI * 2; function containsRectCornerRadii(x, y, width, height, cornerRadii, pointX, pointY) { const bounds = rectBounds(x, y, width, height); const left = bounds.x; const top = bounds.y; const right = left + bounds.width; const bottom = top + bounds.height; if (!(pointX >= left && pointX <= right && pointY >= top && pointY <= bottom)) { return false; } const [topLeft, topRight, bottomRight, bottomLeft] = resolveRectCornerRadii( cornerRadii, bounds.width, bounds.height ); if (pointX < left + topLeft && pointY < top + topLeft) { return insideCircle(pointX, pointY, left + topLeft, top + topLeft, topLeft); } if (pointX > right - topRight && pointY < top + topRight) { return insideCircle( pointX, pointY, right - topRight, top + topRight, topRight ); } if (pointX > right - bottomRight && pointY > bottom - bottomRight) { return insideCircle( pointX, pointY, right - bottomRight, bottom - bottomRight, bottomRight ); } if (pointX < left + bottomLeft && pointY > bottom - bottomLeft) { return insideCircle( pointX, pointY, left + bottomLeft, bottom - bottomLeft, bottomLeft ); } return true; } function squaredDistanceToRectCornerRadii(x, y, width, height, cornerRadii, pointX, pointY) { if (containsRectCornerRadii(x, y, width, height, cornerRadii, pointX, pointY)) { return 0; } const bounds = rectBounds(x, y, width, height); const left = bounds.x; const top = bounds.y; const right = left + bounds.width; const bottom = top + bounds.height; const [topLeft, topRight, bottomRight, bottomLeft] = resolveRectCornerRadii( cornerRadii, bounds.width, bounds.height ); return Math.min( squaredDistanceToSegment( left + topLeft, top, right - topRight, top, pointX, pointY ), squaredDistanceToSegment( right, top + topRight, right, bottom - bottomRight, pointX, pointY ), squaredDistanceToSegment( right - bottomRight, bottom, left + bottomLeft, bottom, pointX, pointY ), squaredDistanceToSegment( left, bottom - bottomLeft, left, top + topLeft, pointX, pointY ), squaredDistanceToArc( pointX, pointY, left + topLeft, top + topLeft, topLeft, Math.PI, Math.PI * 1.5 ), squaredDistanceToArc( pointX, pointY, right - topRight, top + topRight, topRight, Math.PI * 1.5, fullTurn ), squaredDistanceToArc( pointX, pointY, right - bottomRight, bottom - bottomRight, bottomRight, 0, Math.PI * 0.5 ), squaredDistanceToArc( pointX, pointY, left + bottomLeft, bottom - bottomLeft, bottomLeft, Math.PI * 0.5, Math.PI ) ); } function rectBounds(x, y, width, height) { const right = x + width; const bottom = y + height; return { x: Math.min(x, right), y: Math.min(y, bottom), width: Math.abs(width), height: Math.abs(height) }; } function insideCircle(x, y, centerX, centerY, radius) { const dx = x - centerX; const dy = y - centerY; return dx * dx + dy * dy <= radius * radius; } function squaredDistanceToArc(x, y, centerX, centerY, radius, startAngle, endAngle) { if (radius === 0) return squaredDistance(x, y, centerX, centerY); let angle = Math.atan2(y - centerY, x - centerX); if (angle < 0) angle += fullTurn; if (angle >= startAngle && angle <= endAngle) { const centerDistance = Math.hypot(x - centerX, y - centerY); return (centerDistance - radius) ** 2; } return Math.min( squaredDistance( x, y, centerX + Math.cos(startAngle) * radius, centerY + Math.sin(startAngle) * radius ), squaredDistance( x, y, centerX + Math.cos(endAngle) * radius, centerY + Math.sin(endAngle) * radius ) ); } function squaredDistanceToSegment(x1, y1, x2, y2, x, y) { const dx = x2 - x1; const dy = y2 - y1; const lengthSquared = dx * dx + dy * dy; if (lengthSquared === 0) return squaredDistance(x, y, x1, y1); const amount = Math.max( 0, Math.min(1, ((x - x1) * dx + (y - y1) * dy) / lengthSquared) ); return squaredDistance(x, y, x1 + amount * dx, y1 + amount * dy); } function squaredDistance(x1, y1, x2, y2) { return (x1 - x2) ** 2 + (y1 - y2) ** 2; } export { containsRectCornerRadii, rectCornerRadiiPath, resolveRectCornerRadii2 as resolveRectCornerRadii, squaredDistanceToRectCornerRadii };