1
0
Fork 0
Univerxel/src/core/world/iterators.cpp

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;
}