如何实现多圆均匀环绕一个圆

12 阅读13分钟

初始需求】:给出一组信息(中心圆坐标中心圆半径环绕圆半径环绕圆距离中心圆距离),是否能够求助 最大 可以有多少个环绕圆。

延展需求】:基于以上需求,能否实现可设置 最大数量 环绕圆 均匀分布 ,以及能否 自定义 环绕圆之间的 间隔

一、完整代码及样式示例

1.1. 不同情况下效果图

① 紧凑排布,显示最大数量环绕圆

② 均匀分布,显示最大数量环绕圆

③ 自定义环绕圆之间的间隔,显示配置间隔后最大环绕圆数量

1.2. 完整代码示例

二、实现方法及逻辑解析

2.1. 计算逻辑图解

circle-flow.png

2.2. 计算环绕圆不同情况图解

circle-manner.png

三、核心代码(TS及JS)

TS版本封装的SurroundingCircle

interface SurroundingCircleInter {
  centerCircle: {
    position: {
      x: number;
      y: number;
    };
    radius: number;
  };
  surroundCircleRadius: number;
  distance: number;
  surroundManner?: string; // 环绕方式,默认为 "default"
  surroundGap?: number | null; // 环绕圆之间的间距,默认为 null
}

interface CircleDataInter {
  circlePoints: Array<{ x: number; y: number }>;
  surroundingCircleGap: number;
  pointsGather: Array<{ x: number; y: number }>;
}

interface CirclePointInter {
  position: { x: number; y: number };
  radius: number;
}

interface PointsGatherInter {
  circlePoints: Array<{ x: number; y: number }>;
  points: Array<{ x: number; y: number }>;
}

/**
 * 环绕圆类
 * @class surroundingCircle
 * @param {Object} options - 配置选项
 * @param {Object} options.centerCircle - 中心圆的配置
 * @param {Object} options.centerCircle.position - 中心圆的中心点坐标
 * @param {number} options.centerCircle.radius - 中心圆的半径
 * @param {number} options.surroundCircleRadius - 环绕圆的半径
 * @param {number} options.distance - 环绕圆与中心圆之间的距离
 * @param {string} [options.surroundManner="default"] - 环绕方式,"default" 或 "average"
 * @param {number|null} [options.surroundGap=null] - 环绕圆之间的间距,默认为 null
 * @description 计算环绕圆的点数据
 */
export class SurroundingCircle {
  centerPointX: number;
  centerPointY: number;
  centerCircleRadius: number;
  surroundCircleRadius: number;
  distance: number;
  surroundManner: string;
  surroundGap: number | null;
  maxSurroundGap: number;
  constructor({
    centerCircle,
    surroundCircleRadius,
    distance,
    surroundManner = "default",
    surroundGap = null,
  }: SurroundingCircleInter) {
    this.centerPointX = centerCircle.position.x;
    this.centerPointY = centerCircle.position.y;
    this.centerCircleRadius = centerCircle.radius;
    this.surroundCircleRadius = surroundCircleRadius;
    this.distance = distance;
    this.surroundManner = surroundManner;
    this.surroundGap = surroundGap;
    this.maxSurroundGap = 0;
  }
  
  /**
   * 更新环绕圆配置参数
   * @param {Object} options - 配置选项
   * @param {Object} options.centerCircle - 中心圆的配置
   * @param {Object} options.centerCircle.position - 中心圆的中心点坐标
   * @param {number} options.centerCircle.radius - 中心圆的半径
   * @param {number} options.surroundCircleRadius - 环绕圆的半径
   * @param {number} options.distance - 环绕圆与中心圆之间的距离
   * @param {string} [options.surroundManner="default"] - 环绕方式,"default" 或 "average"
   * @param {number|null} [options.surroundGap=null] - 环绕圆之间的间距,默认为 null
   */
  setSurroundingCircleOptions(options: SurroundingCircleInter): void {
    this.centerPointX = options.centerCircle.position.x;
    this.centerPointY = options.centerCircle.position.y;
    this.centerCircleRadius = options.centerCircle.radius;
    this.surroundCircleRadius = options.surroundCircleRadius;
    this.distance = options.distance;
    this.surroundManner = options.surroundManner ?? "default";
    this.surroundGap = options.surroundGap ?? null;
    this.maxSurroundGap = 0;
  }

  /**
   * 获取环绕圆的数据
   * @returns {Object} 返回包含环绕圆点和距离的对象
   */
  getCircleData(): CircleDataInter {
    if (this.surroundManner === "default") {
      return this.getCloseCircleData();
    } else if (this.surroundManner === "average") {
      return this.getAverageCircleData();
    } else {
      throw new Error("Invalid surround manner specified.");
    }
  }

