import { catmullRom2Bezier } from '@antv/path-util'; import { Category, Linear } from '@antv/scale'; import { each, head, isEqual, map } from '@antv/util'; type Point = [number, number]; /** * 点数组转 path * @param points */ function pointsToPath(points: Point[]): any[][] { return map(points, (p: Point, idx: number) => { const command = idx === 0 ? 'M' : 'L'; const [x, y] = p; return [command, x, y]; }); } /** * 将点连接成路径 path * @param points */ export function getLinePath(points: Point[]): any[][] { return pointsToPath(points); } /** * 将点连成平滑的曲线 * @param points */ export function getSmoothLinePath(points: Point[]): any[][] { if (points.length <= 2) { // 两点以内直接绘制成路径 return getLinePath(points); } const data = []; each(points, (p) => { // 当前点和上一个点一样的时候,忽略掉 if (!isEqual(p, data.slice(data.length - 2))) { data.push(p[0], p[1]); } }); // const constraint = [ // 范围 // [ 0, 0 ], // [ 1, 1 ], // ]; const path = catmullRom2Bezier(data, false); const [x, y] = head(points); path.unshift(['M', x, y]); return path; } /** * 将数据转成 path,利用 scale 的归一化能力 * @param data * @param width * @param height * @param smooth */ export function dataToPath(data: number[], width: number, height: number, smooth: boolean = true): any[][] { // 利用 scale 来获取 y 上的映射 const y = new Linear({ values: data, }); const x = new Category({ values: map(data, (v, idx) => idx), }); const points = map(data, (v: number, idx: number) => { return [x.scale(idx) * width, height - y.scale(v) * height] as [number, number]; }); return smooth ? getSmoothLinePath(points) : getLinePath(points); } /** * 获得 area 面积的横向连接线的 px 位置 * @param data * @param width * @param height */ export function getAreaLineY(data: number[], height: number): number { const y = new Linear({ values: data, }); // 当曲线全部为负数时,取最大值,当曲线全部为正数时,取最小值,当曲线有正有负,则取零点 const lineY = y.max < 0 ? y.max : Math.max(0, y.min); return height - y.scale(lineY) * height; } /** * 线 path 转 area path * @param path * @param width * @param height */ export function linePathToAreaPath(path: any[][], width: number, height: number, data: number[]): any[][] { const areaPath = [...path]; const lineYPx = getAreaLineY(data, height); areaPath.push(['L', width, lineYPx]); areaPath.push(['L', 0, lineYPx]); areaPath.push(['Z']); return areaPath; }