c++algorithmpolygon

Optimize reverse polygon with edge attributes


I have a closed polygon, that has attribute parameters at each edge. These are stored at the point at the beginning of the following line segment. So, point[0] holds the attribute for the edge point[0] -> point[1].

Now, I want to reverse the polygon. The edge information, however, is now to be stored at a different point.

Here's a working solution for that problem:

#include <iostream>
#include <vector>
#include <algorithm>
// assume
struct Point{
    double x,y;
    int att;
    void setAttribute(int i){att=i;}
    int attribute()const{return att;}
};

// and in my class I have
std::vector<Point> m_PList;
// where the std::vector is a hybrid std::list/std::vector container. Anyway.

void reverseMyPoly(){
        auto swapStuff = [](Point* a, const Point* b) {
            a->setAttribute(b->attribute());
            // there's a lot more attribute code here
            };

        auto copy = m_PList; // !! this one is expensive !!
        std::reverse(m_PList.begin(), m_PList.end());
        const size_t n = m_PList.size();

        // Each point i in the reversed polygon gets the edge_type that was
        // originally associated with the edge from (i+1) % n to (i) % n
        for (size_t i = 0; i < n; ++i) {
            size_t original_index = (n - i - 2 + n) % n;
            swapStuff(&m_PList[i], &copy[original_index]);
        }
}

int main()
{
    m_PList = {{0,0,0}, {1,0,1}, {2,2,2}, {0,3,3}};
    reverseMyPoly();
    
    for(auto&p:m_PList){
        std::cout << p.x <<", "<<p.y<<" ("<<p.attribute() <<")"<<std::endl;
    }
    return 0;
}

You see, each edge (I put either x or y as the original point index) has the lower point index as an attribute. So edge 2->3 must have attribute 2. The reversed edge 3->2 must have the attribute 2 at it's beginning point.

My method, however, requires a full copy of the polygon. I wish there was a solution to bypass this.


Solution

  • As you noted, reversing the polygon swaps the leading point for each edge. Edge (0, 1) becomes (1, 0), (1, 2) becomes (2, 1), and so on. To keep edge attributes consistent, point 1 must now have the attribute for point 0, and so on. Generalizing, points[i].attribute = points[(i - 1) % n].attribute. Or, in the reversed polygon, reversedPoints[i].attribute = reversedPoints[(i + 1) % n].attribute.

    This can be implemented in a loop, as long as you account for the fact that the last point will need the attribute of the first point, which would have been overwritten earlier.

    std::reverse(m_PList.begin(), m_PList.end());
    const size_t n = m_PList.size();
    
    const auto firstAttribute = m_PList[0].attribute();
    for (size_t i = 0; i < n - 1; ++i) {
        m_PList[i].setAttribute(m_PList[i+1].attribute());
    }
    
    m_PList[n-1].setAttribute(firstAttribute);
    

    An alternative approach is to simply swap the attributes of consecutive points. This lets the first point's attribute "bubble up" to the last point without any special handling.

    auto swapStuff = [](Point* a, Point* b) {
        const auto bAttribute = b->attribute();
        b->setAttribute(a->attribute());
        a->setAttribute(bAttribute);
        // there's a lot more attribute code here
    };
    
    std::reverse(m_PList.begin(), m_PList.end());
    const size_t n = m_PList.size();
    
    // Each point i in the reversed polygon gets the edge_type that was
    // originally associated with the edge from (i-1) % n to (i) % n
    // This was stored at (i-1) % n in the original list, (i+1) % n in the reversed list
    for (size_t i = 0; i < n - 1; ++i) {
        swapStuff(&m_PList[i], &m_PList[i+1]);
    }