  /**
   * 获取环绕圆的点数据--紧凑型
   * @returns {Object} 返回包含环绕圆点和距离的对象
   */
  getCloseCircleData(): CircleDataInter {
    return this.handleCalculatePoints(
      this.centerPointX,
      this.centerPointY,
      this.centerCircleRadius,
      this.surroundCircleRadius,
      this.distance
    );
  }

  /**
   * 获取平均环绕圆的数据-平均型
   * @returns {Object} 返回包含平均环绕圆点和距离的对象
   */
  getAverageCircleData(): CircleDataInter {
    const circlePointGather: CircleDataInter = this.getCloseCircleData();
    const averageGap: number =
      this.surroundGap ??
      circlePointGather.surroundingCircleGap /
        circlePointGather.circlePoints.length;

    return this.handleCalculatePoints(
      this.centerPointX,
      this.centerPointY,
      this.centerCircleRadius,
      this.surroundCircleRadius,
      this.distance,
      averageGap
    );
  }

  /**
   * 获取最大间隔值
   * @returns {number} 返回可设置间隔的最大值
   */
  getMaxSurroundGap(): number {
    return this.maxSurroundGap;
  }

  /**
   * 设置最大间距
   * @param {object} virtualCircle - 虚拟圆对象 { position: { x, y }, radius }
   * @param {object} originPoints - 原始圆对象 { position: { x, y }, radius }
   * @param {number} x - 虚拟圆心的 x 坐标
   * @param {number} y - 虚拟圆心的 y 坐标
   * @param {number} R - 虚拟圆的半径
   * @param {number} d - 偏移量
   * @param {number} r - 原始圆的半径
   */
  setMaxSurroundGap(
    virtualCircle: CirclePointInter,
    originPoints: Array<{ x: number; y: number }> | null,
    x: number,
    y: number,
    R: number,
    d: number,
    r: number
  ) {
    // 对称于originCircle的点,用于计算最大gap,即this.maxSurroundGap
    const symmetryOriginCircle: CirclePointInter = {
      position: { x: x, y: y - R - d - r },
      radius: r,
    };
    // 开始计算最大gap,即this.maxSurroundGap
    this.findCircleIntersection(
      virtualCircle.position.x,
      virtualCircle.position.y,
      virtualCircle.radius,
      symmetryOriginCircle.position.x,
      symmetryOriginCircle.position.y,
      symmetryOriginCircle.radius
    )?.forEach((item) => {
      if (item.x === (originPoints ? originPoints[0] : { x: 0, y: 0 }).x) {
        this.maxSurroundGap =
          (originPoints ? originPoints[0] : { x: 0, y: 0 }).y - item.y;
      }
    });
  }

