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