Newer
Older
// check if a 2d point is already in the container
bool already_placed(const Point &p, const std::vector<Point> &vec, double xs = 1.0, double ys = 1.0, double tol = 1e-6)
{
for (auto &pt : vec) {
if ((std::abs(pt.x() - p.x())/xs < tol) && std::abs(pt.y() - p.y())/ys < tol) {
return true;
}
}
return false;
}
inline bool rec_in_ring(const Point &pt, double sx, double sy, double rmin, double rmax, double phmin, double phmax)
{
if (pt.r() > rmax || pt.r() < rmin) {
return false;
}
// check four corners
std::vector<Point> pts {
Point(pt.x() - sx/2., pt.y() - sy/2.),
Point(pt.x() - sx/2., pt.y() + sy/2.),
Point(pt.x() + sx/2., pt.y() - sy/2.),
Point(pt.x() + sx/2., pt.y() + sy/2.),
};
for (auto &p : pts) {
if (p.r() > rmax || p.r() < rmin || p.phi() > phmax || p.phi() < phmin) {
return false;
}
}
return true;
}
// a helper function to recursively fill square in a ring
void add_rectangle(Point p, std::vector<Point> &res, double sx, double sy,
double rmin, double rmax, double phmin, double phmax, int max_depth = 20, int depth = 0)
// std::cout << depth << "/" << max_depth << std::endl;
// exceeds the maximum depth in searching or already placed
if ((depth > max_depth) || (already_placed(p, res, sx, sy))) {
bool in_ring = rec_in_ring(p, sx, sy, rmin, rmax, phmin, phmax);
if (in_ring) {
res.emplace_back(p);
}
// continue search for a good placement or if no placement found yet
if (in_ring || res.empty()) {
// check adjacent squares
add_rectangle(Point(p.x() + sx, p.y()), res, sx, sy, rmin, rmax, phmin, phmax, max_depth, depth + 1);
add_rectangle(Point(p.x() - sx, p.y()), res, sx, sy, rmin, rmax, phmin, phmax, max_depth, depth + 1);
add_rectangle(Point(p.x(), p.y() + sy), res, sx, sy, rmin, rmax, phmin, phmax, max_depth, depth + 1);
add_rectangle(Point(p.x(), p.y() - sy), res, sx, sy, rmin, rmax, phmin, phmax, max_depth, depth + 1);
}
std::vector<Point> fillRectangles(Point ref, double sx, double sy, double rmin, double rmax, double phmin, double phmax)
// convert (0, 2pi) to (-pi, pi)
if (phmax > M_PI) {
phmin -= M_PI;
phmax -= M_PI;
}
// start with a seed square and find one in the ring
// move to center
ref = ref - Point(int(ref.x()/sx)*sx, int(ref.y()/sy)*sy);
std::vector<Point> res;
add_rectangle(ref, res, sx, sy, rmin, rmax, phmin, phmax, (int(rmax/sx) + 1)*(int(rmax/sy) + 1)*2);
return res;
}
// check if a regular polygon is inside a ring
bool poly_in_ring(const Point &p, int nsides, double lside, double rmin, double rmax, double phmin, double phmax)
{
// outer radius is contained
if ((p.r() + lside <= rmax) && (p.r() - lside >= rmin)) {
return true;
}
// inner radius is not contained
double rin = std::cos(M_PI/nsides)*lside;
if ((p.r() + rin > rmax) || (p.r() - rin < rmin)) {
return false;
}
// in between, check every corner
for (int i = 0; i < nsides; ++i) {
double phi = (i + 0.5)*2.*M_PI/static_cast<double>(nsides);
Point p2(p.x() + 2.*lside*std::sin(phi), p.y() + 2.*lside*std::cos(phi));
if ((p2.r() > rmax) || (p2.r() < rmin)) {
return false;
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
}
return true;
}
// recursively fill square (nside=4) or hexagon (nside=6) in a ring, other polygons won't work
void add_poly(Point p, std::vector<Point> &res, int nsides, double lside,
double rmin, double rmax, double phmin, double phmax, int max_depth = 20, int depth = 0)
{
// std::cout << depth << "/" << max_depth << std::endl;
// exceeds the maximum depth in searching or already placed
if ((depth > max_depth) || (already_placed(p, res, lside, lside))) {
return;
}
bool in_ring = poly_in_ring(p, nsides, lside, rmin, rmax, phmin, phmax);
if (in_ring) {
res.emplace_back(p);
}
// recursively add neigbors, continue if it was a good placement or no placement found yet
if (in_ring || res.empty()) {
double d = 2.*std::cos(M_PI/static_cast<double>(nsides))*lside;
for (int i = 0; i < nsides; ++i) {
double phi = i*2.*M_PI/static_cast<double>(nsides);
add_poly(Point(p.x() + 2.*lside*std::sin(phi), p.y() + 2.*lside*std::cos(phi)), res, nsides, lside,
rmin, rmax, phmin, phmax, max_depth, depth + 1);
}
}
}
std::vector<Point> fillHexagons(Point ref, double lside, double rmin, double rmax, double phmin, double phmax)
{
// convert (0, 2pi) to (-pi, pi)
if (phmax > M_PI) {
phmin -= M_PI;
phmax -= M_PI;
}
// start with a seed and find one in the ring
// move to center
ref = ref - Point(int(ref.x()/lside)*lside, int(ref.y()/lside)*lside);
add_poly(ref, res, 6, lside, rmin, rmax, phmin, phmax, std::pow(int(rmax/lside) + 1, 2)*2);