  /**
   * 处理计算点的逻辑 --- 主要核心函数
   * @param {number} x - 虚拟圆心的 x 坐标
   * @param {number} y - 虚拟圆心的 y 坐标
   * @param {number} R - 虚拟圆的半径
   * @param {number} r - 原始圆的半径
   * @param {number} d - 偏移量
   * @param {number} gap - 原始小圆之间的间隔
   * @returns {Object} 返回包含交点和距离的对象
   */
  handleCalculatePoints(
    x: number,
    y: number,
    R: number,
    r: number,
    d: number,
    gap: number = 0
  ): CircleDataInter {
    const virtualCircle: CirclePointInter = {
      position: { x, y },
      radius: R + d + r,
    };
    const originCircle: CirclePointInter = {
      position: { x: x, y: y + R + d + r },
      radius: r,
    };
    const originPoints: Array<{ x: number; y: number }> | null =
      this.findCircleIntersection(
        virtualCircle.position.x,
        virtualCircle.position.y,
        virtualCircle.radius,
        originCircle.position.x,
        originCircle.position.y,
        originCircle.radius
      );

    // 如果设置了平均间隔且配置了间隔值,则需要执行以下逻辑
    if (this.surroundManner !== "default" && this.surroundGap) {
      // 计算出最大可配置间隔值
      this.setMaxSurroundGap(virtualCircle, originPoints, x, y, R, d, r);
      // 如果设置间隔平均分配且间隔超出最大间隔时,仅返回originCircle圆数据
      if (this.surroundManner !== "default" && gap >= this.maxSurroundGap) {
        return {
          circlePoints: [originCircle.position],
          surroundingCircleGap: gap,
          pointsGather: [originCircle.position, ...(originPoints ?? [])],
        };
      }
    }

    // 开始进行过滤后的正常计算
    const rightCirclePoints: PointsGatherInter = this.calculatePoints(
      originPoints ? originPoints[0] : { x: 0, y: 0 },
      virtualCircle,
      originCircle.radius,
      R,
      gap,
      "right"
    );
    const leftCirclePoints: PointsGatherInter = this.calculatePoints(
      originPoints ? originPoints[1] : { x: 0, y: 0 },
      virtualCircle,
      originCircle.radius,
      R,
      gap,
      "left"
    );
    // 计算:如果左右两部分最后一个点集合之间的距离小于gap时,去除掉其中一边的圆数据即对应的点集合数据
    if (
      rightCirclePoints.circlePoints.length !== 0 &&
      leftCirclePoints.circlePoints.length !== 0
    ) {
      const leftDistanceRight: number = this.calculateDistance(
        rightCirclePoints.points[rightCirclePoints.points.length - 1].x,
        rightCirclePoints.points[rightCirclePoints.points.length - 1].y,
        leftCirclePoints.points[leftCirclePoints.points.length - 1].x,
        leftCirclePoints.points[leftCirclePoints.points.length - 1].y
      );
      if (leftDistanceRight < gap && this.surroundGap) {
        rightCirclePoints.circlePoints.pop();
        rightCirclePoints.points.slice(0, rightCirclePoints.points.length - 3);
      }
    }
    // 初步整理圆心点集合、点集合、默认紧凑时的间距
    const circlePoints: Array<{ x: number; y: number }> = [
      originCircle.position,
      ...rightCirclePoints.circlePoints,
      ...leftCirclePoints.circlePoints,
    ];
    const pointsGather: Array<{ x: number; y: number }> = [
      ...rightCirclePoints.points,
      ...leftCirclePoints.points,
    ];
    let distance: number = this.calculateDistance(
      leftCirclePoints.points[leftCirclePoints.points.length - 1].x,
      leftCirclePoints.points[leftCirclePoints.points.length - 1].y,
      rightCirclePoints.points[rightCirclePoints.points.length - 1].x,
      rightCirclePoints.points[rightCirclePoints.points.length - 1].y
    );

    // 如果左右两部分都没有适合间隔的圆数据,则将originCircle右侧点开始,计算gap距离符合需求的圆数据
    if (
      rightCirclePoints.circlePoints.length === 0 &&
      leftCirclePoints.circlePoints.length === 0 &&
      gap <= R * 2 + d * 2
    ) {
      this.findCircleIntersection(
        virtualCircle.position.x,
        virtualCircle.position.y,
        virtualCircle.radius,
        (originPoints ? originPoints[1] : { x: 0, y: 0 }).x,
        (originPoints ? originPoints[1] : { x: 0, y: 0 }).y,
        gap
      )?.forEach((item) => {
        if (item.x < virtualCircle.position.x) {
          pointsGather.push(item);
          this.findCircleIntersection(
            virtualCircle.position.x,
            virtualCircle.position.y,
            virtualCircle.radius,
            item.x,
            item.y,
            r
          )?.forEach((circle) => {
            if (circle.x > item.x) {
              circlePoints.push(circle);
              this.findCircleIntersection(
                virtualCircle.position.x,
                virtualCircle.position.y,
                virtualCircle.radius,
                circle.x,
                circle.y,
                r
              )?.forEach((point) => {
                if (point.x > circle.x) {
                  pointsGather.push(point);
                  distance = this.calculateDistance(
                    (originPoints ? originPoints[0] : { x: 0, y: 0 }).x,
                    (originPoints ? originPoints[0] : { x: 0, y: 0 }).y,
                    point.x,
                    point.y
                  );
                }
              });
            }
          });
        }
      });
    }

    //  当左右两边的距离还能容纳一个圆时(分是否有gap,以及是否是默认自动均匀分布)
    if (
      (distance >= r * 2 && gap === 0) ||
      (gap !== 0 && (distance >= r * 2 + gap * 2 || this.surroundGap === null))
    ) {
      // 默认 gap 为 0 时,右侧开始补圆的起点
      let x2:number = rightCirclePoints.points[rightCirclePoints.points.length - 1].x;
      let y2:number = rightCirclePoints.points[rightCirclePoints.points.length - 1].y;
      const x1:number = virtualCircle.position.x;
      const y1:number = virtualCircle.position.y;
      const R:number = virtualCircle.radius;
      // 当 gap 不为 0 时,重新设置间隔后,右侧开始补圆的起点
      if (gap !== 0) {
        this.findCircleIntersection(x1, y1, R, x2, y2, gap)?.forEach((item) => {
          if (item.x < x2) {
            pointsGather.push(item);
            x2 = item.x;
            y2 = item.y;
          }
        });
      }
      this.findCircleIntersection(x1, y1, R, x2, y2, r)?.forEach((item) => {
        if (item.x < x2) {
          circlePoints.push(item);
          pointsGather.push(item);
          this.findCircleIntersection(x1, y1, R, item.x, item.y, r)?.forEach(
            (point) => {
              if (point.x < item.x) {
                pointsGather.push(point);
                distance = this.calculateDistance(
                  leftCirclePoints.points[leftCirclePoints.points.length - 1].x,
                  leftCirclePoints.points[leftCirclePoints.points.length - 1].y,
                  point.x,
                  point.y
                );
              }
            }
          );
        }
      });
    }

    return { circlePoints, surroundingCircleGap: distance, pointsGather };
  }

