Vector<Vector2> expand(const Vector<Vector2> &points, const Rect2i &rect, float epsilon = 2.0) { int size = points.size(); ERR_FAIL_COND_V(size < 2, Vector<Vector2>()); ClipperLib::Path subj; ClipperLib::PolyTree solution; ClipperLib::PolyTree out; for (int i = 0; i < points.size(); i++) { subj << ClipperLib::IntPoint(points[i].x * PRECISION, points[i].y * PRECISION); } ClipperLib::ClipperOffset co; co.AddPath(subj, ClipperLib::jtMiter, ClipperLib::etClosedPolygon); co.Execute(solution, epsilon * PRECISION); ClipperLib::PolyNode *p = solution.GetFirst(); ERR_FAIL_COND_V(!p, points); while (p->IsHole()) { p = p->GetNext(); } //turn the result into simply polygon (AKA, fix overlap) //clamp into the specified rect ClipperLib::Clipper cl; cl.StrictlySimple(true); cl.AddPath(p->Contour, ClipperLib::ptSubject, true); //create the clipping rect ClipperLib::Path clamp; clamp.push_back(ClipperLib::IntPoint(0, 0)); clamp.push_back(ClipperLib::IntPoint(rect.size.width * PRECISION, 0)); clamp.push_back(ClipperLib::IntPoint(rect.size.width * PRECISION, rect.size.height * PRECISION)); clamp.push_back(ClipperLib::IntPoint(0, rect.size.height * PRECISION)); cl.AddPath(clamp, ClipperLib::ptClip, true); cl.Execute(ClipperLib::ctIntersection, out); Vector<Vector2> outPoints; ClipperLib::PolyNode *p2 = out.GetFirst(); ERR_FAIL_COND_V(!p2, points); while (p2->IsHole()) { p2 = p2->GetNext(); } int lasti = p2->Contour.size() - 1; Vector2 prev = Vector2(p2->Contour[lasti].X / PRECISION, p2->Contour[lasti].Y / PRECISION); for (unsigned int i = 0; i < p2->Contour.size(); i++) { Vector2 cur = Vector2(p2->Contour[i].X / PRECISION, p2->Contour[i].Y / PRECISION); if (cur.distance_to(prev) > 0.5) { outPoints.push_back(cur); prev = cur; } } return outPoints; }
std::vector<Vec2> AutoPolygon::expand(const std::vector<Vec2>& points, const cocos2d::Rect &rect, const float& epsilon) { auto size = points.size(); // if there are less than 3 points, then we have nothing if(size<3) { log("AUTOPOLYGON: cannot expand points for %s with less than 3 points, e: %f", _filename.c_str(), epsilon); return std::vector<Vec2>(); } ClipperLib::Path subj; ClipperLib::PolyTree solution; ClipperLib::PolyTree out; for(std::vector<Vec2>::const_iterator it = points.begin(); it<points.end(); it++) { subj << ClipperLib::IntPoint(it-> x* PRECISION, it->y * PRECISION); } ClipperLib::ClipperOffset co; co.AddPath(subj, ClipperLib::jtMiter, ClipperLib::etClosedPolygon); co.Execute(solution, epsilon * PRECISION); ClipperLib::PolyNode* p = solution.GetFirst(); if(!p) { log("AUTOPOLYGON: Clipper failed to expand the points"); return points; } while(p->IsHole()){ p = p->GetNext(); } //turn the result into simply polygon (AKA, fix overlap) //clamp into the specified rect ClipperLib::Clipper cl; cl.StrictlySimple(true); cl.AddPath(p->Contour, ClipperLib::ptSubject, true); //create the clipping rect ClipperLib::Path clamp; clamp.push_back(ClipperLib::IntPoint(0, 0)); clamp.push_back(ClipperLib::IntPoint(rect.size.width/_scaleFactor * PRECISION, 0)); clamp.push_back(ClipperLib::IntPoint(rect.size.width/_scaleFactor * PRECISION, rect.size.height/_scaleFactor * PRECISION)); clamp.push_back(ClipperLib::IntPoint(0, rect.size.height/_scaleFactor * PRECISION)); cl.AddPath(clamp, ClipperLib::ptClip, true); cl.Execute(ClipperLib::ctIntersection, out); std::vector<Vec2> outPoints; ClipperLib::PolyNode* p2 = out.GetFirst(); while(p2->IsHole()){ p2 = p2->GetNext(); } auto end = p2->Contour.end(); for(std::vector<ClipperLib::IntPoint>::const_iterator pt = p2->Contour.begin(); pt < end; pt++) { outPoints.push_back(Vec2(pt->X/PRECISION, pt->Y/PRECISION)); } return outPoints; }
void ElementGeometryClipper::visitWay(const Way& way) { ClipperLib::Path wayShape; PointLocation pointLocation = setPath(quadKeyBbox_, way, wayShape); // 1. all geometry inside current quadkey: no need to truncate. if (pointLocation == PointLocation::AllInside) { callback_(way, quadKey_); return; } // 2. all geometry outside : way should be skipped if (pointLocation == PointLocation::AllOutside) { return; } ClipperLib::PolyTree solution; clipper_.AddPath(wayShape, ClipperLib::ptSubject, false); clipper_.AddPath(createPathFromBoundingBox(), ClipperLib::ptClip, true); clipper_.Execute(ClipperLib::ctIntersection, solution); clipper_.Clear(); std::size_t count = static_cast<std::size_t>(solution.Total()); // 3. way intersects border only once: store a copy with clipped geometry if (count == 1) { Way clippedWay; setData(clippedWay, way, solution.GetFirst()->Contour); callback_(clippedWay, quadKey_); } // 4. in this case, result should be stored as relation (collection of ways) else { Relation relation; relation.id = way.id; relation.tags = way.tags; relation.elements.reserve(count); ClipperLib::PolyNode* polyNode = solution.GetFirst(); while (polyNode) { auto clippedWay = std::make_shared<Way>(); clippedWay->id = way.id; setCoordinates(*clippedWay, polyNode->Contour); relation.elements.push_back(clippedWay); polyNode = polyNode->GetNext(); } callback_(relation, quadKey_); } }
void ElementGeometryClipper::visitRelation(const Relation& relation) { struct RelationVisitor : public ElementVisitor { RelationVisitor(const BoundingBox& quadKeyBbox, ClipperLib::Clipper& clipper) : bbox_(quadKeyBbox), clipper_(clipper) { } void visitNode(const Node& node) { // TODO } void visitWay(const Way& way) { ClipperLib::Path wayShape; setPath(bbox_, way, wayShape); clipper_.AddPath(wayShape, ClipperLib::ptSubject, false); } void visitArea(const Area& area) { ClipperLib::Path areaShape; setPath(bbox_, area, areaShape); clipper_.AddPath(areaShape, ClipperLib::ptSubject, true); } void visitRelation(const Relation& relation) { for (const auto& element : relation.elements) { element->accept(*this); } } private: const BoundingBox& bbox_; ClipperLib::Clipper& clipper_; } visitor(quadKeyBbox_, clipper_); relation.accept(visitor); // Process results ClipperLib::PolyTree solution; clipper_.AddPath(createPathFromBoundingBox(), ClipperLib::ptClip, true); clipper_.Execute(ClipperLib::ctIntersection, solution); clipper_.Clear(); std::size_t count = static_cast<size_t>(solution.Total()); // Do not store one result as relation if (count == 1) { ClipperLib::PolyNode* node = solution.GetFirst(); if (node->IsOpen()) { Way way; setData(way, relation, node->Contour); callback_(way, quadKey_); } else { if (!node->IsHole()) { Area area; setData(area, relation, node->Contour); callback_(area, quadKey_); } } } else if (count > 1) { Relation newRelation; newRelation.id = relation.id; newRelation.tags = relation.tags; newRelation.elements.reserve(count); ClipperLib::PolyNode* polyNode = solution.GetFirst(); while (polyNode) { if (polyNode->IsOpen()) { auto way = std::make_shared<Way>(); setCoordinates(*way, polyNode->Contour); newRelation.elements.push_back(way); } else { auto area = std::make_shared<Area>(); setCoordinates(*area, polyNode->Contour); newRelation.elements.push_back(area); } polyNode = polyNode->GetNext(); } callback_(newRelation, quadKey_); } }
bool validate() const { mapnik::geometry::geometry<double> geom; if (!mapnik::from_wkt(wkt_in_, geom)) { throw std::runtime_error("Failed to parse WKT"); } if (mapnik::geometry::is_empty(geom)) { std::clog << "empty geom!\n"; return false; } if (!geom.is<mapnik::geometry::polygon<double> >()) { std::clog << "not a polygon!\n"; return false; } mapnik::geometry::polygon<double> & poly = mapnik::util::get<mapnik::geometry::polygon<double> >(geom); mapnik::geometry::correct(poly); ClipperLib::Clipper clipper; mapnik::geometry::line_string<std::int64_t> path; for (auto const& pt : poly.exterior_ring) { double x = pt.x; double y = pt.y; path.emplace_back(static_cast<ClipperLib::cInt>(x),static_cast<ClipperLib::cInt>(y)); } if (!clipper.AddPath(path, ClipperLib::ptSubject, true)) { std::clog << "ptSubject ext failed!\n"; } for (auto const& ring : poly.interior_rings) { path.clear(); for (auto const& pt : ring) { double x = pt.x; double y = pt.y; path.emplace_back(static_cast<ClipperLib::cInt>(x),static_cast<ClipperLib::cInt>(y)); } if (!clipper.AddPath(path, ClipperLib::ptSubject, true)) { std::clog << "ptSubject ext failed!\n"; } } std::cerr << "path size=" << path.size() << std::endl; mapnik::geometry::line_string<std::int64_t> clip_box; clip_box.emplace_back(static_cast<ClipperLib::cInt>(extent_.minx()),static_cast<ClipperLib::cInt>(extent_.miny())); clip_box.emplace_back(static_cast<ClipperLib::cInt>(extent_.maxx()),static_cast<ClipperLib::cInt>(extent_.miny())); clip_box.emplace_back(static_cast<ClipperLib::cInt>(extent_.maxx()),static_cast<ClipperLib::cInt>(extent_.maxy())); clip_box.emplace_back(static_cast<ClipperLib::cInt>(extent_.minx()),static_cast<ClipperLib::cInt>(extent_.maxy())); clip_box.emplace_back(static_cast<ClipperLib::cInt>(extent_.minx()),static_cast<ClipperLib::cInt>(extent_.miny())); if (!clipper.AddPath( clip_box, ClipperLib::ptClip, true )) { std::clog << "ptClip failed!\n"; } ClipperLib::PolyTree polygons; clipper.Execute(ClipperLib::ctIntersection, polygons, ClipperLib::pftNonZero, ClipperLib::pftNonZero); clipper.Clear(); ClipperLib::PolyNode* polynode = polygons.GetFirst(); mapnik::geometry::multi_polygon<double> mp; mp.emplace_back(); bool first = true; while (polynode) { if (!polynode->IsHole()) { if (first) first = false; else mp.emplace_back(); // start new polygon for (auto const& pt : polynode->Contour) { mp.back().exterior_ring.add_coord(pt.x, pt.y); } // childrens are interior rings for (auto const* ring : polynode->Childs) { mapnik::geometry::linear_ring<double> hole; for (auto const& pt : ring->Contour) { hole.add_coord(pt.x, pt.y); } mp.back().add_hole(std::move(hole)); } } polynode = polynode->GetNext(); } std::string expect = expected_+".png"; std::string actual = expected_+"_actual.png"; mapnik::geometry::geometry<double> geom2(mp); auto env = mapnik::geometry::envelope(geom2); if (!mapnik::util::exists(expect) || (std::getenv("UPDATE") != nullptr)) { std::clog << "generating expected image: " << expect << "\n"; render(mp,env,expect); } render(mp,env,actual); return benchmark::compare_images(actual,expect); }