155 lines
3.8 KiB
C++
155 lines
3.8 KiB
C++
#include "iterators.hpp"
|
|
|
|
using namespace world::iterator;
|
|
|
|
std::unique_ptr<AbstractFill> world::iterator::Get(world::action::Shape shape, uint16_t radius) {
|
|
switch (shape) {
|
|
case world::action::Shape::Cube:
|
|
return std::make_unique<Cube>(radius);
|
|
case world::action::Shape::RawSphere:
|
|
return std::make_unique<RawSphere>(radius);
|
|
case world::action::Shape::SmoothSphere:
|
|
return std::make_unique<SmoothSphere>(radius);
|
|
default:
|
|
return std::unique_ptr<AbstractFill>(nullptr);
|
|
}
|
|
}
|
|
std::unique_ptr<AbstractBorder> world::iterator::GetBorder(world::action::Shape shape, uint16_t radius) {
|
|
switch (shape) {
|
|
case world::action::Shape::Cube:
|
|
return std::make_unique<CubeBorder>(radius);
|
|
case world::action::Shape::RawSphere:
|
|
case world::action::Shape::SmoothSphere:
|
|
return std::make_unique<SphereBorder>(radius);
|
|
default:
|
|
return std::unique_ptr<AbstractBorder>(nullptr);
|
|
}
|
|
}
|
|
|
|
bool Cube::next(pair& out) {
|
|
if (pos.z > radius)
|
|
return false;
|
|
|
|
out.first = pos;
|
|
out.second = 1;
|
|
|
|
// MAYBE: use linear idx to make branch less
|
|
if (pos.x < radius) {
|
|
pos.x++;
|
|
} else {
|
|
pos.x = -radius;
|
|
if (pos.y < radius) {
|
|
pos.y++;
|
|
} else {
|
|
pos.y = -radius;
|
|
pos.z++;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
bool CubeBorder::next(glm::ivec3& out) {
|
|
if (face > 5)
|
|
return false;
|
|
|
|
out = pos;
|
|
|
|
const auto getAxis = [&](glm::ivec3 &in, int offset) -> int& {
|
|
const auto v = (face / 2 + offset) % 3;
|
|
return v == 0 ? in.x : (v == 1 ? in.y : in.z);
|
|
};
|
|
const auto r0_max = radius - (face > 1);
|
|
if (auto& r0 = getAxis(pos, 0); r0 < r0_max) {
|
|
r0++;
|
|
} else {
|
|
r0 = -r0_max;
|
|
const auto r1_max = radius - (face > 3);
|
|
if (auto &r1 = getAxis(pos, 1); r1 < r1_max) {
|
|
r1++;
|
|
} else {
|
|
r1 = -r1_max;
|
|
getAxis(pos, 2) = -radius;
|
|
face++;
|
|
getAxis(pos, 2) = (face % 2 == 0) ? -radius : radius;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool RawSphere::next(pair& out) {
|
|
const auto delta = [](int radius, int pos) -> int {
|
|
const auto rad = radius + .5;
|
|
return floor(sqrt(rad * rad - pos * pos));
|
|
};
|
|
|
|
if (pos.z < dz) {
|
|
pos.z++;
|
|
} else {
|
|
if (pos.y < dy) {
|
|
pos.y++;
|
|
} else {
|
|
if (pos.x < radius) {
|
|
pos.x++;
|
|
dy = delta(radius, pos.x);
|
|
pos.y = -dy;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
dz = delta(dy, pos.y);
|
|
pos.z = -dz;
|
|
}
|
|
|
|
out.first = pos;
|
|
out.second = 1;
|
|
return true;
|
|
}
|
|
|
|
bool SmoothSphere::next(pair& out) {
|
|
const auto delta = [](double radius, int pos) {
|
|
return sqrt(radius * radius - pos * pos);
|
|
};
|
|
|
|
if (pos.z < dz) {
|
|
const int idz = floor(dz);
|
|
out.second = pos.z < idz ? 1 : dz - idz;
|
|
pos.z++;
|
|
} else {
|
|
if (pos.y < dy) {
|
|
pos.y++;
|
|
} else {
|
|
if (pos.x < radius) {
|
|
pos.x++;
|
|
dy = delta(radius, pos.x);
|
|
pos.y = -ceil(dy);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
dz = delta(dy, pos.y);
|
|
out.second = dz - floor(dz);
|
|
pos.z = -ceil(dz);
|
|
}
|
|
|
|
out.first = pos;
|
|
return true;
|
|
}
|
|
|
|
constexpr float PI = 3.14159;
|
|
bool SphereBorder::next(glm::ivec3& out) {
|
|
if (i >= max_i)
|
|
return false;
|
|
|
|
const float si = sin(PI * i / (float)max_i);
|
|
const float sj = sin(2 * PI * j / (float)max_j);
|
|
const float ci = cos(PI * i / (float)max_i);
|
|
const float cj = cos(2 * PI * j / (float)max_j);
|
|
out = glm::vec3(si * cj, si * sj, ci) * (radius + .5f);
|
|
|
|
if (j < max_j - 2) {
|
|
j++;
|
|
} else {
|
|
j = 0;
|
|
i++;
|
|
}
|
|
return true;
|
|
} |