  /**
   * 计算两个圆的交点
   * @param {number} x1 - 第一个圆心的 x 坐标
   * @param {number} y1 - 第一个圆心的 y 坐标
   * @param {number} R - 第一个圆的半径
   * @param {number} x2 - 第二个圆心的 x 坐标
   * @param {number} y2 - 第二个圆心的 y 坐标
   * @param {number} r - 第二个圆的半径
   * @returns {Array|null} 返回交点坐标数组或 null(如果没有交点)
   */
  findCircleIntersection(
    x1: number,
    y1: number,
    R: number,
    x2: number,
    y2: number,
    r: number
  ): Array<{ x: number; y: number }> | null {
    const d:number = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2); // 圆心距离

    if (d > R + r || d < Math.abs(R - r)) {
      return null; // 两圆没有交点
    }

    const a: number = (R ** 2 - r ** 2 + d ** 2) / (2 * d);
    const h: number = Math.sqrt(R ** 2 - a ** 2);

    const px: number = x1 + (a * (x2 - x1)) / d;
    const py: number = y1 + (a * (y2 - y1)) / d;

    const offsetX: number = (h * (y2 - y1)) / d;
    const offsetY: number = (h * (x2 - x1)) / d;

    const intersection1: { x: number; y: number } = {
      x: px + offsetX,
      y: py - offsetY,
    };
    const intersection2: { x: number; y: number } = {
      x: px - offsetX,
      y: py + offsetY,
    };

    return [intersection1, intersection2];
  }

  /**
   * 计算两点之间的距离
   * @param {number} x1 - 第一个点的 x 坐标
   * @param {number} y1 - 第一个点的 y 坐标
   * @param {number} x2 - 第二个点的 x 坐标
   * @param {number} y2 - 第二个点的 y 坐标
   * @returns {number} 返回两点之间的距离
   */
  calculateDistance(x1: number, y1: number, x2: number, y2: number): number {
    return Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2);
  }

  /**
   * 判断两个点是否相同
   * @param {Object} point1 - 第一个点坐标对象 { x, y }
   * @param {Object} point2 - 第二个点坐标对象 { x, y }
   * @returns {boolean} 返回 true 如果两个点相同,否则返回 false
   */
  calculatePointsIsSame(
    point1: { x: number; y: number },
    point2: { x: number; y: number }
  ): boolean {
    return point1.x === point2.x && point1.y === point2.y;
  }

  /**
   * 计算从一个点开始,沿着虚拟圆的边界向下找到所有交点
   * @param {Object} points - 起始点坐标对象 { x, y }
   * @param {Object} virtualCircle - 虚拟圆对象 { position: { x, y }, radius }
   * @param {number} r - 原始圆的半径
   * @param {number} R - 虚拟圆的半径
   * @param {number} gap - 原始小圆之间的间隔
   * @param {string} direction - 虚拟圆的左侧或是右侧
   * @returns {Array} 返回所有找到的交点坐标数组
   */
  calculatePoints(
    points: { x: number; y: number },
    virtualCircle: CirclePointInter,
    r: number,
    R: number,
    gap: number | null = 0,
    direction: "right" | "left" = "right"
  ): PointsGatherInter {
    const newPoints = new Map();
    newPoints.set(0, points);
    let index: number = 0;

    while (
      newPoints.has(index) &&
      this.calculateDistance(
        newPoints.get(index).x,
        newPoints.get(index).y,
        virtualCircle.position.x,
        virtualCircle.position.y - R
      ) >= r
    ) {
      const x1: number = virtualCircle.position.x;
      const y1: number = virtualCircle.position.y;
      const x2: number = newPoints.get(index).x;
      const y2: number = newPoints.get(index).y;
      const R: number = virtualCircle.radius;
      const dblPoints: Array<{ x: number; y: number }> | null =
        this.findCircleIntersection(
          x1,
          y1,
          R,
          x2,
          y2,
          gap !== null && gap !== 0 && index % 3 === 0 ? gap : r
        );
      dblPoints?.forEach((item, i) => {
        if (
          item.y < y2 &&
          (direction === "right" ? item.x > x1 : item.x < x1)
        ) {
          newPoints.set(index + 1, item);
        }
      });

      index++;
    }

    let circleGather: Array<{ x: number; y: number }> = [];
    let allPoints: Array<{ x: number; y: number }> = [];
    for (let key of newPoints) {
      if (gap === 0) {
        if (key[0] % 2 !== 0) {
          circleGather.push(newPoints.get(key[0]));
        }
      } else {
        if ((key[0] + 1) % 3 === 0) {
          circleGather.push(newPoints.get(key[0]));
        }
      }

      allPoints.push(newPoints.get(key[0]));
    }
    // 处理最后一个点的情况,以防止多余的点被添加到结果中 --- 无间隔
    if (allPoints.length - 1 !== circleGather.length * 2 && gap === 0) {
      circleGather = circleGather.slice(0, circleGather.length - 1);
      allPoints = allPoints.slice(0, circleGather.length * 2 + 1);
    }
    // 处理最后一个点的情况,以防止多余的点被添加到结果中 --- 有间隔
    if (allPoints.length - 1 !== circleGather.length * 3 && gap !== 0) {
      // 处理当最后一个圆心点是最后一个点集合数据一样时的问题,此时需要删去最后一个圆点数据,并更新所有点集合数据
      if (
        this.calculatePointsIsSame(
          circleGather[circleGather.length - 1],
          allPoints[allPoints.length - 1]
        )
      ) {
        circleGather = circleGather.slice(0, circleGather.length - 1);
      }
      allPoints = allPoints.slice(0, circleGather.length * 3 + 1);
    }

    return { circlePoints: circleGather, points: allPoints };
  }
}

