Esempio n. 1
0
void HPlane::distance(LineSegmentSide const &lineSeg, coord_t *fromDist, coord_t *toDist) const
{
    // Any work to do?
    if(!fromDist && !toDist) return;

    /// @attention Ensure line segments produced from the partition's source
    /// line are always treated as collinear. This special case is only
    /// necessary due to precision inaccuracies when a line is split into
    /// multiple segments.
    if(d->lineSegment && &d->lineSegment->mapSide().line() == lineSeg.partitionMapLine())
    {
        if(fromDist) *fromDist = 0;
        if(toDist)   *toDist   = 0;
        return;
    }

    coord_t toSegDirectionV1[2] = { d->partition.direction.x, d->partition.direction.y } ;

    if(fromDist)
    {
        coord_t fromV1[2] = { lineSeg.from().origin().x, lineSeg.from().origin().y };
        *fromDist = V2d_PointLinePerpDistance(fromV1, toSegDirectionV1, d->perp, d->length);
    }
    if(toDist)
    {
        coord_t toV1[2]   = { lineSeg.to().origin().x, lineSeg.to().origin().y };
        *toDist = V2d_PointLinePerpDistance(toV1, toSegDirectionV1, d->perp, d->length);
    }
}
Esempio n. 2
0
void HPlane::configure(LineSegmentSide const &newBaseSeg)
{
    // Only map line segments are suitable.
    DENG2_ASSERT(newBaseSeg.hasMapSide());

    LOG_AS("HPlane::configure");

    // Clear the list of intersection points.
    clearIntercepts();

    // Reconfigure the partition line.
    LineSide &mapSide = newBaseSeg.mapSide();

    d->partition.direction = mapSide.to().origin() - mapSide.from().origin();
    d->partition.origin    = mapSide.from().origin();

    d->lineSegment = const_cast<LineSegmentSide *>(&newBaseSeg);

    d->length    = d->partition.direction.length();
    d->angle     = M_DirectionToAngleXY(d->partition.direction.x, d->partition.direction.y);
    d->slopeType = M_SlopeTypeXY(d->partition.direction.x, d->partition.direction.y);

    d->perp = d->partition.origin.y * d->partition.direction.x
              - d->partition.origin.x * d->partition.direction.y;

    d->para = -d->partition.origin.x * d->partition.direction.x
              -  d->partition.origin.y * d->partition.direction.y;

    //LOG_DEBUG("line segment %p %s")
    //        << &newBaseSeg << d->partition.asText();
}
Esempio n. 3
0
    /**
     * Find the intersection point between a line segment and the current
     * partition plane. Takes advantage of some common situations like
     * horizontal and vertical lines to choose a 'nicer' intersection point.
     */
    Vector2d intersectPartition(LineSegmentSide const &seg, coord_t fromDist,
                                coord_t toDist) const
    {
        // Horizontal partition vs vertical line segment.
        if(hplane.slopeType() == ST_HORIZONTAL && seg.slopeType() == ST_VERTICAL)
        {
            return Vector2d(seg.from().origin().x, hplane.partition().origin.y);
        }

        // Vertical partition vs horizontal line segment.
        if(hplane.slopeType() == ST_VERTICAL && seg.slopeType() == ST_HORIZONTAL)
        {
            return Vector2d(hplane.partition().origin.x, seg.from().origin().y);
        }

        // 0 = start, 1 = end.
        coord_t ds = fromDist / (fromDist - toDist);

        Vector2d point = seg.from().origin();
        if(seg.slopeType() != ST_VERTICAL)
            point.x += seg.direction().x * ds;

        if(seg.slopeType() != ST_HORIZONTAL)
            point.y += seg.direction().y * ds;

        return point;
    }
Esempio n. 4
0
double HPlane::intersect(LineSegmentSide const &lineSeg, int edge)
{
    Vertex &vertex = lineSeg.vertex(edge);
    coord_t pointV1[2]     = { vertex.origin().x, vertex.origin().y };
    coord_t directionV1[2] = { d->partition.direction.x, d->partition.direction.y };
    return V2d_PointLineParaDistance(pointV1, directionV1, d->para, d->length);
}
Esempio n. 5
0
    void buildSubspaceGeometries()
    {
        for(ConvexSubspaceProxy const &subspace : subspaces)
        {
            /// @todo Move BSP leaf construction here?
            BspLeaf &bspLeaf = *subspace.bspLeaf();

            subspace.buildGeometry(bspLeaf, *mesh);

            // Account the new segments.
            /// @todo Refactor away.
            for(OrderedSegment const &oseg : subspace.segments())
            {
                if(oseg.segment->hasHEdge())
                {
                    // There is now one more line segment.
                    segmentCount += 1;
                }
            }
        }

        /*
         * Finalize the built geometry by adding a twin half-edge for any
         * which don't yet have one.
         */
        for(ConvexSubspaceProxy const &convexSet : subspaces)
        for(OrderedSegment const &oseg : convexSet.segments())
        {
            LineSegmentSide *seg = oseg.segment;

            if(seg->hasHEdge() && !seg->back().hasHEdge())
            {
                HEdge *hedge = &seg->hedge();
                DENG2_ASSERT(!hedge->hasTwin());

                // Allocate the twin from the same mesh.
                hedge->setTwin(hedge->mesh().newHEdge(seg->back().from()));
                hedge->twin().setTwin(hedge);
            }
        }
    }
