/** * 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); }
/** * 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); } }