通过矢量扩展2D多边形

克莱托佩多

我有凸多边形,我想通过沿着矢量投影来扩展它们,如下所示:

在此处输入图片说明

(原始多边形和矢量在左侧,期望的结果在右侧。)

我的多边形以逆时针缠绕的方式存储为一系列点。我想找到的是我需要从中投影的“开始”和“停止”点,如下面带圆圈的顶点所示。在此处输入图片说明

(绿色箭头表示多边形的缠绕,给出了每个边的“方向”。)

我最初的计划是通过从每个点投射具有矢量方向的射线来确定要使用的点,并找到射线不与边缘相交的第一个和最后一个点。但是,这似乎很昂贵。

Is there a way I can use the edge directions vs the vector direction, or a similar trick, to determine which points to extend from?

Claytorpedo

n.m. answered the question as I asked and pictured it, but upon programming I soon noticed that there was a common case where all vertices would be "outside" vertices (this can be easily seen on triangles, and can occur for other polygons too).

The text explanation.

The solution I used was to look at the normal vectors of the edges leading into and exiting each vertex. The vertices we want to extend are vertices that have at least one edge normal with a minimum angle of less than 90 degrees to the delta vector we are extending by.

The outward-facing edge normals on a counterclockwise-wound polygon can be found by:

normal = (currentVertex.y - nextVertex.y, nextVertex.x - currentVertex.x)

Note that since we don't care about the exact angle, we don't need to normalize (make a unit vector of) the normal, which saves a square root.

To compare it to the delta vector, we use the dot product:

dot = edgeNormal.dot(deltaVector)

If the result is greater than zero, the minimum angle is acute (less than 90). If the result is exactly zero, the vectors are perpendicular. If the result is less than zero, the minimum angle is obtuse. It is worth noting when the vectors are perpendicular, since it lets us avoid adding extra vertices to the extended polygon.

If you want to visualize how the angle works with the dot product, like I did, just look at a graph of arc cosine (normally you get the angle via acos(dot)).

Now we can find the vertices that have one acute and one not-acute minimum angle between its edge normals and the delta vector. Everything on the "acute side" of these vertices has the delta vector added to it, and everything on the "obtuse side" stays the same. The two boarder vertices themselves are duplicated, having one extended and one staying the same, unless the "obtuse side" is exactly perpendicular to the delta vector (in this case we only need to extend the vertex, since otherwise we would have two vertices on the same line).

Here is the C++ code for this solution.

It may look a little long, but it is actually quite straightforward and has many comments so it hopefully shouldn't be hard to follow.

它是我的Polygon类的一部分,该类具有一个std :: vector逆时针缠绕的顶点。unit :: Coordinate是浮点数,units :: Coordinate2D是一个矢量类,我认为应该是不言自明的。