Esempio n. 6
0
    /**
     * Analyze the half-plane intercepts, building new line segments to cap
     * any gaps (new segments are added onto the end of the appropriate list
     * (rights to @a rightList and lefts to @a leftList)).
     *
     * @param rights  Set of line segments on the right of the partition.
     * @param lefts   Set of line segments on the left of the partition.
     */
    void addPartitionLineSegments(LineSegmentBlockTreeNode &rights,
                                  LineSegmentBlockTreeNode &lefts)
    {
        LOG_TRACE("Building line segments along partition %s")
                << hplane.partition().asText();

        // First, fix any near-distance issues with the intercepts.
        hplane.sortAndMergeIntercepts();

        //hplane.printIntercepts();

        // We must not create new line segments on top of the source partition
        // line segment (as this will result in duplicate edges finding their
        // way into the BSP leaf geometries).
        LineSegmentSide *partSeg = hplane.lineSegment();
        double nearDist = 0, farDist = 0;

        if(partSeg)
        {
            nearDist = hplane.intersect(*partSeg, LineSegment::From);
            farDist  = hplane.intersect(*partSeg, LineSegment::To);
        }

        // Create new line segments.
        for(int i = 0; i < hplane.interceptCount() - 1; ++i)
        {
            HPlaneIntercept const &cur  = hplane.intercepts()[i];
            HPlaneIntercept const &next = hplane.intercepts()[i+1];

            // Does this range overlap the partition line segment?
            if(partSeg && cur.distance() >= nearDist && next.distance() <= farDist)
                continue;

            // Void space or an existing segment between the two intercepts?
            if(!cur.after() && !next.before())
                continue;

            // Check for some nasty open/closed or close/open cases.
            if(cur.after() && !next.before())
            {
                if(!cur.lineSegmentIsSelfReferencing())
                {
                    Vector2d nearPoint = (cur.vertex().origin() + next.vertex().origin()) / 2;
                    notifyUnclosedSectorFound(*cur.after(), nearPoint);
                }
                continue;
            }

            if(!cur.after() && next.before())
            {
                if(!next.lineSegmentIsSelfReferencing())
                {
                    Vector2d nearPoint = (cur.vertex().origin() + next.vertex().origin()) / 2;
                    notifyUnclosedSectorFound(*next.before(), nearPoint);
                }
                continue;
            }

            /*
             * This is definitely open space.
             */
            Vertex &fromVertex = cur.vertex();
            Vertex &toVertex   = next.vertex();

            Sector *sector = cur.after();
            if(!cur.before() && next.before() == next.after())
            {
                sector = next.before();
            }
            // Choose the non-self-referencing sector when we can.
            else if(cur.after() != next.before())
            {
                if(!cur.lineSegmentIsSelfReferencing() &&
                   !next.lineSegmentIsSelfReferencing())
                {
                    LOG_DEBUG("Sector mismatch #%d %s != #%d %s")
                            << cur.after()->indexInMap()
                            << cur.vertex().origin().asText()
                            << next.before()->indexInMap()
                            << next.vertex().origin().asText();
                }

                LineSegmentSide *afterSeg = cur.afterLineSegment();
                if(afterSeg->hasMapLine() && afterSeg->mapLine().isSelfReferencing())
                {
                    LineSegmentSide *beforeSeg = next.beforeLineSegment();
                    if(beforeSeg->hasMapLine() && !beforeSeg->mapLine().isSelfReferencing())
                    {
                        sector = next.before();
                    }
                }
            }

            DENG2_ASSERT(sector);

            LineSegment &newSeg = *makeLineSegment(fromVertex, toVertex,
                                                   sector, sector, nullptr /*no map line*/,
                                                   partSeg? &partSeg->mapLine() : nullptr);

            edgeTipSet(newSeg.from()) << EdgeTip(newSeg.front());
            edgeTipSet(newSeg.to())   << EdgeTip(newSeg.back());

            // Add each new line segment to the appropriate set.
            linkLineSegmentInBlockTree(rights, newSeg.front());
            linkLineSegmentInBlockTree(lefts,  newSeg.back());

            /*
            LOG_DEBUG("Built line segment from %s to %s (sector #%i)")
                    << fromVertex.origin().asText()
                    << toVertex.origin().asText()
                    << sector->indexInArchive();
            */
        }
    }
