/*private*/ void OffsetCurveBuilder::addLineEndCap(const Coordinate &p0, const Coordinate &p1) { LineSegment seg(p0, p1); LineSegment offsetL; computeOffsetSegment(seg, Position::LEFT, distance, offsetL); LineSegment offsetR; computeOffsetSegment(seg, Position::RIGHT, distance, offsetR); double dx=p1.x-p0.x; double dy=p1.y-p0.y; double angle=atan2(dy, dx); switch (bufParams.getEndCapStyle()) { case BufferParameters::CAP_ROUND: // add offset seg points with a fillet between them vertexList->addPt(offsetL.p1); addFillet(p1, angle+PI/2.0, angle-PI/2.0, CGAlgorithms::CLOCKWISE, distance); vertexList->addPt(offsetR.p1); break; case BufferParameters::CAP_FLAT: // only offset segment points are added vertexList->addPt(offsetL.p1); vertexList->addPt(offsetR.p1); break; case BufferParameters::CAP_SQUARE: // add a square defined by extensions of the offset // segment endpoints Coordinate squareCapSideOffset; squareCapSideOffset.x=fabs(distance)*cos(angle); squareCapSideOffset.y=fabs(distance)*sin(angle); Coordinate squareCapLOffset( offsetL.p1.x+squareCapSideOffset.x, offsetL.p1.y+squareCapSideOffset.y); Coordinate squareCapROffset( offsetR.p1.x+squareCapSideOffset.x, offsetR.p1.y+squareCapSideOffset.y); vertexList->addPt(squareCapLOffset); vertexList->addPt(squareCapROffset); break; } }
/*private*/ void OffsetCurveBuilder::initSideSegments(const Coordinate &nS1, const Coordinate &nS2, int nSide) { s1=nS1; s2=nS2; side=nSide; seg1.setCoordinates(s1, s2); computeOffsetSegment(seg1, side, distance, offset1); }
/*public*/ void OffsetSegmentGenerator::addNextSegment(const Coordinate &p, bool addStartPoint) { // do nothing if points are equal if (s2==p) return; // s0-s1-s2 are the coordinates of the previous segment // and the current one s0=s1; s1=s2; s2=p; seg0.setCoordinates(s0, s1); computeOffsetSegment(seg0, side, distance, offset0); seg1.setCoordinates(s1, s2); computeOffsetSegment(seg1, side, distance, offset1); int orientation=CGAlgorithms::computeOrientation(s0, s1, s2); bool outsideTurn = (orientation==CGAlgorithms::CLOCKWISE && side==Position::LEFT) || (orientation==CGAlgorithms::COUNTERCLOCKWISE && side==Position::RIGHT); if (orientation==0) { // lines are collinear addCollinear(addStartPoint); } else if (outsideTurn) { addOutsideTurn(orientation, addStartPoint); } else { // inside turn addInsideTurn(orientation, addStartPoint); } }
/*private*/ void OffsetCurveBuilder::addNextSegment(const Coordinate &p, bool addStartPoint) { // s0-s1-s2 are the coordinates of the previous segment and the current one s0=s1; s1=s2; s2=p; seg0.setCoordinates(s0, s1); computeOffsetSegment(seg0, side, distance, offset0); seg1.setCoordinates(s1, s2); computeOffsetSegment(seg1, side, distance, offset1); // do nothing if points are equal if (s1==s2) return; int orientation=CGAlgorithms::computeOrientation(s0, s1, s2); bool outsideTurn =(orientation==CGAlgorithms::CLOCKWISE && side==Position::LEFT) ||(orientation==CGAlgorithms::COUNTERCLOCKWISE && side==Position::RIGHT); if (orientation==0) { // lines are collinear li.computeIntersection(s0,s1,s1,s2); int numInt=li.getIntersectionNum(); /** * if numInt is<2, the lines are parallel and in the same direction. * In this case the point can be ignored, since the offset lines will also be * parallel. */ if (numInt>= 2) { /** * segments are collinear but reversing. Have to add an "end-cap" fillet * all the way around to other direction * This case should ONLY happen for LineStrings, so the orientation is always CW. * Polygons can never have two consecutive segments which are parallel but * reversed, because that would be a self intersection. */ addFillet(s1, offset0.p1, offset1.p0, CGAlgorithms::CLOCKWISE, distance); } } else if (outsideTurn) { // add a fillet to connect the endpoints of the offset segments if (addStartPoint) vertexList->addPt(offset0.p1); // TESTING-comment out to produce beveled joins addFillet(s1, offset0.p1, offset1.p0, orientation, distance); vertexList->addPt(offset1.p0); } else { // inside turn // add intersection point of offset segments (if any) li.computeIntersection( offset0.p0, offset0.p1, offset1.p0, offset1.p1); if (li.hasIntersection()) { vertexList->addPt(li.getIntersection(0)); } else { /** * If no intersection, it means the angle is so small and/or the offset so large * that the offsets segments don't intersect. * In this case we must add a offset joining curve to make sure the buffer line * is continuous and tracks the buffer correctly around the corner. * Note that the joining curve won't appear in the final buffer. * * The intersection test above is vulnerable to robustness errors; * i.e. it may be that the offsets should intersect very close to their * endpoints, but don't due to rounding. To handle this situation * appropriately, we use the following test: * If the offset points are very close, don't add a joining curve * but simply use one of the offset points */ if (offset0.p1.distance(offset1.p0)<distance / 1000.0) { vertexList->addPt(offset0.p1); } else { // add endpoint of this segment offset vertexList->addPt(offset0.p1); // <FIX> MD-add in centre point of corner, to make sure offset closer lines have correct topology vertexList->addPt(s1); vertexList->addPt(offset1.p0); } } } }