扫描线填充-如何确保覆盖整个多边形?

时间:2019-06-17 14:37:21

标签: javascript algorithm graphics

我正在尝试有效地将GeoJSON对象“划分”为任何大小的对齐正方形图块,例如:

  1. 整个多边形或多面体都被覆盖了(多边形中没有没有瓷砖覆盖的区域)。
  2. 没有没有覆盖多边形任何区域的图块。

example

我曾尝试使用Turfjs的方格网(https://turfjs.org/docs/#squareGrid)之类的库,但是遮罩操作速度过慢,对于大面积的低正方形区域而言,这需要几分钟。它也不会覆盖多边形的整个区域-而是内部。

我正在尝试使用 scanline fill 算法来执行此操作,因为我的研究表明,它的运行速度非常快,并且可以解决此问题,但是它并不总是覆盖整个区域-它仅覆盖内部,有时会遗留角落:

missing corners

或将整个水平区域都排除在外:

missing horizontal area

我的扫描线的工作方式类似于普通的扫描线填充,但是基于浮点数而不是整数。它对初始x位置进行铺底,使其位于网格线上(Math.floor((x -polygonLeftBoundary)/ cellWidth)* cellWidth)

这是我的实现(使用TypeScript):

public partitionIntoGrid(polygon, cellSizeKm) {
    const bounds = this.getBoundingBox(polygon);
    const left = bounds[0];
    const cellDistance = this.distanceToDegrees(cellSizeKm);

    const grid = [];

    if (polygon.geometry.type === 'Polygon') {
      polygon = [polygon.geometry.coordinates];
    } else {
      polygon = polygon.geometry.coordinates;
    }

    polygon.forEach(poly => {
      poly = poly[0];

      const edges = poly.reduce((acc, vertex, vertexIndex) => {
        acc.push([vertex, poly[vertexIndex === poly.length - 1 ? 0 : vertexIndex + 1]]);
        return acc;
      }, []);

      let edgeTable = edges
        .map(edge => {
          const x = Math.floor((edge[0][1] < edge[1][1] ? edge[0][0] : edge[1][0]) / cellDistance) * cellDistance;
          return {
            yMin: Math.min(edge[0][1], edge[1][1]), // minumum y coordinate
            yMax: Math.max(edge[0][1], edge[1][1]), // maximum y coordinate
            originalX: x,
            x, // lower coordinate's x
            w: (edge[0][0] - edge[1][0]) / (edge[0][1] - edge[1][1]), // change of edge per y
          };
        })
        .filter(edge => !isNaN(edge.w))
        .sort((a, b) => a.yMax - b.yMax);

      let activeEdgeTable = [];

      let y = edgeTable[0].yMin;
      const originalY = y;

      while (edgeTable.length || activeEdgeTable.length) {
        // move edges from edge table under y into the active edge table
        edgeTable = edgeTable.filter(edge => {
          if (edge.yMin <= y) {
            activeEdgeTable.push(edge);
            return false;
          }
          return true;
        });

        // remove edges from the active edge table whose yMax is smaller than y
        activeEdgeTable = activeEdgeTable.filter(edge => edge.yMax >= y);
        // sort active edge table by x
        activeEdgeTable = activeEdgeTable.sort((a, b) => a.x - b.x);

        // fill pixels between even and odd adjacent pairs of intersections in active edge table
        const pairs = Array.from({ length: activeEdgeTable.length / 2 }, (v, k) => [
          activeEdgeTable[k * 2], activeEdgeTable[k * 2 + 1],
        ]);
        pairs.forEach(pair => {
          for (let x = pair[0].x; x <= pair[1].x; x += cellDistance) {
            grid.push(bboxPolygon([
              x, // minX
              y, // minY
              x + cellDistance, // maxX
              y + cellDistance, // maxY
            ]));
          }
        });

        // increment y
        y += cellDistance;

        // update x for all edges in active edge table
        activeEdgeTable.forEach(edge => edge.x += edge.w * cellDistance);
        // activeEdgeTable.forEach(edge => edge.x = edge.originalX + Math.floor((edge.w * (y - originalY) / cellDistance)) * cellDistance);
      }
    });

    return grid;
  }

我一直在尝试增加渐变效果,但是还没到那儿,因此注释了这一行:

activeEdgeTable.forEach(edge => edge.x = edge.originalX + Math.floor((edge.w * (y - originalY) / cellDistance)) * cellDistance);

0 个答案:

没有答案