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