过滤多边形中冗余的点

68 阅读3分钟

过滤多边形中冗余的点

大家好,我是前端丁修。

在图形学和前端开发中,处理多边形时,顶点通常以循环的方式连接,即第一个点与最后一个点相连。有时需要优化多边形的点,移除路径中不必要的点以简化多边形结构。本节将介绍如何使用 TypeScript 实现一个过滤多边形中冗余点的工具。

功能列表

  1. 初始化多边形
    定义多边形的点集。

  2. 检测和移除冗余点
    判断哪些点是多余的,并将其移除。

  3. 优化后的多边形输出
    输出经过优化后的点集。

实现过滤冗余点的方法

我们将采用共线检测距离阈值的方法来判断点是否冗余。通过检查连续三个点是否共线,可以有效识别并删除那些位于直线上的中间点,从而优化多边形的顶点数,提升计算效率。

共线检测

如果三个连续的点在同一直线上,中间的点就是冗余的,可以被移除。

距离阈值

在某些情况下,点之间的距离过小也可以被认为是冗余的,根据设定的阈值来移除这些点。

代码实现

下面是过滤多边形中冗余点的 TypeScript 实现。

// 定义点的接口
interface Point {
  x: number;
  y: number;
}

// 多边形的类
class Polygon {
  points: Point[];

  constructor(points: Point[]) {
    this.points = points;
  }

  // 检查三个点是否共线
  private isCollinear(p1: Point, p2: Point, p3: Point): boolean {
    const area =
      p1.x * (p2.y - p3.y) + p2.x * (p3.y - p1.y) + p3.x * (p1.y - p2.y);
    return Math.abs(area) < 1e-6; // 容差
  }

  // 计算两点之间的距离
  private distance(p1: Point, p2: Point): number {
    return Math.sqrt((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2);
  }

  // 移除近距离的点
  private removeClosePoints(distanceThreshold: number): void {
    if (this.points.length <= 3) return;

    // 先检查首尾点
    if (
      this.distance(this.points[0], this.points[this.points.length - 1]) <=
      distanceThreshold
    ) {
      this.points.pop();
    }

    this.points = this.points.filter((point, index, arr) => {
      if (index === 0) return true;
      return this.distance(point, arr[index - 1]) > distanceThreshold;
    });
  }

  // 移除共线的点
  private removeCollinearPoints(): void {
    if (this.points.length <= 3) return;
    const filteredPoints: Point[] = [];
    const n = this.points.length;
    for (let i = 0; i < n; i++) {
      // 保证 i 为 0 时,i - 1 不会变为负数
      const prev = this.points[(i - 1 + n) % n];
      const current = this.points[i];
      // 取模运算,用于确保索引始终在 0 到 n-1 之间,避免数组越界
      const next = this.points[(i + 1) % n];

      if (
        this.isCollinear(prev, current, next) &&
        // 检查当前点之后是否至少还有两个点。
        // 如果 i + 2 大于或等于 n,意味着已经到达数组的末尾,此时不需要进一步检查共线性,直接保留当前点。
        (i + 2 >= n ||
          !this.isCollinear(current, next, this.points[(i + 2) % n]))
      ) {
        continue;
      }
      filteredPoints.push(current);
    }
    this.points = filteredPoints;
  }

  // 过滤冗余点
  private filterRedundantPoints(distanceThreshold: number = 0.01): void {
    if (this.points.length < 3) return;
    this.removeClosePoints(distanceThreshold);
    this.removeCollinearPoints();
  }

  // 输出多边形的点
  getPoints(): Point[] {
    return this.points;
  }
}

使用方法

创建一个 Polygon 实例,传入点集后调用 filterRedundantPoints 方法即可过滤冗余点。通过调整 distanceThreshold 参数,可以控制距离过滤的敏感度。

const polygon = new Polygon([
  { x: 100, y: 100 },
  { x: 200, y: 100 },
  { x: 200.02, y: 100 }, // 距离过近的点
  { x: 300, y: 100 }, // 共线点
  { x: 400, y: 100 },
  { x: 400, y: 200 },
  { x: 400.01, y: 200.01 }, // 距离过近的点
  { x: 300, y: 300 },
  { x: 200, y: 300 },
  { x: 100, y: 300 },
  { x: 100, y: 200 }, // 回到起点
]);

polygon.filterRedundantPoints(0.05);
console.log("优化后点集:", polygon.getPoints());

移除多边形冗余点 说明:

蓝色点:保留的点 红色点:因距离过近被移除的点 橙色点:因共线被移除的点 实线:过滤后的多边形 输出:


优化后点集: [
  {
    "x": 100,
    "y": 100
  },
  {
    "x": 200,
    "y": 100
  },
  {
    "x": 400,
    "y": 100
  },
  {
    "x": 400,
    "y": 200
  },
  {
    "x": 300,
    "y": 300
  },
  {
    "x": 100,
    "y": 300
  }
]