#ifndef MEDUSA_BITS_DOMAINS_POLYGONSHAPE_HPP_ #define MEDUSA_BITS_DOMAINS_POLYGONSHAPE_HPP_ #include "PolygonShape_fwd.hpp" #include "discretization_helpers.hpp" #include "DomainDiscretization.hpp" #include /** * @file * Implementation of PolygonShape class. */ namespace mm { template PolygonShape::PolygonShape(const std::vector& points) : points_(points) { scalar_t area = 0; int n = points_.size(); assert_msg(n >= 3, "At least three points are needed to form a polygon, got %d.", n); for (int i = 0; i < n; ++i) { int j = (i == n-1) ? 0 : (i+1); // next point area += (points_[i][0] - points_[j][0]) * (points_[i][1] + points_[j][1]); } assert_msg(std::abs(area) > 1e-10, "Given polygon has no area."); if (area < 0) { std::reverse(points_.begin(), points_.end()); } extendByMargin(); } template void PolygonShape::setMargin(scalar_t margin) { base_t::setMargin(margin); extendByMargin(); } template void PolygonShape::extendByMargin() { int n = points_.size(); points_with_margin_ = points_; for (int i = 0; i < n; ++i) { int j = (i == n - 1) ? 0 : (i + 1); // next int k = (i == 0) ? n - 1 : (i - 1); // prev vec_t next_edge = (points_[j] - points_[i]).normalized(); vec_t prev_edge = (points_[i] - points_[k]).normalized(); vec_t normal = {next_edge[1] + prev_edge[1], -next_edge[0] - prev_edge[0]}; points_with_margin_[i] += margin_*normal.normalized(); } points_with_margin_.push_back(points_with_margin_[0]); } template bool PolygonShape::contains(const vec_t& point) const { // Winding number test loosely based on http://geomalgorithms.com/a03-_inclusion.html. int n = points_.size(); // loop through all edges of the polygon int wn = 0; // the winding number counter auto& pts = points_with_margin_; for (int i = 0; i < n; i++) { // edge from points[i] to points[i+1] if (pts[i][1] <= point[1]) { // Start y <= point[1]. // An upward crossing and point left of edge. if (pts[i+1][1] > point[1] && isLeft(pts[i], pts[i+1], point) > 0) { ++wn; // Have a valid up intersect. } } else { // Start y > point[1] (no test needed). // A downward crossing and point right of edge. if (pts[i+1][1] <= point[1] && isLeft(pts[i], pts[i+1], point) < 0) { --wn; // Have a valid down intersect. } } } return wn != 0; } template std::pair PolygonShape::bbox() const { scalar_t maxx = -INF, maxy = -INF, minx = INF, miny = INF; int n = points_.size(); for (int i = 0; i < n; ++i) { if (points_[i][0] > maxx) maxx = points_[i][0]; if (points_[i][0] < minx) minx = points_[i][0]; if (points_[i][1] > maxy) maxy = points_[i][1]; if (points_[i][1] < miny) miny = points_[i][1]; } return {{minx, miny}, {maxx, maxy}}; } template DomainDiscretization PolygonShape::discretizeBoundaryWithStep( scalar_t step, int type) const { DomainDiscretization d(*this); int n = points_.size(); for (int i = 0; i < n; ++i) { int j = (i == n-1) ? 0 : (i+1); // next int k = (i == 0) ? n-1 : (i-1); // prev vec_t edge = points_[j] - points_[i]; scalar_t len = edge.norm(); // Add the vertex point first. vec_t next_edge_n = edge / len; vec_t prev_edge_n = (points_[i] - points_[k]).normalized(); vec_t p_normal = {next_edge_n[1] + prev_edge_n[1], -next_edge_n[0] - prev_edge_n[0]}; d.addBoundaryNode(points_[i], (type == 0) ? -i-1 : type, p_normal.normalized()); // And then the interior points on the edge. int count = iceil(len/step); vec_t e_normal = {edge[1] / len, -edge[0] / len}; for (int c = 1; c < count; ++c) { // only internal points d.addBoundaryNode(points_[i] + c / scalar_t(count) * edge, (type == 0) ? -i-1 : type, e_normal); } } return d; } template DomainDiscretization PolygonShape::discretizeBoundaryWithDensity( const std::function& dr, int type) const { DomainDiscretization d(*this); int n = points_.size(); for (int i = 0; i < n; ++i) { int j = (i == n-1) ? 0 : (i+1); // next int k = (i == 0) ? n-1 : (i-1); // prev vec_t edge = points_[j] - points_[i]; scalar_t len = edge.norm(); // Add the vertex point first. vec_t next_edge_n = edge / len; vec_t prev_edge_n = (points_[i] - points_[k]).normalized(); vec_t p_normal = {next_edge_n[1] + prev_edge_n[1], -next_edge_n[0] - prev_edge_n[0]}; d.addBoundaryNode(points_[i], (type == 0) ? -i-1 : type, p_normal.normalized()); auto points = discretization_helpers::discretizeLineWithDensity(points_[i], points_[j], dr); vec_t e_normal = {points_[j][1] - points_[i][1], -points_[j][0] + points_[i][0]}; e_normal.normalize(); for (const auto& p : points) { // only internal points d.addBoundaryNode(p, (type == 0) ? -i-1 : type, e_normal); } } return d; } } // namespace mm #endif // MEDUSA_BITS_DOMAINS_POLYGONSHAPE_HPP_