Esempio n. 7
0
    /**
     * Remove all the line segments from the list, partitioning them into the
     * left or right sets according to their position relative to partition line.
     * Adds any intersections onto the intersection list as it goes.
     *
     * @param node    Block tree node containing the line segments to be partitioned.
     * @param rights  Set of line segments on the right side of the partition.
     * @param lefts   Set of line segments on the left side of the partition.
     */
    void divideSegments(LineSegmentBlockTreeNode &node, LineSegmentBlockTreeNode &rights,
                        LineSegmentBlockTreeNode &lefts)
    {
        /**
         * @todo Revise this algorithm so that @var segments is not modified
         * during the partitioning process.
         */
        int const totalSegs = node.userData()->totalCount();
        DENG2_ASSERT(totalSegs != 0);
        DENG2_UNUSED(totalSegs);

        // Iterative pre-order traversal of SuperBlock.
        LineSegmentBlockTreeNode *cur  = &node;
        LineSegmentBlockTreeNode *prev = nullptr;
        while(cur)
        {
            while(cur)
            {
                LineSegmentBlock &segs = *cur->userData();

                LineSegmentSide *seg;
                while((seg = segs.pop()))
                {
                    // Disassociate the line segment from the block tree.
                    seg->setBlockTreeNode(nullptr);

                    divideOneSegment(*seg, rights, lefts);
                }

                if(prev == cur->parentPtr())
                {
                    // Descending - right first, then left.
                    prev = cur;
                    if(cur->hasRight()) cur = cur->rightPtr();
                    else                cur = cur->leftPtr();
                }
                else if(prev == cur->rightPtr())
                {
                    // Last moved up the right branch - descend the left.
                    prev = cur;
                    cur = cur->leftPtr();
                }
                else if(prev == cur->leftPtr())
                {
                    // Last moved up the left branch - continue upward.
                    prev = cur;
                    cur = cur->parentPtr();
                }
            }

            if(prev)
            {
                // No left child - back up.
                cur = prev->parentPtr();
            }
        }

        // Sanity checks...
        DENG2_ASSERT(rights.userData()->totalCount());
        DENG2_ASSERT(lefts.userData ()->totalCount());
        DENG2_ASSERT((  rights.userData()->totalCount()
                      + lefts.userData ()->totalCount()) >= totalSegs);
    }
Esempio n. 8
0
    /**
     * Take the given line segment @a lineSeg, compare it with the partition
     * plane and determine into which of the two sets it should be. If the
     * line segment is found to intersect the partition, the intercept point
     * is determined and the line segment then split in two at this point.
     * Each piece of the line segment is then added to the relevant set.
     *
     * If the line segment is collinear with, or intersects the partition then
     * a new intercept is added to the partitioning half-plane.
     *
     * @note Any existing @em twin of @a lineSeg is so too handled uniformly.
     *
     * @param seg     Line segment to be "partitioned".
     * @param rights  Set of line segments on the right side of the partition.
     * @param lefts   Set of line segments on the left side of the partition.
     */
    void divideOneSegment(LineSegmentSide &seg, LineSegmentBlockTreeNode &rights,
                          LineSegmentBlockTreeNode &lefts)
    {
        coord_t fromDist, toDist;
        LineRelationship rel = hplane.relationship(seg, &fromDist, &toDist);
        switch(rel)
        {
        case Collinear: {
            interceptPartition(seg, LineSegment::From);
            interceptPartition(seg, LineSegment::To);

            // Direction (vs that of the partition plane) determines in which
            // subset this line segment belongs.
            if(seg.direction().dot(hplane.partition().direction) < 0)
            {
                linkLineSegmentInBlockTree(lefts, seg);
            }
            else
            {
                linkLineSegmentInBlockTree(rights, seg);
            }
            break; }

        case Right:
        case RightIntercept:
            if(rel == RightIntercept)
            {
                // Direction determines which edge of the line segment interfaces
                // with the new half-plane intercept.
                interceptPartition(seg, (fromDist < DIST_EPSILON? LineSegment::From : LineSegment::To));
            }
            linkLineSegmentInBlockTree(rights, seg);
            break;

        case Left:
        case LeftIntercept:
            if(rel == LeftIntercept)
            {
                interceptPartition(seg, (fromDist > -DIST_EPSILON? LineSegment::From : LineSegment::To));
            }
            linkLineSegmentInBlockTree(lefts, seg);
            break;

        case Intersects: {
            // Calculate the intersection point and split this line segment.
            Vector2d point = intersectPartition(seg, fromDist, toDist);
            LineSegmentSide &newFrontRight = splitLineSegment(seg, point);

            // Ensure the new back left segment is inserted into the same block as
            // the old back right segment.
            if(LineSegmentBlockTreeNode *backLeftBlock = (LineSegmentBlockTreeNode *)seg.back().blockTreeNodePtr())
            {
                linkLineSegmentInBlockTree(*backLeftBlock, newFrontRight.back());
            }

            interceptPartition(seg, LineSegment::To);

            // Direction determines which subset the line segments are added to.
            if(fromDist < 0)
            {
                linkLineSegmentInBlockTree(rights, newFrontRight);
                linkLineSegmentInBlockTree(lefts,  seg);
            }
            else
            {
                linkLineSegmentInBlockTree(rights, seg);
                linkLineSegmentInBlockTree(lefts,  newFrontRight);
            }
            break; }
        }
    }
