|
@@ -0,0 +1,462 @@
|
|
|
|
|
+/**
|
|
|
|
|
+ * 其他辅助函数模块
|
|
|
|
|
+ * 包含轴文本列表、tooltip数据、标记线数据、上下文旋转、随机数、碰撞检测和词云点计算等辅助函数
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+// @ts-nocheck - 为了与原始 u-charts 代码保持兼容性,跳过类型检查
|
|
|
|
|
+
|
|
|
|
|
+import type { ChartOptions, SeriesItem } from '../data-processing/index.js';
|
|
|
|
|
+import { measureText } from '../utils/text.js';
|
|
|
|
|
+import { dataCombine, dataCombineStack, getDataRange } from '../data-processing/index.js';
|
|
|
|
|
+
|
|
|
|
|
+// 使用 any 类型来简化与原始 u-charts 代码的兼容性
|
|
|
|
|
+type AnyChartOptions = any;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 获取X轴文本列表
|
|
|
|
|
+ * @param series 数据系列
|
|
|
|
|
+ * @param opts 图表配置选项
|
|
|
|
|
+ * @param config 图表配置
|
|
|
|
|
+ * @param stack 是否堆叠
|
|
|
|
|
+ * @param index Y轴索引
|
|
|
|
|
+ * @returns 文本列表
|
|
|
|
|
+ */
|
|
|
|
|
+export function getXAxisTextList(
|
|
|
|
|
+ series: SeriesItem[],
|
|
|
|
|
+ opts: AnyChartOptions,
|
|
|
|
|
+ config: any,
|
|
|
|
|
+ stack: string,
|
|
|
|
|
+ index: number = -1
|
|
|
|
|
+): number[] {
|
|
|
|
|
+ let data: any[];
|
|
|
|
|
+ if (stack === 'stack') {
|
|
|
|
|
+ data = dataCombineStack(series, opts.categories.length);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ data = dataCombine(series);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const sorted: any[] = [];
|
|
|
|
|
+ // remove null from data
|
|
|
|
|
+ const filteredData = data.filter(function (item) {
|
|
|
|
|
+ if (typeof item === 'object' && item !== null) {
|
|
|
|
|
+ if (Array.isArray(item)) {
|
|
|
|
|
+ return item !== null;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return item.value !== null;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return item !== null;
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ filteredData.forEach(function (item) {
|
|
|
|
|
+ if (typeof item === 'object') {
|
|
|
|
|
+ if (Array.isArray(item)) {
|
|
|
|
|
+ if (opts.type === 'candle') {
|
|
|
|
|
+ item.forEach(function (subitem: any) {
|
|
|
|
|
+ sorted.push(subitem);
|
|
|
|
|
+ });
|
|
|
|
|
+ } else {
|
|
|
|
|
+ sorted.push(item[0]);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ sorted.push(item.value);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ sorted.push(item);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ let minData = 0;
|
|
|
|
|
+ let maxData = 0;
|
|
|
|
|
+ if (sorted.length > 0) {
|
|
|
|
|
+ minData = Math.min.apply(null, sorted);
|
|
|
|
|
+ maxData = Math.max.apply(null, sorted);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 为了兼容v1.9.0之前的项目
|
|
|
|
|
+ if (index > -1) {
|
|
|
|
|
+ if (typeof opts.xAxis.data[index].min === 'number') {
|
|
|
|
|
+ minData = Math.min(opts.xAxis.data[index].min, minData);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (typeof opts.xAxis.data[index].max === 'number') {
|
|
|
|
|
+ maxData = Math.max(opts.xAxis.data[index].max, maxData);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if (typeof opts.xAxis.min === 'number') {
|
|
|
|
|
+ minData = Math.min(opts.xAxis.min, minData);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (typeof opts.xAxis.max === 'number') {
|
|
|
|
|
+ maxData = Math.max(opts.xAxis.max, maxData);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (minData === maxData) {
|
|
|
|
|
+ const rangeSpan = maxData || 10;
|
|
|
|
|
+ maxData += rangeSpan;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const minRange = minData;
|
|
|
|
|
+ const maxRange = maxData;
|
|
|
|
|
+ const range: number[] = [];
|
|
|
|
|
+ const eachRange = (maxRange - minRange) / opts.xAxis.splitNumber;
|
|
|
|
|
+ for (let i = 0; i <= opts.xAxis.splitNumber; i++) {
|
|
|
|
|
+ range.push(minRange + eachRange * i);
|
|
|
|
|
+ }
|
|
|
|
|
+ return range;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 获取Y轴文本列表
|
|
|
|
|
+ * @param series 数据系列
|
|
|
|
|
+ * @param opts 图表配置选项
|
|
|
|
|
+ * @param config 图表配置
|
|
|
|
|
+ * @param stack 是否堆叠
|
|
|
|
|
+ * @param yData Y轴数据
|
|
|
|
|
+ * @param index Y轴索引
|
|
|
|
|
+ * @returns 文本列表
|
|
|
|
|
+ */
|
|
|
|
|
+export function getYAxisTextList(
|
|
|
|
|
+ series: SeriesItem[],
|
|
|
|
|
+ opts: AnyChartOptions,
|
|
|
|
|
+ config: any,
|
|
|
|
|
+ stack: string,
|
|
|
|
|
+ yData: { min?: number; max?: number },
|
|
|
|
|
+ index: number = -1
|
|
|
|
|
+): number[] {
|
|
|
|
|
+ let data: any[];
|
|
|
|
|
+ if (stack === 'stack') {
|
|
|
|
|
+ data = dataCombineStack(series, opts.categories.length);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ data = dataCombine(series);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const sorted: any[] = [];
|
|
|
|
|
+ // remove null from data
|
|
|
|
|
+ const filteredData = data.filter(function (item) {
|
|
|
|
|
+ if (typeof item === 'object' && item !== null) {
|
|
|
|
|
+ if (Array.isArray(item)) {
|
|
|
|
|
+ return item !== null;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return item.value !== null;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return item !== null;
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ filteredData.forEach(function (item) {
|
|
|
|
|
+ if (typeof item === 'object') {
|
|
|
|
|
+ if (Array.isArray(item)) {
|
|
|
|
|
+ if (opts.type === 'candle') {
|
|
|
|
|
+ item.forEach(function (subitem: any) {
|
|
|
|
|
+ sorted.push(subitem);
|
|
|
|
|
+ });
|
|
|
|
|
+ } else {
|
|
|
|
|
+ sorted.push(item[1]);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ sorted.push(item.value);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ sorted.push(item);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ let minData = yData.min || 0;
|
|
|
|
|
+ let maxData = yData.max || 0;
|
|
|
|
|
+ if (sorted.length > 0) {
|
|
|
|
|
+ minData = Math.min.apply(null, sorted);
|
|
|
|
|
+ maxData = Math.max.apply(null, sorted);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (minData === maxData) {
|
|
|
|
|
+ if (maxData === 0) {
|
|
|
|
|
+ maxData = 10;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ minData = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const dataRange = getDataRange(minData, maxData);
|
|
|
|
|
+ const minRange =
|
|
|
|
|
+ yData.min === undefined || yData.min === null
|
|
|
|
|
+ ? dataRange.minRange
|
|
|
|
|
+ : yData.min;
|
|
|
|
|
+ const maxRange =
|
|
|
|
|
+ yData.max === undefined || yData.max === null
|
|
|
|
|
+ ? dataRange.maxRange
|
|
|
|
|
+ : yData.max;
|
|
|
|
|
+ const eachRange = (maxRange - minRange) / opts.yAxis.splitNumber;
|
|
|
|
|
+ const range: number[] = [];
|
|
|
|
|
+ for (let i = 0; i <= opts.yAxis.splitNumber; i++) {
|
|
|
|
|
+ range.push(minRange + eachRange * i);
|
|
|
|
|
+ }
|
|
|
|
|
+ return range.reverse();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 计算提示框Y轴数据
|
|
|
|
|
+ * @param point 当前触摸点坐标
|
|
|
|
|
+ * @param series 数据系列
|
|
|
|
|
+ * @param opts 图表配置选项
|
|
|
|
|
+ * @param config 图表配置
|
|
|
|
|
+ * @param eachSpacing 每个数据点的间距
|
|
|
|
|
+ * @returns Y轴数据数组
|
|
|
|
|
+ */
|
|
|
|
|
+export function calTooltipYAxisData(
|
|
|
|
|
+ point: { x: number; y: number },
|
|
|
|
|
+ series: SeriesItem[],
|
|
|
|
|
+ opts: AnyChartOptions,
|
|
|
|
|
+ config: any,
|
|
|
|
|
+ eachSpacing: number
|
|
|
|
|
+): string[] {
|
|
|
|
|
+ const ranges: number[][] = [].concat(opts.chartData.yAxisData.ranges);
|
|
|
|
|
+ const spacingValid = opts.height - opts.area[0] - opts.area[2];
|
|
|
|
|
+ const minAxis = opts.area[0];
|
|
|
|
|
+ const items: string[] = [];
|
|
|
|
|
+
|
|
|
|
|
+ for (let i = 0; i < ranges.length; i++) {
|
|
|
|
|
+ const maxVal = Math.max.apply(null, ranges[i]);
|
|
|
|
|
+ const minVal = Math.min.apply(null, ranges[i]);
|
|
|
|
|
+ let item = maxVal - ((maxVal - minVal) * (point.y - minAxis)) / spacingValid;
|
|
|
|
|
+ item =
|
|
|
|
|
+ opts.yAxis.data && opts.yAxis.data[i].formatter
|
|
|
|
|
+ ? opts.yAxis.data[i].formatter(item, i, opts)
|
|
|
|
|
+ : item.toFixed(0);
|
|
|
|
|
+ items.push(String(item));
|
|
|
|
|
+ }
|
|
|
|
|
+ return items;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 计算标记线数据
|
|
|
|
|
+ * @param points 标记线点数组
|
|
|
|
|
+ * @param opts 图表配置选项
|
|
|
|
|
+ * @returns 标记线数据数组
|
|
|
|
|
+ */
|
|
|
|
|
+export function calMarkLineData(
|
|
|
|
|
+ points: Array<{ value: number; yAxisIndex?: number; y?: number }>,
|
|
|
|
|
+ opts: AnyChartOptions
|
|
|
|
|
+): Array<{ value: number; yAxisIndex?: number; y?: number }> {
|
|
|
|
|
+ let minRange: number, maxRange: number;
|
|
|
|
|
+ const spacingValid = opts.height - opts.area[0] - opts.area[2];
|
|
|
|
|
+
|
|
|
|
|
+ for (let i = 0; i < points.length; i++) {
|
|
|
|
|
+ points[i].yAxisIndex = points[i].yAxisIndex ? points[i].yAxisIndex : 0;
|
|
|
|
|
+ const range = [].concat(opts.chartData.yAxisData.ranges[points[i].yAxisIndex!]);
|
|
|
|
|
+ minRange = range.pop() as number;
|
|
|
|
|
+ maxRange = range.shift() as number;
|
|
|
|
|
+ const height =
|
|
|
|
|
+ (spacingValid * (points[i].value - minRange)) / (maxRange - minRange);
|
|
|
|
|
+ points[i].y = opts.height - Math.round(height) - opts.area[2];
|
|
|
|
|
+ }
|
|
|
|
|
+ return points;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 上下文旋转
|
|
|
|
|
+ * @param context Canvas上下文
|
|
|
|
|
+ * @param opts 图表配置选项
|
|
|
|
|
+ */
|
|
|
|
|
+export function contextRotate(
|
|
|
|
|
+ context: any,
|
|
|
|
|
+ opts: AnyChartOptions
|
|
|
|
|
+): void {
|
|
|
|
|
+ if (opts.rotateLock !== true) {
|
|
|
|
|
+ context.translate(opts.height, 0);
|
|
|
|
|
+ context.rotate((90 * Math.PI) / 180);
|
|
|
|
|
+ } else if (opts._rotate_ !== true) {
|
|
|
|
|
+ context.translate(opts.height, 0);
|
|
|
|
|
+ context.rotate((90 * Math.PI) / 180);
|
|
|
|
|
+ opts._rotate_ = true;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 生成标准化整数随机数
|
|
|
|
|
+ * @param min 最小值
|
|
|
|
|
+ * @param max 最大值
|
|
|
|
|
+ * @param iter 迭代次数
|
|
|
|
|
+ * @returns 随机整数
|
|
|
|
|
+ */
|
|
|
|
|
+export function normalInt(min: number, max: number, iter: number): number {
|
|
|
|
|
+ iter = iter === 0 ? 1 : iter;
|
|
|
|
|
+ const arr: number[] = [];
|
|
|
|
|
+ for (let i = 0; i < iter; i++) {
|
|
|
|
|
+ arr[i] = Math.random();
|
|
|
|
|
+ }
|
|
|
|
|
+ return (
|
|
|
|
|
+ Math.floor((arr.reduce((i, j) => i + j) / iter) * (max - min)) + min
|
|
|
|
|
+ );
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 新碰撞检测
|
|
|
|
|
+ * @param area 区域 [x1, y1, x2, y2]
|
|
|
|
|
+ * @param points 点数组
|
|
|
|
|
+ * @param width 宽度
|
|
|
|
|
+ * @param height 高度
|
|
|
|
|
+ * @returns 是否碰撞
|
|
|
|
|
+ */
|
|
|
|
|
+export function collisionNew(
|
|
|
|
|
+ area: number[],
|
|
|
|
|
+ points: Array<{ area?: number[] }>,
|
|
|
|
|
+ width: number,
|
|
|
|
|
+ height: number
|
|
|
|
|
+): boolean {
|
|
|
|
|
+ let isIn = false;
|
|
|
|
|
+ for (let i = 0; i < points.length; i++) {
|
|
|
|
|
+ if (points[i].area) {
|
|
|
|
|
+ if (
|
|
|
|
|
+ area[3] < points[i].area![1] ||
|
|
|
|
|
+ area[0] > points[i].area![2] ||
|
|
|
|
|
+ area[1] > points[i].area![3] ||
|
|
|
|
|
+ area[2] < points[i].area![0]
|
|
|
|
|
+ ) {
|
|
|
|
|
+ if (
|
|
|
|
|
+ area[0] < 0 ||
|
|
|
|
|
+ area[1] < 0 ||
|
|
|
|
|
+ area[2] > width ||
|
|
|
|
|
+ area[3] > height
|
|
|
|
|
+ ) {
|
|
|
|
|
+ isIn = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ isIn = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ isIn = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return isIn;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 获取词云点
|
|
|
|
|
+ * @param opts 图表配置选项
|
|
|
|
|
+ * @param type 类型 ('normal' | 'vertical')
|
|
|
|
|
+ * @param context Canvas上下文
|
|
|
|
|
+ * @returns 词云数据点数组
|
|
|
|
|
+ */
|
|
|
|
|
+export function getWordCloudPoint(
|
|
|
|
|
+ opts: AnyChartOptions,
|
|
|
|
|
+ type: 'normal' | 'vertical',
|
|
|
|
|
+ context: any
|
|
|
|
|
+): Array<{
|
|
|
|
|
+ name: string;
|
|
|
|
|
+ textSize: number;
|
|
|
|
|
+ area: number[];
|
|
|
|
|
+ areav?: number[];
|
|
|
|
|
+ rotate?: boolean;
|
|
|
|
|
+}> {
|
|
|
|
|
+ const points = opts.series;
|
|
|
|
|
+
|
|
|
|
|
+ switch (type) {
|
|
|
|
|
+ case 'normal':
|
|
|
|
|
+ for (let i = 0; i < points.length; i++) {
|
|
|
|
|
+ const text = points[i].name;
|
|
|
|
|
+ const tHeight = points[i].textSize * opts.pix;
|
|
|
|
|
+ const tWidth = measureText(text, tHeight, context);
|
|
|
|
|
+ let x: number, y: number;
|
|
|
|
|
+ let area: number[];
|
|
|
|
|
+ let breaknum = 0;
|
|
|
|
|
+ while (true) {
|
|
|
|
|
+ breaknum++;
|
|
|
|
|
+ x = normalInt(-opts.width / 2, opts.width / 2, 5) - tWidth / 2;
|
|
|
|
|
+ y = normalInt(-opts.height / 2, opts.height / 2, 5) + tHeight / 2;
|
|
|
|
|
+ area = [
|
|
|
|
|
+ x - 5 + opts.width / 2,
|
|
|
|
|
+ y - 5 - tHeight + opts.height / 2,
|
|
|
|
|
+ x + tWidth + 5 + opts.width / 2,
|
|
|
|
|
+ y + 5 + opts.height / 2,
|
|
|
|
|
+ ];
|
|
|
|
|
+ const isCollision = collisionNew(area, points, opts.width, opts.height);
|
|
|
|
|
+ if (!isCollision) break;
|
|
|
|
|
+ if (breaknum === 1000) {
|
|
|
|
|
+ area = [-100, -100, -100, -100];
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ (points[i] as any).area = area;
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'vertical':
|
|
|
|
|
+ function Spin(): boolean {
|
|
|
|
|
+ // 获取均匀随机值,是否旋转,旋转的概率为(1-0.5)
|
|
|
|
|
+ if (Math.random() > 0.7) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ for (let i = 0; i < points.length; i++) {
|
|
|
|
|
+ const text = points[i].name;
|
|
|
|
|
+ const tHeight = points[i].textSize * opts.pix;
|
|
|
|
|
+ const tWidth = measureText(text, tHeight, context);
|
|
|
|
|
+ const isSpin = Spin();
|
|
|
|
|
+ let x: number, y: number, area: number[], areav: number[] | undefined;
|
|
|
|
|
+ let breaknum = 0;
|
|
|
|
|
+ while (true) {
|
|
|
|
|
+ breaknum++;
|
|
|
|
|
+ let isCollision: boolean;
|
|
|
|
|
+ if (isSpin) {
|
|
|
|
|
+ x = normalInt(-opts.width / 2, opts.width / 2, 5) - tWidth / 2;
|
|
|
|
|
+ y = normalInt(-opts.height / 2, opts.height / 2, 5) + tHeight / 2;
|
|
|
|
|
+ area = [
|
|
|
|
|
+ y - 5 - tWidth + opts.width / 2,
|
|
|
|
|
+ -x - 5 + opts.height / 2,
|
|
|
|
|
+ y + 5 + opts.width / 2,
|
|
|
|
|
+ -x + tHeight + 5 + opts.height / 2,
|
|
|
|
|
+ ];
|
|
|
|
|
+ areav = [
|
|
|
|
|
+ opts.width -
|
|
|
|
|
+ (opts.width / 2 - opts.height / 2) -
|
|
|
|
|
+ (-x + tHeight + 5 + opts.height / 2) -
|
|
|
|
|
+ 5,
|
|
|
|
|
+ (opts.height / 2 - opts.width / 2) +
|
|
|
|
|
+ (y - 5 - tWidth + opts.width / 2) -
|
|
|
|
|
+ 5,
|
|
|
|
|
+ opts.width -
|
|
|
|
|
+ (opts.width / 2 - opts.height / 2) -
|
|
|
|
|
+ (-x + tHeight + 5 + opts.height / 2) +
|
|
|
|
|
+ tHeight,
|
|
|
|
|
+ (opts.height / 2 - opts.width / 2) +
|
|
|
|
|
+ (y - 5 - tWidth + opts.width / 2) +
|
|
|
|
|
+ tWidth +
|
|
|
|
|
+ 5,
|
|
|
|
|
+ ];
|
|
|
|
|
+ isCollision = collisionNew(areav, points, opts.height, opts.width);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ x = normalInt(-opts.width / 2, opts.width / 2, 5) - tWidth / 2;
|
|
|
|
|
+ y = normalInt(-opts.height / 2, opts.height / 2, 5) + tHeight / 2;
|
|
|
|
|
+ area = [
|
|
|
|
|
+ x - 5 + opts.width / 2,
|
|
|
|
|
+ y - 5 - tHeight + opts.height / 2,
|
|
|
|
|
+ x + tWidth + 5 + opts.width / 2,
|
|
|
|
|
+ y + 5 + opts.height / 2,
|
|
|
|
|
+ ];
|
|
|
|
|
+ isCollision = collisionNew(area, points, opts.width, opts.height);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!isCollision) break;
|
|
|
|
|
+ if (breaknum === 1000) {
|
|
|
|
|
+ area = [-1000, -1000, -1000, -1000];
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (isSpin) {
|
|
|
|
|
+ (points[i] as any).area = areav;
|
|
|
|
|
+ (points[i] as any).areav = area;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ (points[i] as any).area = area;
|
|
|
|
|
+ }
|
|
|
|
|
+ (points[i] as any).rotate = isSpin;
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return points as any;
|
|
|
|
|
+}
|