JS版本封装的SurroundingCircle

/**
 * 环绕圆类
 * @class surroundingCircle
 * @param {Object} options - 配置选项
 * @param {Object} options.centerCircle - 中心圆的配置
 * @param {Object} options.centerCircle.position - 中心圆的中心点坐标
 * @param {number} options.centerCircle.radius - 中心圆的半径
 * @param {number} options.surroundCircleRadius - 环绕圆的半径
 * @param {number} options.distance - 环绕圆与中心圆之间的距离
 * @param {string} [options.surroundManner="default"] - 环绕方式,"default" 或 "average"
 * @param {number|null} [options.surroundGap=null] - 环绕圆之间的间距,默认为 null
 * @description 计算环绕圆的点数据
 */
export class SurroundingCircle {
  constructor({
    centerCircle,
    surroundCircleRadius,
    distance,
    surroundManner = "default",
    surroundGap = null,
  }) {
    this.centerPointX = centerCircle.position.x;
    this.centerPointY = centerCircle.position.y;
    this.centerCircleRadius = centerCircle.radius;
    this.surroundCircleRadius = surroundCircleRadius;
    this.distance = distance;
    this.surroundManner = surroundManner;
    this.surroundGap = surroundGap;
    this.maxSurroundGap = 0;
  }
  
  /**
   * 更新环绕圆配置参数
   * @param {Object} options - 配置选项
   * @param {Object} options.centerCircle - 中心圆的配置
   * @param {Object} options.centerCircle.position - 中心圆的中心点坐标
   * @param {number} options.centerCircle.radius - 中心圆的半径
   * @param {number} options.surroundCircleRadius - 环绕圆的半径
   * @param {number} options.distance - 环绕圆与中心圆之间的距离
   * @param {string} [options.surroundManner="default"] - 环绕方式,"default" 或 "average"
   * @param {number|null} [options.surroundGap=null] - 环绕圆之间的间距,默认为 null
   */
  setSurroundingCircleOptions(options) {
    this.centerPointX = options.centerCircle.position.x;
    this.centerPointY = options.centerCircle.position.y;
    this.centerCircleRadius = options.centerCircle.radius;
    this.surroundCircleRadius = options.surroundCircleRadius;
    this.distance = options.distance;
    this.surroundManner = options.surroundManner ?? "default";
    this.surroundGap = options.surroundGap ?? null;
    this.maxSurroundGap = 0;
  }

  /**
   * 获取环绕圆的数据
   * @returns {Object} 返回包含环绕圆点和距离的对象
   */
  getCircleData() {
    if (this.surroundManner === "default") {
      return this.getCloseCircleData();
    } else if (this.surroundManner === "average") {
      return this.getAverageCircleData();
    } else {
      throw new Error("Invalid surround manner specified.");
    }
  }

  /**
   * 获取环绕圆的点数据--紧凑型
   * @returns {Object} 返回包含环绕圆点和距离的对象
   */
  getCloseCircleData() {
    return this.handleCalculatePoints(
      this.centerPointX,
      this.centerPointY,
      this.centerCircleRadius,
      this.surroundCircleRadius,
      this.distance
    );
  }

