|
1 | | -// Types |
2 | | -import type { Point } from '../VTrendline' |
| 1 | +// @ts-nocheck |
| 2 | +/* eslint-disable */ |
| 3 | + |
| 4 | +import { Point } from '../VSparkline' |
| 5 | +// import { checkCollinear, getDistance, moveTo } from './math' |
3 | 6 |
|
4 | 7 | /** |
5 | | - * Catmull-Rom spline converted to cubic Bezier. |
6 | | - * Unlike the previous corner-rounding approach, this passes through every |
7 | | - * data point, so markers placed at point positions are always on the line. |
8 | | - * |
9 | | - * `smooth` controls tension: 0 = straight lines, 8 (default true) = full curve. |
| 8 | + * From https://github.com/unsplash/react-trend/blob/master/src/helpers/DOM.helpers.js#L18 |
10 | 9 | */ |
11 | | -export function genPath (points: Point[], smooth: number, fill = false, height = 75) { |
| 10 | +export function genPath (points: Point[], radius: number, fill = false, height = 75) { |
12 | 11 | if (points.length === 0) return '' |
13 | | - |
14 | | - const start = points[0] |
| 12 | + const start = points.shift()! |
15 | 13 | const end = points[points.length - 1] |
16 | 14 |
|
17 | | - const prefix = fill |
18 | | - ? `M${start.x} ${height - start.x + 2} L${start.x} ${start.y}` |
19 | | - : `M${start.x} ${start.y}` |
| 15 | + return ( |
| 16 | + (fill ? `M${start.x} ${height - start.x + 2} L${start.x} ${start.y}` : `M${start.x} ${start.y}`) + |
| 17 | + points |
| 18 | + .map((point, index) => { |
| 19 | + const next = points[index + 1] |
| 20 | + const prev = points[index - 1] || start |
| 21 | + const isCollinear = next && checkCollinear(next, point, prev) |
20 | 22 |
|
21 | | - const suffix = fill ? `L${end.x} ${height - start.x + 2} Z` : '' |
| 23 | + if (!next || isCollinear) { |
| 24 | + return `L${point.x} ${point.y}` |
| 25 | + } |
22 | 26 |
|
23 | | - if (smooth === 0 || points.length < 3) { |
24 | | - return prefix + points.slice(1).map(point => `L${point.x} ${point.y}`).join('') + suffix |
25 | | - } |
| 27 | + const threshold = Math.min( |
| 28 | + getDistance(prev, point), |
| 29 | + getDistance(next, point) |
| 30 | + ) |
| 31 | + const isTooCloseForRadius = threshold / 2 < radius |
| 32 | + const radiusForPoint = isTooCloseForRadius ? threshold / 2 : radius |
| 33 | + |
| 34 | + const before = moveTo(prev, point, radiusForPoint) |
| 35 | + const after = moveTo(next, point, radiusForPoint) |
| 36 | + |
| 37 | + return `L${before.x} ${before.y}S${point.x} ${point.y} ${after.x} ${after.y}` |
| 38 | + }) |
| 39 | + .join('') + |
| 40 | + (fill ? `L${end.x} ${height - start.x + 2} Z` : '') |
| 41 | + ) |
| 42 | +} |
26 | 43 |
|
27 | | - const tension = Math.min(smooth / 8, 1) |
| 44 | +function int (value: string | number): number { |
| 45 | + return parseInt(value, 10) |
| 46 | +} |
28 | 47 |
|
29 | | - const curves = points.slice(1).map((curr, index) => { |
30 | | - const prev = points[index] |
31 | | - const prevPrev = points[Math.max(0, index - 1)] |
32 | | - const next = points[Math.min(points.length - 1, index + 2)] |
| 48 | +/** |
| 49 | + * https://en.wikipedia.org/wiki/Collinearity |
| 50 | + * x=(x1+x2)/2 |
| 51 | + * y=(y1+y2)/2 |
| 52 | + */ |
| 53 | +export function checkCollinear (p0: Point, p1: Point, p2: Point): boolean { |
| 54 | + return int(p0.x + p2.x) === int(2 * p1.x) && int(p0.y + p2.y) === int(2 * p1.y) |
| 55 | +} |
33 | 56 |
|
34 | | - const controlPoint1X = prev.x + (curr.x - prevPrev.x) * tension / 6 |
35 | | - const controlPoint1Y = prev.y + (curr.y - prevPrev.y) * tension / 6 |
36 | | - const controlPoint2X = curr.x - (next.x - prev.x) * tension / 6 |
37 | | - const controlPoint2Y = curr.y - (next.y - prev.y) * tension / 6 |
| 57 | +export function getDistance (p1: Point, p2: Point): number { |
| 58 | + return Math.sqrt( |
| 59 | + Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2) |
| 60 | + ) |
| 61 | +} |
38 | 62 |
|
39 | | - return `C${controlPoint1X} ${controlPoint1Y} ${controlPoint2X} ${controlPoint2Y} ${curr.x} ${curr.y}` |
40 | | - }) |
| 63 | +export function moveTo (to: Point, from: Point, radius: number) { |
| 64 | + const vector = { x: to.x - from.x, y: to.y - from.y } |
| 65 | + const length = Math.sqrt((vector.x * vector.x) + (vector.y * vector.y)) |
| 66 | + const unitVector = { x: vector.x / length, y: vector.y / length } |
41 | 67 |
|
42 | | - return prefix + curves.join('') + suffix |
| 68 | + return { |
| 69 | + x: from.x + unitVector.x * radius, |
| 70 | + y: from.y + unitVector.y * radius, |
| 71 | + } |
43 | 72 | } |
0 commit comments