/*private*/ void OffsetCurveBuilder::addCircle(const Coordinate &p, double distance) { // add start point Coordinate pt(p.x + distance, p.y); vertexList->addPt(pt); addFillet(p, 0.0, 2.0*PI, -1, distance); }
/*private*/ void OffsetSegmentGenerator::createCircle(const Coordinate &p, double distance) { // add start point Coordinate pt(p.x + distance, p.y); segList.addPt(pt); addFillet(p, 0.0, 2.0*PI, -1, distance); segList.closeRing(); }
/*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::addFillet(const Coordinate &p, const Coordinate &p0, const Coordinate &p1, int direction, double distance) { double dx0=p0.x-p.x; double dy0=p0.y-p.y; double startAngle=atan2(dy0, dx0); double dx1=p1.x-p.x; double dy1=p1.y-p.y; double endAngle=atan2(dy1, dx1); if (direction==CGAlgorithms::CLOCKWISE) { if (startAngle<= endAngle) startAngle += 2.0*PI; } else { // direction==COUNTERCLOCKWISE if (startAngle>=endAngle) startAngle-=2.0*PI; } vertexList->addPt(p0); addFillet(p, startAngle, endAngle, direction, distance); vertexList->addPt(p1); }
/* private */ void OffsetCurveBuilder::addCollinear(bool addStartPoint) { /** * This test could probably be done more efficiently, * but the situation of exact collinearity should be fairly rare. */ 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. * 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). */ if ( bufParams.getJoinStyle() == BufferParameters::JOIN_BEVEL || bufParams.getJoinStyle() == BufferParameters::JOIN_MITRE) { if (addStartPoint) vertexList->addPt(offset0.p1); vertexList->addPt(offset1.p0); } else { addFillet(s1, offset0.p1, offset1.p0, CGAlgorithms::CLOCKWISE, distance); } } }
/*private*/ void OffsetSegmentGenerator::addFillet(const Coordinate &p, const Coordinate &p0, const Coordinate &p1, int direction, double radius) { double dx0 = p0.x - p.x; double dy0 = p0.y - p.y; double startAngle = atan2(dy0, dx0); double dx1 = p1.x - p.x; double dy1 = p1.y - p.y; double endAngle = atan2(dy1, dx1); if (direction == CGAlgorithms::CLOCKWISE) { if (startAngle <= endAngle) startAngle += 2.0 * PI; } else { // direction==COUNTERCLOCKWISE if (startAngle >= endAngle) startAngle -= 2.0 * PI; } segList.addPt(p0); addFillet(p, startAngle, endAngle, direction, radius); segList.addPt(p1); }
/* private */ void OffsetCurveBuilder::addOutsideTurn(int orientation, bool addStartPoint) { /** * Heuristic: If offset endpoints are very close together, * just use one of them as the corner vertex. * This avoids problems with computing mitre corners in the case * where the two segments are almost parallel * (which is hard to compute a robust intersection for). */ if (offset0.p1.distance(offset1.p0) < distance*OFFSET_SEGMENT_SEPARATION_FACTOR) { vertexList->addPt(offset0.p1); return; } if (bufParams.getJoinStyle() == BufferParameters::JOIN_MITRE) { addMitreJoin(s1, offset0, offset1, distance); } else if (bufParams.getJoinStyle() == BufferParameters::JOIN_BEVEL) { addBevelJoin(offset0, offset1); } else { // add a circular fillet connecting 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); } }
/*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); } } } }