halfmesh_insert_edge.hpp

Back to Half-edge structure

pastel/geometry/halfmesh/

// Description: Inserts an edge.

#ifndef PASTELGEOMETRY_HALFMESH_INSERT_EDGE_HPP
#define PASTELGEOMETRY_HALFMESH_INSERT_EDGE_HPP

#include "pastel/geometry/halfmesh/halfmesh.h"

namespace Pastel
{

    template <
        typename Settings,
        template <typename> class Customization>
    template <typename... Type>
    auto HalfMesh<Settings, Customization>::insertEdge(
        const Vertex_ConstIterator& from, 
        const Vertex_ConstIterator& to,
        Type&&... data)
        -> InsertEdge_Return
    {
        ENSURE(from.isNormal());
        ENSURE(to.isNormal());

        if (!Loops)
        {
            if (from == to)
            {
                return insertEdgeReturn(edgeEnd(), false);
            }
        }

        if (!MultipleEdges)
        {
            Half_Iterator existingHalf = findHalf(from, to);
            if (!existingHalf.empty())
            {
                return insertEdgeReturn(existingHalf->edge(), false);
            }
        }

        // Check that the vertices are free. If not, the edge 
        // addition is not possible.

        // This will be reused later..
        Half_Iterator fromFree;

        if (!from->isolated())
        {
            fromFree = findFreeIncident(from);
            if (fromFree.empty())
            {
                return insertEdgeReturn(edgeEnd(), false);
            }
        }

        if (!to->isolated())
        {
            Half_Iterator toFreeHalf = findFreeIncident(to);
            if (toFreeHalf.empty())
            {
                return insertEdgeReturn(edgeEnd(), false);
            }
        }

        // Allocate data

        integer rollback = 0;

        Edge_Iterator edge;
        Half_Iterator fromTo;
        Half_Iterator toFrom;

        try
        {
            edge = edgeSet_.insertBack(std::forward<Type>(data)...);
            ++rollback;

            fromTo = halfSet_.insertBack();
            ++rollback;

            toFrom = halfSet_.insertBack();
            ++rollback;
        }
        catch(...)
        {
            switch(rollback)
            {
            case 2:
                halfSet_.erase(fromTo);
                // Fall-through
            case 1:
                edgeSet_.erase(edge);
                break;
            };

            throw;
        };

        // Initialize data

        {
            edge->half_ = fromTo;
        }

        {
            fromTo->next_ = toFrom;
            fromTo->previous_ = toFrom;

            fromTo->pair_ = toFrom;
            fromTo->origin_ = cast(from);
            fromTo->edge_ = edge;
            fromTo->left_ = Polygon_Iterator();
        }

        {
            toFrom->next_ = fromTo;
            toFrom->previous_ = fromTo;

            toFrom->pair_ = fromTo;
            toFrom->origin_ = cast(to);
            toFrom->edge_ = edge;
            toFrom->left_ = Polygon_Iterator();
        }

        // Link the from-side of the edge.

        if (from->isolated())
        {
            cast(from)->half_ = fromTo;
        }
        else
        {
            Half_Iterator fromIn = fromFree;

            ASSERT(!fromIn.empty());

            Half_Iterator fromOut = fromIn->next();

            fromIn->next_ = fromTo;
            fromTo->previous_ = fromIn;

            toFrom->next_ = fromOut;
            fromOut->previous_ = toFrom;
        }

        // Link the to-side of the edge.

        if (to->isolated())
        {
            cast(to)->half_ = toFrom;
        }
        else
        {
            // 'toFreeHalf' can't be reused here,
            // because the perimeter has changed
            // due to the linking of the from-side.
            Half_Iterator toIn = findFreeIncident(to);
            ASSERT(!toIn.empty());

            Half_Iterator toOut = toIn->next();

            toIn->next_ = toFrom;
            toFrom->previous_ = toIn;

            fromTo->next_ = toOut;
            toOut->previous_ = fromTo;
        }

        this->onInsertEdge(edge);

        return insertEdgeReturn(edge, true);
    }

}

#endif