UNPKG

@antv/g2

Version:

the Grammar of Graphics in Javascript

166 lines 5.6 kB
import { Linear } from '@antv/scale'; import { isNumber, lowerFirst } from '@antv/util'; import { extent } from 'd3-array'; import { indexOf } from '../utils/array'; import { isPolar, isTranspose } from '../utils/coordinate'; import { angle, angleWithQuadrant, dist, sub } from '../utils/vector'; export function applyStyle(selection, style) { for (const [key, value] of Object.entries(style)) { selection.style(key, value); } } /** * Draw polygon path with points. * @param path * @param points */ export function appendPolygon(path, points) { points.forEach((p, idx) => idx === 0 ? path.moveTo(p[0], p[1]) : path.lineTo(p[0], p[1])); path.closePath(); return path; } /** * Draw arrow between `from` and `to`. * @param from * @param to * @returns */ export function arrowPoints(from, to, options) { const { arrowSize } = options; const size = typeof arrowSize === 'string' ? (+parseFloat(arrowSize) / 100) * dist(from, to) : arrowSize; // TODO Use config from style. // Default arrow rotate is 30°. const arrowAngle = Math.PI / 6; const angle = Math.atan2(to[1] - from[1], to[0] - from[0]); const arrowAngle1 = Math.PI / 2 - angle - arrowAngle; const arrow1 = [ to[0] - size * Math.sin(arrowAngle1), to[1] - size * Math.cos(arrowAngle1), ]; const arrowAngle2 = angle - arrowAngle; const arrow2 = [ to[0] - size * Math.cos(arrowAngle2), to[1] - size * Math.sin(arrowAngle2), ]; return [arrow1, arrow2]; } /** * Draw arc by from -> to, with center and radius. * @param path * @param from * @param to * @param center * @param radius */ export function appendArc(path, from, to, center, radius) { const startAngle = angle(sub(center, from)) + Math.PI; const endAngle = angle(sub(center, to)) + Math.PI; path.arc(center[0], center[1], radius, startAngle, endAngle, endAngle - startAngle < 0); return path; } /** * @todo Fix wrong key point. */ export function computeGradient(C, X, Y, from = 'y', mode = 'between', tpShape = false) { // The angles of gradients rendering are varies when 'from' and 'tpShape' are different. const getTheta = (from, tpShape) => { if (from === 'y' || from === true) { if (tpShape) { return 180; } else { return 90; } } else { if (tpShape) { return 90; } else { return 0; } } }; const P = from === 'y' || from === true ? Y : X; const theta = getTheta(from, tpShape); const I = indexOf(P); const [min, max] = extent(I, (i) => P[i]); // This need to improve for non-uniform distributed colors. const p = new Linear({ domain: [min, max], range: [0, 100], }); const percentage = (i) => isNumber(P[i]) && !Number.isNaN(P[i]) ? p.map(P[i]) : 0; const gradientMode = { // Interpolate the colors for this segment. between: (i) => `${C[i]} ${percentage(i)}%`, // Use the color of the start point as the color for this segment. start: (i) => i === 0 ? `${C[i]} ${percentage(i)}%` : `${C[i - 1]} ${percentage(i)}%, ${C[i]} ${percentage(i)}%`, // Use the color of the end point as the color for this segment. end: (i) => i === C.length - 1 ? `${C[i]} ${percentage(i)}%` : `${C[i]} ${percentage(i)}%, ${C[i + 1]} ${percentage(i)}%`, }; const gradient = I.sort((a, b) => percentage(a) - percentage(b)) .map(gradientMode[mode] || gradientMode['between']) .join(','); return `linear-gradient(${theta}deg, ${gradient})`; } export function reorder(points) { const [p0, p1, p2, p3] = points; return [p3, p0, p1, p2]; } export function getArcObject(coordinate, points, Y) { const [p0, p1, , p3] = isTranspose(coordinate) ? reorder(points) : points; const [y, y1] = Y; const center = coordinate.getCenter(); const a1 = angleWithQuadrant(sub(p0, center)); const a2 = angleWithQuadrant(sub(p1, center)); // There are two situations that a2 === a1: // 1. a1 - a2 = 0 // 2. |a1 - a2| = Math.PI * 2 // Distinguish them by y and y1: const a3 = a2 === a1 && y !== y1 ? a2 + Math.PI * 2 : a2; return { startAngle: a1, endAngle: a3 - a1 >= 0 ? a3 : Math.PI * 2 + a3, innerRadius: dist(p3, center), outerRadius: dist(p0, center), }; } /** * Pick connectStyle from style. * @param style */ export function getConnectStyle(style) { const PREFIX = 'connect'; return Object.fromEntries(Object.entries(style) .filter(([key]) => key.startsWith(PREFIX)) .map(([key, value]) => [ lowerFirst(key.replace(PREFIX, '').trim()), value, ]) .filter(([key]) => key !== undefined)); } export function toOpacityKey(options) { const { colorAttribute, opacityAttribute = colorAttribute } = options; return `${opacityAttribute}Opacity`; } export function getTransform(coordinate, value) { if (!isPolar(coordinate)) return ''; const center = coordinate.getCenter(); const { transform: suffix } = value; return `translate(${center[0]}, ${center[1]}) ${suffix || ''}`; } export function getOrigin(points) { if (points.length === 1) return points[0]; const [[x0, y0, z0 = 0], [x2, y2, z2 = 0]] = points; return [(x0 + x2) / 2, (y0 + y2) / 2, (z0 + z2) / 2]; } //# sourceMappingURL=utils.js.map