Esempio n. 9
0
 /// @todo refactor away
 inline void interceptPartition(LineSegmentSide &seg, int edge)
 {
     hplane.intercept(seg, edge, edgeTipSet(seg.vertex(edge)));
 }
Esempio n. 10
0
    /**
     * Link @a seg into the line segment block tree.
     *
     * Performs k-d tree subdivision of the 2D coordinate space, splitting the node
     * tree as necessary, however new nodes are created only when they need to be
     * populated (i.e., a split does not generate two nodes at the same time).
     */
    void linkLineSegmentInBlockTree(LineSegmentBlockTreeNode &node_, LineSegmentSide &seg)
    {
        // Traverse the node tree beginning at "this" node.
        for(LineSegmentBlockTreeNode *node = &node_; ;)
        {
            LineSegmentBlock &block = *node->userData();
            AABox const &bounds     = block.bounds();

            // The segment "touches" this node; increment the ref counters.
            block.addRef(seg);

            // Determine whether further subdivision is necessary/possible.
            Vector2i dimensions(Vector2i(bounds.max) - Vector2i(bounds.min));
            if(dimensions.x <= 256 && dimensions.y <= 256)
            {
                // Thats as small as we go; link it in and return.
                block.link(seg);
                seg.setBlockTreeNode(node);
                return;
            }

            // Determine whether the node should be split and on which axis.
            int const splitAxis = (dimensions.x < dimensions.y); // x=0, y=1
            int const midOnAxis = (bounds.min[splitAxis] + bounds.max[splitAxis]) / 2;
            LineSegmentBlockTreeNode::ChildId fromSide = LineSegmentBlockTreeNode::ChildId(seg.from().origin()[splitAxis] >= midOnAxis);
            LineSegmentBlockTreeNode::ChildId toSide   = LineSegmentBlockTreeNode::ChildId(seg.to  ().origin()[splitAxis] >= midOnAxis);

            // Does the segment lie entirely within one half of this node?
            if(fromSide != toSide)
            {
                // No, the segment crosses @var midOnAxis; link it in and return.
                block.link(seg);
                seg.setBlockTreeNode(node);
                return;
            }

            // Do we need to create the child node?
            if(!node->hasChild(fromSide))
            {
                bool const toLeft = (fromSide == LineSegmentBlockTreeNode::Left);

                AABox childBounds;
                if(splitAxis)
                {
                    // Vertical split.
                    int division = bounds.minY + 0.5 + (bounds.maxY - bounds.minY) / 2;

                    childBounds.minX = bounds.minX;
                    childBounds.minY = (toLeft? division : bounds.minY);

                    childBounds.maxX = bounds.maxX;
                    childBounds.maxY = (toLeft? bounds.maxY : division);
                }
                else
                {
                    // Horizontal split.
                    int division = bounds.minX + 0.5 + (bounds.maxX - bounds.minX) / 2;

                    childBounds.minX = (toLeft? division : bounds.minX);
                    childBounds.minY = bounds.minY;

                    childBounds.maxX = (toLeft? bounds.maxX : division);
                    childBounds.maxY = bounds.maxY;
                }

                // Add a new child node and link it to its parent.
                LineSegmentBlock *childBlock = new LineSegmentBlock(childBounds);
                node->setChild(fromSide, new LineSegmentBlockTreeNode(childBlock, node));
            }

            // Descend to the child node.
            node = node->childPtr(fromSide);
        }
    }