  /**
   * 获取平均环绕圆的数据-平均型
   * @returns {Object} 返回包含平均环绕圆点和距离的对象
   */
  getAverageCircleData() {
    const circlePointGather = this.getCloseCircleData();
    const averageGap =
      this.surroundGap ??
      circlePointGather.surroundingCircleGap /
        circlePointGather.circlePoints.length;

    return this.handleCalculatePoints(
      this.centerPointX,
      this.centerPointY,
      this.centerCircleRadius,
      this.surroundCircleRadius,
      this.distance,
      averageGap
    );
  }

  /**
   * 获取最大间隔值
   * @returns {number} 返回可设置间隔的最大值
   */
  getMaxSurroundGap() {
    return this.maxSurroundGap;
  }

  /**
   * 设置最大间距
   * @param {object} virtualCircle - 虚拟圆对象 { position: { x, y }, radius }
   * @param {object} originPoints - 原始圆对象 { position: { x, y }, radius }
   * @param {number} x - 虚拟圆心的 x 坐标
   * @param {number} y - 虚拟圆心的 y 坐标
   * @param {number} R - 虚拟圆的半径
   * @param {number} d - 偏移量
   * @param {number} r - 原始圆的半径
   */
  setMaxSurroundGap(virtualCircle, originPoints, x, y, R, d, r) {
    // 对称于originCircle的点,用于计算最大gap,即this.maxSurroundGap
    const symmetryOriginCircle = {
      position: { x: x, y: y - R - d - r },
      radius: r,
    };
    // 开始计算最大gap,即this.maxSurroundGap
    this.findCircleIntersection(
      virtualCircle.position.x,
      virtualCircle.position.y,
      virtualCircle.radius,
      symmetryOriginCircle.position.x,
      symmetryOriginCircle.position.y,
      symmetryOriginCircle.radius
    )?.forEach((item) => {
      if (item.x === (originPoints ? originPoints[0] : { x: 0, y: 0 }).x) {
        this.maxSurroundGap =
          (originPoints ? originPoints[0] : { x: 0, y: 0 }).y - item.y;
      }
    });
  }

