import { deepMix, get, isBoolean } from '@antv/util'; import { DIRECTION } from '../constant'; import { Coordinate, Scale } from '../dependents'; import { AxisCfg, AxisOption, Point, Region } from '../interface'; import { getName } from './scale'; import { vec2 } from '@antv/matrix-util'; /** * @ignore * get axis relative region ( 0 ~ 1) by direction when coordinate is rect * @param direction * @returns axis coordinate region */ export function getLineAxisRelativeRegion(direction: DIRECTION): Region { let start; let end; switch (direction) { case DIRECTION.TOP: start = { x: 0, y: 1 }; end = { x: 1, y: 1 }; break; case DIRECTION.RIGHT: start = { x: 1, y: 0 }; end = { x: 1, y: 1 }; break; case DIRECTION.BOTTOM: start = { x: 0, y: 0 }; end = { x: 1, y: 0 }; break; case DIRECTION.LEFT: start = { x: 0, y: 0 }; end = { x: 0, y: 1 }; break; default: start = end = { x: 0, y: 0 }; } return { start, end }; } /** * @ignore * get axis relative region ( 0 ~ 1) by direction when coordinate is polar * @param coordinate * @returns axis coordinate region */ export function getCircleAxisRelativeRegion(coordinate: Coordinate) { let start; let end; if (coordinate.isTransposed) { start = { x: 0, y: 0, }; end = { x: 1, y: 0, }; } else { start = { x: 0, y: 0, }; end = { x: 0, y: 1, }; } return { start, end }; } /** * @ignore * get the axis region from coordinate * @param coordinate * @param direction * @returns the axis region (start point, end point) */ export function getAxisRegion(coordinate: Coordinate, direction: DIRECTION): Region { let region = { start: { x: 0, y: 0 }, end: { x: 0, y: 0 } }; if (coordinate.isRect) { region = getLineAxisRelativeRegion(direction); } else if (coordinate.isPolar) { region = getCircleAxisRelativeRegion(coordinate); } const { start, end } = region; return { start: coordinate.convert(start), end: coordinate.convert(end), }; } /** * @ignore * get axis factor * @param coordinate * @param direction * @returns factor */ export function getAxisFactor(coordinate: Coordinate, direction: DIRECTION): number { // rect coordinate, by direction if (coordinate.isRect) { return coordinate.isTransposed ? [DIRECTION.RIGHT, DIRECTION.BOTTOM].includes(direction) ? 1 : -1 : [DIRECTION.BOTTOM, DIRECTION.RIGHT].includes(direction) ? -1 : 1; } // polar y axis, by angle if (coordinate.isPolar) { const startAngle = coordinate.x.start; return startAngle < 0 ? -1 : 1; } return 1; } /** * @ignore * whether the axis isVertical * @param region * @returns isVertical */ export function isVertical(region: Region): boolean { const { start, end } = region; return start.x === end.x; } /** * @ignore * get factor by region (real position) * @param region * @param center * @returns factor */ export function getAxisFactorByRegion(region: Region, center: Point): number { const { start, end } = region; const isAxisVertical = isVertical(region); // 垂直 if (isAxisVertical) { // 左方,从下到上、右方,从上到下 if ((start.y - end.y) * (center.x - start.x) > 0) { return 1; } else { return -1; } } else { // 下方,从左到右、上方,从右到做 if ((end.x - start.x) * (start.y - center.y) > 0) { return -1; } else { return 1; } } } /** * @ignore * get the axis cfg from theme, will mix the common cfg of legend theme * * @param theme view theme object * @param direction axis direction * @returns axis theme cfg */ export function getAxisThemeCfg(theme: object, direction: string): object { const axisTheme = get(theme, ['components', 'axis'], {}); return deepMix({}, get(axisTheme, ['common'], {}), deepMix({}, get(axisTheme, [direction], {}))); } /** * get the options of axis title,mix the cfg from theme, avoid common themeCfg not work * @param theme * @param direction * @param axisOptions * @returns axis title options */ export function getAxisTitleOptions(theme: object, direction: string, axisOptions?: object): object { const axisTheme = get(theme, ['components', 'axis'], {}); return deepMix( {}, get(axisTheme, ['common', 'title'], {}), deepMix({}, get(axisTheme, [direction, 'title'], {})), axisOptions ); } /** * @ignore * get circle axis center and radius * @param coordinate */ export function getCircleAxisCenterRadius(coordinate: Coordinate) { // @ts-ignore const { x, y, circleCenter: center } = coordinate; const isReflectY = y.start > y.end; const start = coordinate.isTransposed ? coordinate.convert({ x: isReflectY ? 0 : 1, y: 0, }) : coordinate.convert({ x: 0, y: isReflectY ? 0 : 1, }); const startVector: [number, number] = [start.x - center.x, start.y - center.y]; const normalVector: [number, number] = [1, 0]; const startAngle = start.y > center.y ? vec2.angle(startVector, normalVector) : vec2.angle(startVector, normalVector) * -1; const endAngle = startAngle + (x.end - x.start); const radius = Math.sqrt((start.x - center.x) ** 2 + (start.y - center.y) ** 2); return { center, radius, startAngle, endAngle, }; } /** * @ignore * 从配置中获取单个字段的 axis 配置 * @param axes * @param field * @returns the axis option of field */ export function getAxisOption(axes: Record | boolean, field: string) { if (isBoolean(axes)) { return axes === false ? false : {}; } return get(axes, [field]); } /** * @ignore * 如果配置了 position,则使用配置 * @param axisOption * @param def */ export function getAxisDirection(axisOption: AxisOption, def: DIRECTION): DIRECTION { return get(axisOption, 'position', def); } /** * 获取 axis 的 title 文本 * @param scale * @param axisOption */ export function getAxisTitleText(scale: Scale, axisOption: AxisCfg): string { return get(axisOption, ['title', 'text'], getName(scale)); }