/Polygon

任意多边形填充、裁剪、平移、旋转、缩放、翻转算法的实现。

Primary LanguageTypeScript

Polygon

任意多边形填充、裁剪、平移、旋转、缩放、翻转算法的实现。

Demo 页面

运行

# prepare
npm install -g yarn
yarn install

# develop
npm start

# build
npm run build

使用方式

左键添加点,右键闭合当前多边形。

进入界面后首先绘制外环,左键逆时针添加若干个外环点,右键闭合外环。

然后左键顺时针添加若干内环点,右键闭合内环(可以直接闭合,表示无内环)。

然后可以对多边形做各种操作:

  • 平移:按 M,然后依次输入平移的 x、y 值
  • 旋转:按 R,然后输入旋转的角度(°)
  • 缩放:按 S,然后输入缩放系数
  • 翻转:按 F,然后输入 x 或 y,代表翻转方向
  • 填充:多边形完成后自动填充黑色,按 I 并依次输入 r、g、b (0-255)
  • 裁剪:按 C,然后按照第一次输入多边形的方式输入裁剪多边形(暂时不支持内环,所以不需要内环的步骤),完成后自动裁剪。

实现

框架

算法使用 TypeScript 实现,使用 HTML5 Canvas API 在网页上作画,仅使用了 fillRect(x, y, 1, 1) 来模拟 setPixel(x, y)。在具体实现上,每个多边形仅记录了外环、内环的顶点和连接顺序,像素图是每次根据顶点计算而得到的。像素画布的尺寸就是真实的浏览器尺寸。

填充

填充使用了边界标志填充法,实现了任意多边形(凹、凸、带内环)的填充,处理了所有边界情况,具体实现上,使用了一个标志矩阵来记录每个像素的边界情况,如果标志位是奇数,那么就需要翻转 inside,反之则不用。对于多边形的顶点,根据它跟相邻两个节点的关系,如果是一上一下,那么顶点像素位的标志加一,如果同上或者同下,标志位不变。对于边上的点,如果线段平行于 x 轴, 标志位不变,否则标志位加一。测试对于复杂的多边形,都能正确填充。

填充 demo:

填充 Demo

裁剪

裁剪使用 Weiler-Atherton 算法实现,并且利用计算几何边追赶的**,对 WA 算法做了一些改动,省略了预处理的过程。

由于算法的复杂性,实现中省略了内环的情况,仅能任意凹凸多边形被任意凹凸多边形裁剪,且对于一些边边、边点、点点重合的情况,以及可能切割出多个多边形的情况没有处理,仅保证实现了的部分的正确性。

算法的思路如下:

  1. 首先找到任何一个交点,如果找不到,判断两个多边形的包含关系直接得到结果,算法结束,否则记第一个节点为 P1。
  2. 使用两个指针分别代表两个多边形上的点,初始化全部指向 P1。定义点数组 intersections 代表结果,初始化为 [P1]。初始化 P 为 P1。
  3. 从 P 开始使用叉积判断当前两条边的相对关系。
  4. 靠外的边所属的多边形上的指针向前行走,边走边判断与另一个多边形的交点,直到遇到下一个交点,停止,记为 P2。
  5. 另一个多边形上的指针「追赶」上一个多边形的指针,将沿途经过的顶点加入 intersections,直到走到 P2。
  6. intersections 压入 P2,更新 P 为 P2。
  7. 如果 P 和 P1 相等,算法结束,返回 intersections,否则转 3。

简化后的算法和 WA 算法很相似,创新点:

  1. 不需要事先预处理得到所有交点并标记出入性。随着双指针行进的过程逐渐计算交点。
  2. 不需要区分出入点,从任何交点开始算法都是正确的。

裁剪 demo

裁剪 demo

平移、旋转、缩放、翻转

因为只记录了每个多边形的顶点,可以简单地让每个顶点乘相应的变换矩阵完成变换,为了更好的界面效果,旋转、缩放操作是相对于重心的,翻转操作以重心所在的 x 轴或 y 轴为对称轴。