  /**
   * 处理计算点的逻辑 --- 主要核心函数
   * @param {number} x - 虚拟圆心的 x 坐标
   * @param {number} y - 虚拟圆心的 y 坐标
   * @param {number} R - 虚拟圆的半径
   * @param {number} r - 原始圆的半径
   * @param {number} d - 偏移量
   * @param {number} gap - 原始小圆之间的间隔
   * @returns {Object} 返回包含交点和距离的对象
   */
  handleCalculatePoints(x, y, R, r, d, gap = 0) {
    const virtualCircle = {
      position: { x, y },
      radius: R + d + r,
    };
    const originCircle = {
      position: { x: x, y: y + R + d + r },
      radius: r,
    };
    const originPoints = this.findCircleIntersection(
      virtualCircle.position.x,
      virtualCircle.position.y,
      virtualCircle.radius,
      originCircle.position.x,
      originCircle.position.y,
      originCircle.radius
    );

    // 如果设置了平均间隔且配置了间隔值,则需要执行以下逻辑
    if (this.surroundManner !== "default" && this.surroundGap) {
      // 计算出最大可配置间隔值
      this.setMaxSurroundGap(virtualCircle, originPoints, x, y, R, d, r);
      // 如果设置间隔平均分配且间隔超出最大间隔时,仅返回originCircle圆数据
      if (this.surroundManner !== "default" && gap >= this.maxSurroundGap) {
        return {
          circlePoints: [originCircle.position],
          surroundingCircleGap: gap,
          pointsGather: [originCircle.position, ...(originPoints ?? [])],
        };
      }
    }

    // 开始进行过滤后的正常计算
    const rightCirclePoints = this.calculatePoints(
      originPoints ? originPoints[0] : { x: 0, y: 0 },
      virtualCircle,
      originCircle.radius,
      R,
      gap,
      "right"
    );
    const leftCirclePoints = this.calculatePoints(
      originPoints ? originPoints[1] : { x: 0, y: 0 },
      virtualCircle,
      originCircle.radius,
      R,
      gap,
      "left"
    );
    // 计算:如果左右两部分最后一个点集合之间的距离小于gap时,去除掉其中一边的圆数据即对应的点集合数据
    if (
      rightCirclePoints.circlePoints.length !== 0 &&
      leftCirclePoints.circlePoints.length !== 0
    ) {
      const leftDistanceRight = this.calculateDistance(
        rightCirclePoints.points[rightCirclePoints.points.length - 1].x,
        rightCirclePoints.points[rightCirclePoints.points.length - 1].y,
        leftCirclePoints.points[leftCirclePoints.points.length - 1].x,
        leftCirclePoints.points[leftCirclePoints.points.length - 1].y
      );
      if (leftDistanceRight < gap && this.surroundGap) {
        rightCirclePoints.circlePoints.pop();
        rightCirclePoints.points.slice(0, rightCirclePoints.points.length - 3);
      }
    }
    // 初步整理圆心点集合、点集合、默认紧凑时的间距
    const circlePoints = [
      originCircle.position,
      ...rightCirclePoints.circlePoints,
      ...leftCirclePoints.circlePoints,
    ];
    const pointsGather = [
      ...rightCirclePoints.points,
      ...leftCirclePoints.points,
    ];
    let distance = this.calculateDistance(
      leftCirclePoints.points[leftCirclePoints.points.length - 1].x,
      leftCirclePoints.points[leftCirclePoints.points.length - 1].y,
      rightCirclePoints.points[rightCirclePoints.points.length - 1].x,
      rightCirclePoints.points[rightCirclePoints.points.length - 1].y
    );

    // 如果左右两部分都没有适合间隔的圆数据,则将originCircle右侧点开始,计算gap距离符合需求的圆数据
    if (
      rightCirclePoints.circlePoints.length === 0 &&
      leftCirclePoints.circlePoints.length === 0 &&
      gap <= R * 2 + d * 2
    ) {
      this.findCircleIntersection(
        virtualCircle.position.x,
        virtualCircle.position.y,
        virtualCircle.radius,
        (originPoints ? originPoints[1] : { x: 0, y: 0 }).x,
        (originPoints ? originPoints[1] : { x: 0, y: 0 }).y,
        gap
      )?.forEach((item) => {
        if (item.x < virtualCircle.position.x) {
          pointsGather.push(item);
          this.findCircleIntersection(
            virtualCircle.position.x,
            virtualCircle.position.y,
            virtualCircle.radius,
            item.x,
            item.y,
            r
          )?.forEach((circle) => {
            if (circle.x > item.x) {
              circlePoints.push(circle);
              this.findCircleIntersection(
                virtualCircle.position.x,
                virtualCircle.position.y,
                virtualCircle.radius,
                circle.x,
                circle.y,
                r
              )?.forEach((point) => {
                if (point.x > circle.x) {
                  pointsGather.push(point);
                  distance = this.calculateDistance(
                    (originPoints ? originPoints[0] : { x: 0, y: 0 }).x,
                    (originPoints ? originPoints[0] : { x: 0, y: 0 }).y,
                    point.x,
                    point.y
                  );
                }
              });
            }
          });
        }
      });
    }

    //  当左右两边的距离还能容纳一个圆时(分是否有gap,以及是否是默认自动均匀分布)
    if (
      (distance >= r * 2 && gap === 0) ||
      (gap !== 0 && (distance >= r * 2 + gap * 2 || this.surroundGap === null))
    ) {
      // 默认 gap 为 0 时,右侧开始补圆的起点
      let x2 = rightCirclePoints.points[rightCirclePoints.points.length - 1].x;
      let y2 = rightCirclePoints.points[rightCirclePoints.points.length - 1].y;
      const x1 = virtualCircle.position.x;
      const y1 = virtualCircle.position.y;
      const R = virtualCircle.radius;
      // 当 gap 不为 0 时,重新设置间隔后,右侧开始补圆的起点
      if (gap !== 0) {
        this.findCircleIntersection(x1, y1, R, x2, y2, gap)?.forEach((item) => {
          if (item.x < x2) {
            pointsGather.push(item);
            x2 = item.x;
            y2 = item.y;
          }
        });
      }
      this.findCircleIntersection(x1, y1, R, x2, y2, r)?.forEach((item) => {
        if (item.x < x2) {
          circlePoints.push(item);
          pointsGather.push(item);
          this.findCircleIntersection(x1, y1, R, item.x, item.y, r)?.forEach(
            (point) => {
              if (point.x < item.x) {
                pointsGather.push(point);
                distance = this.calculateDistance(
                  leftCirclePoints.points[leftCirclePoints.points.length - 1].x,
                  leftCirclePoints.points[leftCirclePoints.points.length - 1].y,
                  point.x,
                  point.y
                );
              }
            }
          );
        }
      });
    }

    return { circlePoints, surroundingCircleGap: distance, pointsGather };
  }

  /**
   * 计算两个圆的交点
   * @param {number} x1 - 第一个圆心的 x 坐标
   * @param {number} y1 - 第一个圆心的 y 坐标
   * @param {number} R - 第一个圆的半径
   * @param {number} x2 - 第二个圆心的 x 坐标
   * @param {number} y2 - 第二个圆心的 y 坐标
   * @param {number} r - 第二个圆的半径
   * @returns {Array|null} 返回交点坐标数组或 null(如果没有交点)
   */
  findCircleIntersection(x1, y1, R, x2, y2, r) {
    const d = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2); // 圆心距离