// Compute the normal of an edge of a polygon with counterclockwise winding, without normalizing it to a unit vector.
inline units::Coordinate2D _get_non_normalized_normal(units::Coordinate2D first, units::Coordinate2D second) {
    return units::Coordinate2D(first.y - second.y, second.x - first.x);
}
enum AngleResult {
    ACUTE,
    PERPENDICULAR,
    OBTUSE
};
// Avoid accumulative floating point errors.
// Choosing a good epsilon is extra important, since we don't normalize our vectors (so it is scale dependent).
const units::Coordinate eps = 0.001;
// Check what kind of angle the minimum angle between two vectors is.
inline AngleResult _check_min_angle(units::Coordinate2D vec1, units::Coordinate2D vec2) {
    const units::Coordinate dot = vec1.dot(vec2);
    if (std::abs(dot) <= eps)
        return PERPENDICULAR;
    if ((dot + eps) > 0)
        return ACUTE;
    return OBTUSE;
}
Polygon Polygon::extend(units::Coordinate2D delta) const {
    if (delta.isZero()) { // Isn't being moved. Just return the current polygon.
        return Polygon(*this);
    }
    const std::size_t numVerts = vertices_.size();
    if (numVerts < 3) {
        std::cerr << "Error: Cannot extend polygon (polygon invalid; must have at least three vertices).\n";
        return Polygon();
    }
    // We are interested in extending from vertices that have at least one edge normal with a minimum angle acute to the delta.
    // With a convex polygon, there will form a single contiguous range of such vertices.
    // The first and last vertex in that range may need to be duplicated, and then the vertices within the range
    // are projected along the delta to form the new polygon.
    // The first and last vertices are defined by the vertices that have only one acute edge normal.

    // Whether the minimum angle of the normal of the edge made from the last and first vertices is acute with delta.
    const AngleResult firstEdge = _check_min_angle(_get_non_normalized_normal(vertices_[numVerts-1], vertices_[0]), delta);
    const bool isFirstEdgeAcute = firstEdge == ACUTE;

    AngleResult prevEdge = firstEdge;
    AngleResult currEdge;
    bool found = false;
    std::size_t vertexInRegion;
    for (std::size_t i = 0; i < numVerts - 1; ++i) {
        currEdge = _check_min_angle(_get_non_normalized_normal(vertices_[i], vertices_[i+1]), delta);
        if (isFirstEdgeAcute != (currEdge == ACUTE)) {
            // Either crossed from inside to outside the region, or vice versa.
            // (One side of the vertex has an edge normal that is acute, the other side obtuse.)
            found = true;
            vertexInRegion = i;
            break;
        }
        prevEdge = currEdge;
    }
    if (!found) {
        // A valid polygon has two points that define where the region starts and ends.
        // If we didn't find one in the loop, the polygon is invalid.
        std::cerr << "Error: Polygon can not be extended (invalid polygon).\n";
        return Polygon();
    }
    found = false;
    std::size_t first, last;
    // If an edge being extended is perpendicular to the delta, there is no need to duplicate that vertex.
    bool shouldDuplicateFirst, shouldDuplicateLast;
    // We found either the first or last vertex for the region.
    if (isFirstEdgeAcute) {
        // It is the last vertex in the region.
        last = vertexInRegion;
        shouldDuplicateLast = currEdge != PERPENDICULAR; // currEdge is either perpendicular or obtuse.
        // Loop backwards from the end to find the first vertex.
        for (std::size_t i = numVerts - 1; i > 0; --i) {
            currEdge = _check_min_angle(_get_non_normalized_normal(vertices_[i-1], vertices_[i]), delta);
            if (currEdge != ACUTE) {
                first = i;
                shouldDuplicateFirst = currEdge != PERPENDICULAR;
                found = true;
                break;
            }
        }
        if (!found) {
            std::cerr << "Error: Polygon can not be extended (invalid polygon).\n";
            return Polygon();
        }
    } else {
        // It is the first vertex in the region.
        first = vertexInRegion;
        shouldDuplicateFirst = prevEdge != PERPENDICULAR; // prevEdge is either perpendicular or obtuse.
        // Loop forwards from the first vertex to find where it ends.
        for (std::size_t i = vertexInRegion + 1; i < numVerts - 1; ++i) {
            currEdge = _check_min_angle(_get_non_normalized_normal(vertices_[i], vertices_[i+1]), delta);
            if (currEdge != ACUTE) {
                last = i;
                shouldDuplicateLast = currEdge != PERPENDICULAR;
                found = true;
                break;
            }
        }
        if (!found) {
            // The edge normal between the last and first vertex is the only non-acute edge normal.
            last = numVerts - 1;
            shouldDuplicateLast = firstEdge != PERPENDICULAR;
        }
    }

    // Create the new polygon.
    std::vector<units::Coordinate2D> newVertices;
    newVertices.reserve(numVerts + (shouldDuplicateFirst ? 1 : 0) + (shouldDuplicateLast ? 1 : 0) );
    for (std::size_t i = 0; i < numVerts; ++i) {
        // Extend vertices in the region first-to-last inclusive. Duplicate first/last vertices if required.
        if (i == first && shouldDuplicateFirst) {
            newVertices.push_back(vertices_[i]);
            newVertices.push_back(vertices_[i] + delta);
        } else if (i == last && shouldDuplicateLast) {
            newVertices.push_back(vertices_[i] + delta);
            newVertices.push_back(vertices_[i]);
        } else {
            newVertices.push_back( isFirstEdgeAcute ? // Determine which range to use.
                ( (i <= last || i >= first) ? vertices_[i] + delta : vertices_[i] ) : // Range overlaps start/end of the array.
                ( (i <= last && i >= first) ? vertices_[i] + delta : vertices_[i] )); // Range is somewhere in the middle of the array.
        }
    }
    return Polygon(newVertices);
}

到目前为止,我已经用三角形,矩形,近似圆和任意凸多边形测试了此代码,这些三角形是通过将近似圆依次扩展许多不同的增量矢量而制成的。

请注意,该解决方案仍然仅对凸多边形有效。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

通过Sharpdx API用4个顶点(2D位置)绘制并填充多边形

来自分类Dev

解决2D游戏碰撞(多边形)

来自分类Dev

如何计算任何2D多边形的重力?

来自分类Dev

在openGL中绘制2D多边形

来自分类Dev

Unity 2D 多边形碰撞器

来自分类Dev

最佳实践,以检测点是否在2D多边形内(多边形的顶点在表上)

来自分类Dev

使用D3.js SVG的2D多边形布尔运算

来自分类Dev

Boost :: Geometry-在3D空间中查找2D多边形的区域?

来自分类Dev

从3D对象获取2D凹面多边形

来自分类Dev

2D多边形顶点法线朝向内/外?

来自分类Dev

如何对多个高速多边形碰撞(2D)进行插值?

来自分类Dev

如何比N时间更快地确定点是否在2D凸多边形内

来自分类Dev

旋转2D多边形而不更改其位置

来自分类Dev

使用2D多边形而不是航点的AI寻路-是否有推荐的算法?

来自分类Dev

如何选择指向任何凸多边形内部的法线向量(2d)?

来自分类Dev

从多边形网格创建地形2D曲线

来自分类Dev

关于计算多个凸2D多边形的交集的建议

来自分类Dev

如何处理2D多边形轮廓的自相交

来自分类Dev

不同图层中2D多边形的岛的微分或相交

来自分类Dev

2D多边形顶点法线朝内/朝外?

来自分类Dev

如何选择指向任何凸多边形内部的法线向量(2d)?

来自分类Dev

使用 Struct 的 Encodable 协议解码 2D 多边形坐标

来自分类Dev

沿某个轴为 2D 多边形添加每个像素点

来自分类Dev

获取通过移动多边形创建的多边形

来自分类Dev

用boost :: geometry扩展多边形?

来自分类Dev

获取3D空间中平面多边形的顶点的局部2D坐标

来自分类Dev

在Slick2D中多边形的定位

来自分类Dev

Box2D libgdx多边形形状

来自分类Dev

在Slick2D中多边形的定位

Related 相关文章

热门标签

归档