    if (d > R + r || d < Math.abs(R - r)) {
      return null; // 两圆没有交点
    }

    const a = (R ** 2 - r ** 2 + d ** 2) / (2 * d);
    const h = Math.sqrt(R ** 2 - a ** 2);

    const px = x1 + (a * (x2 - x1)) / d;
    const py = y1 + (a * (y2 - y1)) / d;

    const offsetX = (h * (y2 - y1)) / d;
    const offsetY = (h * (x2 - x1)) / d;

    const intersection1 = {
      x: px + offsetX,
      y: py - offsetY,
    };
    const intersection2 = {
      x: px - offsetX,
      y: py + offsetY,
    };

    return [intersection1, intersection2];
  }

  /**
   * 计算两点之间的距离
   * @param {number} x1 - 第一个点的 x 坐标
   * @param {number} y1 - 第一个点的 y 坐标
   * @param {number} x2 - 第二个点的 x 坐标
   * @param {number} y2 - 第二个点的 y 坐标
   * @returns {number} 返回两点之间的距离
   */
  calculateDistance(x1, y1, x2, y2) {
    return Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2);
  }

  /**
   * 判断两个点是否相同
   * @param {Object} point1 - 第一个点坐标对象 { x, y }
   * @param {Object} point2 - 第二个点坐标对象 { x, y }
   * @returns {boolean} 返回 true 如果两个点相同,否则返回 false
   */
  calculatePointsIsSame(point1, point2) {
    return point1.x === point2.x && point1.y === point2.y;
  }

  /**
   * 计算从一个点开始,沿着虚拟圆的边界向下找到所有交点
   * @param {Object} points - 起始点坐标对象 { x, y }
   * @param {Object} virtualCircle - 虚拟圆对象 { position: { x, y }, radius }
   * @param {number} r - 原始圆的半径
   * @param {number} R - 虚拟圆的半径
   * @param {number} gap - 原始小圆之间的间隔
   * @param {string} direction - 虚拟圆的左侧或是右侧
   * @returns {Array} 返回所有找到的交点坐标数组
   */
  calculatePoints(points, virtualCircle, r, R, gap = 0, direction = "right") {
    const newPoints = new Map();
    newPoints.set(0, points);
    let index = 0;

    while (
      newPoints.has(index) &&
      this.calculateDistance(
        newPoints.get(index).x,
        newPoints.get(index).y,
        virtualCircle.position.x,
        virtualCircle.position.y - R
      ) >= r
    ) {
      const x1 = virtualCircle.position.x;
      const y1 = virtualCircle.position.y;
      const x2 = newPoints.get(index).x;
      const y2 = newPoints.get(index).y;
      const R = virtualCircle.radius;
      const dblPoints = this.findCircleIntersection(
        x1,
        y1,
        R,
        x2,
        y2,
        gap !== null && gap !== 0 && index % 3 === 0 ? gap : r
      );
      dblPoints?.forEach((item, i) => {
        if (
          item.y < y2 &&
          (direction === "right" ? item.x > x1 : item.x < x1)
        ) {
          newPoints.set(index + 1, item);
        }
      });

      index++;
    }

    let circleGather = [];
    let allPoints = [];
    for (let key of newPoints) {
      if (gap === 0) {
        if (key[0] % 2 !== 0) {
          circleGather.push(newPoints.get(key[0]));
        }
      } else {
        if ((key[0] + 1) % 3 === 0) {
          circleGather.push(newPoints.get(key[0]));
        }
      }

      allPoints.push(newPoints.get(key[0]));
    }
    // 处理最后一个点的情况,以防止多余的点被添加到结果中 --- 无间隔
    if (allPoints.length - 1 !== circleGather.length * 2 && gap === 0) {
      circleGather = circleGather.slice(0, circleGather.length - 1);
      allPoints = allPoints.slice(0, circleGather.length * 2 + 1);
    }
    // 处理最后一个点的情况,以防止多余的点被添加到结果中 --- 有间隔
    if (allPoints.length - 1 !== circleGather.length * 3 && gap !== 0) {
      // 处理当最后一个圆心点是最后一个点集合数据一样时的问题,此时需要删去最后一个圆点数据,并更新所有点集合数据
      if (
        this.calculatePointsIsSame(
          circleGather[circleGather.length - 1],
          allPoints[allPoints.length - 1]
        )
      ) {
        circleGather = circleGather.slice(0, circleGather.length - 1);
      }
      allPoints = allPoints.slice(0, circleGather.length * 3 + 1);
    }

    return { circlePoints: circleGather, points: allPoints };
  }
}