mirror of
https://github.com/allemangD/toddcox-visualize.git
synced 2025-11-10 12:02:47 -05:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f9f4e568b | ||
|
|
2d6dbc6804 | ||
|
|
a28cc2d8be | ||
| fbd23aea02 | |||
| a3233c2686 | |||
| c1f5163008 | |||
|
|
01043e9bce | ||
|
|
6e3ea1900b | ||
|
|
9ce626ee64 | ||
| 6ff09dc375 | |||
| 49927568e4 | |||
|
|
6b34694784 | ||
|
|
0534c4322c | ||
|
|
c164c319fc |
2
vendor/toddcox
vendored
2
vendor/toddcox
vendored
Submodule vendor/toddcox updated: 16c9d7d62f...265de59917
@@ -15,7 +15,7 @@ add_custom_command(
|
|||||||
add_definitions(${NANOGUI_EXTRA_DEFS})
|
add_definitions(${NANOGUI_EXTRA_DEFS})
|
||||||
include_directories(${NANOGUI_EXTRA_INCS})
|
include_directories(${NANOGUI_EXTRA_INCS})
|
||||||
|
|
||||||
add_executable(vis-gui src/main-gui.cpp)
|
add_executable(vis src/main.cpp)
|
||||||
target_include_directories(vis-gui PRIVATE include)
|
target_include_directories(vis PRIVATE include)
|
||||||
target_link_libraries(vis-gui PRIVATE tc nanogui yaml-cpp ${NANOGUI_EXTRA_LIBS})
|
target_link_libraries(vis PRIVATE tc nanogui yaml-cpp ${NANOGUI_EXTRA_LIBS})
|
||||||
add_dependencies(vis-gui shaders presets)
|
add_dependencies(vis shaders presets)
|
||||||
|
|||||||
@@ -53,8 +53,13 @@ namespace cgl {
|
|||||||
glNamedBufferData(id, sizeof(T), &data, usage);
|
glNamedBufferData(id, sizeof(T), &data, usage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void put(const std::vector<T> &data, GLenum usage = GL_STATIC_DRAW) {
|
void put(const T *data, const size_t &size, GLenum usage = GL_STATIC_DRAW) {
|
||||||
glNamedBufferData(id, sizeof(T) * data.size(), &data[0], usage);
|
glNamedBufferData(id, sizeof(T) * size, data, usage);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class E>
|
||||||
|
void put(const E &data, GLenum usage = GL_STATIC_DRAW) {
|
||||||
|
put(data.data(), data.size(), usage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bound(GLenum target, const std::function<void()> &action) const {
|
void bound(GLenum target, const std::function<void()> &action) const {
|
||||||
|
|||||||
53
vis/include/combinations.hpp
Normal file
53
vis/include/combinations.hpp
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
template<class V>
|
||||||
|
V select(const V &options, const std::vector<bool> &mask, size_t count) {
|
||||||
|
V result;
|
||||||
|
result.reserve(count);
|
||||||
|
|
||||||
|
for (int i = 0; i < mask.size(); ++i) {
|
||||||
|
if (mask[i]) result.push_back(options[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class V>
|
||||||
|
std::set<V> combinations(const V &options, size_t count) {
|
||||||
|
std::set<V> result;
|
||||||
|
|
||||||
|
std::vector<bool> mask(options.size(), false);
|
||||||
|
std::fill(mask.begin(), mask.begin() + count, true);
|
||||||
|
|
||||||
|
do {
|
||||||
|
result.insert(select(options, mask, count));
|
||||||
|
} while (std::next_permutation(mask.begin(), mask.end(), std::greater<>()));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class V>
|
||||||
|
std::set<V> set_difference(const std::set<V> &a, const std::set<V> &b) {
|
||||||
|
std::set<V> result;
|
||||||
|
std::set_difference(
|
||||||
|
a.begin(), a.end(),
|
||||||
|
b.begin(), b.end(),
|
||||||
|
std::inserter(result, result.end())
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class V>
|
||||||
|
std::set<V> set_union(const std::set<V> &a, const std::set<V> &b) {
|
||||||
|
std::set<V> result;
|
||||||
|
std::set_union(
|
||||||
|
a.begin(), a.end(),
|
||||||
|
b.begin(), b.end(),
|
||||||
|
std::inserter(result, result.end())
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <array>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <numeric>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
size_t choose(size_t n, size_t k) {
|
|
||||||
if (k == 0) return 1;
|
|
||||||
return n * choose(n - 1, k - 1) / k;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
class ComboIterator {
|
|
||||||
private:
|
|
||||||
const std::vector<T> &options;
|
|
||||||
|
|
||||||
std::vector<bool> bits;
|
|
||||||
std::vector<T> curr;
|
|
||||||
int at;
|
|
||||||
|
|
||||||
void set_curr() {
|
|
||||||
for (int i = 0, j = 0; i < bits.size(); ++i) {
|
|
||||||
if (bits[i]) curr[j++] = options[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
ComboIterator(const std::vector<T> &options, int k, int at = 0)
|
|
||||||
: options(options), bits(options.size()), curr(k), at(at) {
|
|
||||||
std::fill(bits.begin(), bits.begin() + k, true);
|
|
||||||
set_curr();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool operator==(const ComboIterator<T> &o) const {
|
|
||||||
return at == o.at;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool operator!=(const ComboIterator<T> &o) const {
|
|
||||||
return at != o.at;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto operator*() const {
|
|
||||||
return curr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto &operator->() const {
|
|
||||||
return &this;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto operator++(int) {
|
|
||||||
std::prev_permutation(bits.begin(), bits.end());
|
|
||||||
set_curr();
|
|
||||||
++at;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto operator++() &{
|
|
||||||
auto res = *this;
|
|
||||||
(*this)++;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto operator--(int) {
|
|
||||||
std::next_permutation(bits.begin(), bits.end());
|
|
||||||
set_curr();
|
|
||||||
--at;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto operator--() &{
|
|
||||||
auto res = *this;
|
|
||||||
(*this)--;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
class Combos {
|
|
||||||
private:
|
|
||||||
const std::vector<T> options;
|
|
||||||
int k;
|
|
||||||
int size;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Combos(const std::vector<T> &options, int k)
|
|
||||||
: options(options), k(k), size(choose(options.size(), k)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
ComboIterator<T> begin() const {
|
|
||||||
return ComboIterator<T>(options, k);
|
|
||||||
}
|
|
||||||
|
|
||||||
ComboIterator<T> end() const {
|
|
||||||
return ComboIterator<T>(options, k, size);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -5,32 +5,55 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "combo_iterator.hpp"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An primitive stage N indices.
|
|
||||||
* @tparam N
|
|
||||||
*/
|
|
||||||
template<unsigned N>
|
template<unsigned N>
|
||||||
struct Primitive {
|
using Prims = Eigen::Matrix<unsigned, N, Eigen::Dynamic>;
|
||||||
static_assert(N > 0, "Primitives must contain at least one point. Primitive<0> or lower is impossible.");
|
|
||||||
|
|
||||||
std::array<unsigned, N> inds;
|
template<int N>
|
||||||
|
using vec = Eigen::Matrix<float, N, 1>;
|
||||||
|
template<int N>
|
||||||
|
using mat = Eigen::Matrix<float, N, N>;
|
||||||
|
|
||||||
Primitive() = default;
|
using vec1 = vec<1>;
|
||||||
|
using vec2 = vec<2>;
|
||||||
|
using vec3 = vec<3>;
|
||||||
|
using vec4 = vec<4>;
|
||||||
|
using vec5 = vec<5>;
|
||||||
|
|
||||||
Primitive(const Primitive<N> &) = default;
|
using mat1 = mat<1>;
|
||||||
|
using mat2 = mat<2>;
|
||||||
|
using mat3 = mat<3>;
|
||||||
|
using mat4 = mat<4>;
|
||||||
|
using mat5 = mat<5>;
|
||||||
|
|
||||||
Primitive(const Primitive<N - 1> &sub, unsigned root) {
|
mat4 orthographic(float left, float right, float bottom, float top, float front, float back) {
|
||||||
std::copy(sub.inds.begin(), sub.inds.end(), inds.begin());
|
mat4 res = mat4();
|
||||||
inds[N - 1] = root;
|
res <<
|
||||||
}
|
2 / (right - left), 0, 0, -(right + left) / (right - left),
|
||||||
|
0, 2 / (top - bottom), 0, -(top + bottom) / (top - bottom),
|
||||||
|
0, 0, 2 / (front - back), -(front + back) / (front - back),
|
||||||
|
0, 0, 0, 1;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
~Primitive() = default;
|
mat4 perspective(float fovy, float aspect, float zNear, float zFar) {
|
||||||
|
float tanHalfFovy(std::tan(fovy / 2));
|
||||||
|
|
||||||
void apply(const tc::Cosets &table, int gen) {
|
mat4 res = mat4::Identity();
|
||||||
for (auto &ind : inds) {
|
res(0, 0) = 1 / (aspect * tanHalfFovy);
|
||||||
ind = table.get(ind, gen);
|
res(1, 1) = 1 / (tanHalfFovy);
|
||||||
}
|
res(2, 2) = -(zFar + zNear) / (zFar - zNear);
|
||||||
}
|
res(3, 2) = -1;
|
||||||
};
|
res(2, 3) = -(2 + zFar * zNear) / (zFar - zNear);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat4 translation(float x, float y, float z) {
|
||||||
|
mat4 res = mat4();
|
||||||
|
res <<
|
||||||
|
1, 0, 0, x,
|
||||||
|
0, 1, 0, y,
|
||||||
|
0, 0, 1, z,
|
||||||
|
0, 0, 0, 1;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,22 +8,7 @@
|
|||||||
|
|
||||||
#include <nanogui/glutil.h>
|
#include <nanogui/glutil.h>
|
||||||
|
|
||||||
template<int N>
|
#include <geometry.hpp>
|
||||||
using vec = Eigen::Matrix<float, N, 1>;
|
|
||||||
template<int N>
|
|
||||||
using mat = Eigen::Matrix<float, N, N>;
|
|
||||||
|
|
||||||
using vec1 = vec<1>;
|
|
||||||
using vec2 = vec<2>;
|
|
||||||
using vec3 = vec<3>;
|
|
||||||
using vec4 = vec<4>;
|
|
||||||
using vec5 = vec<5>;
|
|
||||||
|
|
||||||
using mat1 = mat<1>;
|
|
||||||
using mat2 = mat<2>;
|
|
||||||
using mat3 = mat<3>;
|
|
||||||
using mat4 = mat<4>;
|
|
||||||
using mat5 = mat<5>;
|
|
||||||
|
|
||||||
template<class V>
|
template<class V>
|
||||||
float dot(int n, const V &a, const V &b) {
|
float dot(int n, const V &a, const V &b) {
|
||||||
@@ -143,13 +128,3 @@ mat<N> rot(int u, int v, float theta) {
|
|||||||
res(v, v) = std::cos(theta);
|
res(v, v) = std::cos(theta);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat4 ortho(float left, float right, float bottom, float top, float front, float back) {
|
|
||||||
mat<4> res = mat4();
|
|
||||||
res <<
|
|
||||||
2 / (right - left), 0, 0, -(right + left) / (right - left),
|
|
||||||
0, 2 / (top - bottom), 0, -(top + bottom) / (top - bottom),
|
|
||||||
0, 0, 2 / (front - back), -(front + back) / (front - back),
|
|
||||||
0, 0, 0, 1;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
@@ -3,49 +3,110 @@
|
|||||||
#include <cgl/vertexarray.hpp>
|
#include <cgl/vertexarray.hpp>
|
||||||
#include <cgl/buffer.hpp>
|
#include <cgl/buffer.hpp>
|
||||||
#include <cgl/pipeline.hpp>
|
#include <cgl/pipeline.hpp>
|
||||||
|
|
||||||
#include <geometry.hpp>
|
#include <geometry.hpp>
|
||||||
|
#include <mirror.hpp>
|
||||||
|
#include <combinations.hpp>
|
||||||
|
|
||||||
#include <tuple>
|
struct Matrices {
|
||||||
|
mat4 proj = mat4::Identity();
|
||||||
|
mat4 view = mat4::Identity();
|
||||||
|
|
||||||
template<unsigned N, class... T>
|
Matrices() = default;
|
||||||
struct Prop {
|
|
||||||
|
Matrices(mat4 proj, mat4 view) : proj(std::move(proj)), view(std::move(view)) {}
|
||||||
|
|
||||||
|
static Matrices build(const nanogui::Screen &screen) {
|
||||||
|
auto aspect = (float) screen.width() / (float) screen.height();
|
||||||
|
auto pheight = 1.4f;
|
||||||
|
auto pwidth = aspect * pheight;
|
||||||
|
// auto proj = orthographic(-pwidth, pwidth, -pheight, pheight, -10.0f, 10.0f);
|
||||||
|
// auto proj = perspective(-pwidth, pwidth, pheight, -pheight, 10.0f, 0.01f);
|
||||||
|
auto proj = perspective(0.4, aspect, 0.1, 10.0);
|
||||||
|
|
||||||
|
auto view = translation(0, 0, -4);
|
||||||
|
|
||||||
|
return Matrices(proj, view);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class Renderer {
|
||||||
|
public:
|
||||||
|
virtual void draw(const T &prop) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<unsigned N>
|
||||||
|
class Slice {
|
||||||
|
private:
|
||||||
|
const tc::Group group;
|
||||||
|
|
||||||
|
public:
|
||||||
|
cgl::Buffer<unsigned> ibo;
|
||||||
|
cgl::Buffer<vec4> vbo;
|
||||||
cgl::VertexArray vao;
|
cgl::VertexArray vao;
|
||||||
std::tuple<cgl::Buffer<T>...> vbos;
|
|
||||||
cgl::Buffer<Primitive<N>> ibo;
|
vec5 root = vec5::Ones().normalized();
|
||||||
|
vec5 center = vec5::Zero();
|
||||||
|
mat5 transform = mat5::Identity();
|
||||||
|
|
||||||
|
vec3 color = vec3::Ones();
|
||||||
|
|
||||||
|
explicit Slice(const tc::Group &g) : group(g) {
|
||||||
|
vao.ipointer(0, ibo, 4, GL_UNSIGNED_INT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMesh(const Mesh<N> &mesh) {
|
||||||
|
ibo.put(mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPoints() {
|
||||||
|
auto cosets = group.solve();
|
||||||
|
auto mirrors = mirror<5>(group);
|
||||||
|
|
||||||
|
auto corners = plane_intersections(mirrors);
|
||||||
|
auto start = barycentric(corners, root);
|
||||||
|
|
||||||
|
auto higher = cosets.path.walk<vec5, vec5>(start, mirrors, reflect<vec5>);
|
||||||
|
|
||||||
|
std::transform(
|
||||||
|
higher.begin(), higher.end(), higher.begin(),
|
||||||
|
[&](const vec5& v) { return center + transform * v; }
|
||||||
|
);
|
||||||
|
|
||||||
|
std::vector<vec4> lower(higher.size());
|
||||||
|
std::transform(higher.begin(), higher.end(), lower.begin(), stereo<4>);
|
||||||
|
|
||||||
|
vbo.put(lower);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<unsigned N, class T>
|
template<unsigned N>
|
||||||
struct Renderer {
|
class SliceRenderer : public Renderer<Slice<N>> {
|
||||||
cgl::pipeline pipe;
|
private:
|
||||||
|
|
||||||
virtual void draw(const Prop<N, T> &prop) const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<unsigned N, class T>
|
|
||||||
struct SliceRenderer : public Renderer<N, T> {
|
|
||||||
cgl::pgm::vert defer = cgl::pgm::vert::file(
|
cgl::pgm::vert defer = cgl::pgm::vert::file(
|
||||||
"shaders/slice/deferred.vs.glsl");
|
"shaders/slice/deferred.vs.glsl");
|
||||||
cgl::pgm::geom slice = cgl::pgm::geom::file(
|
cgl::pgm::geom slice = cgl::pgm::geom::file(
|
||||||
"shaders/slice/slice.gm.glsl");
|
"shaders/slice/slice.gm.glsl");
|
||||||
cgl::pgm::frag solid = cgl::pgm::frag::file(
|
cgl::pgm::frag solid = cgl::pgm::frag::file(
|
||||||
"shaders/solid.fs.glsl");
|
"shaders/solid.fs.glsl");
|
||||||
|
|
||||||
cgl::pipeline pipe;
|
cgl::pipeline pipe;
|
||||||
|
|
||||||
|
cgl::Buffer<Matrices> ubo;
|
||||||
|
|
||||||
|
public:
|
||||||
SliceRenderer() {
|
SliceRenderer() {
|
||||||
pipe.stage(defer);
|
pipe.stage(defer);
|
||||||
pipe.stage(slice);
|
pipe.stage(slice);
|
||||||
pipe.stage(solid);
|
pipe.stage(solid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw(const Prop<N, T> &prop) const override {
|
void draw(const Slice<N> &prop) const {
|
||||||
pipe.bound([&]() {
|
glBindProgramPipeline(pipe);
|
||||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, std::get<0>(prop.vbos));
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, prop.vbo);
|
||||||
//// glProgramUniform3fv(solid, 2, 1, &prop.color.front());
|
glProgramUniform3fv(solid, 2, 1, prop.color.data());
|
||||||
glProgramUniform3f(solid, 2, 1.f, 1.f, 1.f);
|
glBindVertexArray(prop.vao);
|
||||||
prop.vao.bound([&]() {
|
glDrawArrays(GL_POINTS, 0, prop.ibo.count() * N);
|
||||||
glDrawArrays(GL_POINTS, 0, prop.ibo.count() * N);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,8 +7,9 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <geometry.hpp>
|
#include <geometry.hpp>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "combo_iterator.hpp"
|
#include <combinations.hpp>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce a list of all generators for the group context. The range [0..group.ngens).
|
* Produce a list of all generators for the group context. The range [0..group.ngens).
|
||||||
@@ -19,213 +20,237 @@ std::vector<int> generators(const tc::Group &context) {
|
|||||||
return g_gens;
|
return g_gens;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
namespace {
|
||||||
* Determine which of g_gens are the correct names for sg_gens within the current context
|
/**
|
||||||
*/
|
* Determine which of g_gens are the correct names for sg_gens within the current context
|
||||||
std::vector<int> recontext_gens(
|
*/
|
||||||
const tc::Group &context,
|
std::vector<int> recontext_gens(
|
||||||
std::vector<int> g_gens,
|
const tc::Group &context,
|
||||||
std::vector<int> sg_gens) {
|
std::vector<int> g_gens,
|
||||||
|
std::vector<int> sg_gens) {
|
||||||
|
|
||||||
std::sort(g_gens.begin(), g_gens.end());
|
std::sort(g_gens.begin(), g_gens.end());
|
||||||
|
|
||||||
int inv_gen_map[context.ngens];
|
int inv_gen_map[context.ngens];
|
||||||
for (size_t i = 0; i < g_gens.size(); i++) {
|
for (size_t i = 0; i < g_gens.size(); i++) {
|
||||||
inv_gen_map[g_gens[i]] = i;
|
inv_gen_map[g_gens[i]] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> s_sg_gens;
|
||||||
|
s_sg_gens.reserve(sg_gens.size());
|
||||||
|
for (const auto gen : sg_gens) {
|
||||||
|
s_sg_gens.push_back(inv_gen_map[gen]);
|
||||||
|
}
|
||||||
|
std::sort(s_sg_gens.begin(), s_sg_gens.end());
|
||||||
|
|
||||||
|
return s_sg_gens;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int> s_sg_gens;
|
/**
|
||||||
s_sg_gens.reserve(sg_gens.size());
|
* Solve the cosets generated by sg_gens within the subgroup generated by g_gens of the group context
|
||||||
for (const auto gen : sg_gens) {
|
*/
|
||||||
s_sg_gens.push_back(inv_gen_map[gen]);
|
tc::Cosets solve(
|
||||||
|
const tc::Group &context,
|
||||||
|
const std::vector<int> &g_gens,
|
||||||
|
const std::vector<int> &sg_gens
|
||||||
|
) {
|
||||||
|
const auto proper_sg_gens = recontext_gens(context, g_gens, sg_gens);
|
||||||
|
return context.subgroup(g_gens).solve(proper_sg_gens);
|
||||||
}
|
}
|
||||||
std::sort(s_sg_gens.begin(), s_sg_gens.end());
|
|
||||||
|
|
||||||
return s_sg_gens;
|
/**
|
||||||
|
* Apply some context transformation to all primitives of this mesh.
|
||||||
|
*/
|
||||||
|
template<unsigned N>
|
||||||
|
void apply(const tc::Cosets &table, int gen, Prims<N> &mat) {
|
||||||
|
auto data = mat.data();
|
||||||
|
for (int i = 0; i < mat.size(); ++i) {
|
||||||
|
data[i] = table.get(data[i], gen);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Solve the cosets generated by sg_gens within the subgroup generated by g_gens of the group context
|
|
||||||
*/
|
|
||||||
tc::Cosets solve(
|
|
||||||
const tc::Group &context,
|
|
||||||
const std::vector<int> &g_gens,
|
|
||||||
const std::vector<int> &sg_gens
|
|
||||||
) {
|
|
||||||
const auto proper_sg_gens = recontext_gens(context, g_gens, sg_gens);
|
|
||||||
return context.subgroup(g_gens).solve(proper_sg_gens);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply some context transformation to all primitives of this mesh.
|
|
||||||
*/
|
|
||||||
template<unsigned N>
|
template<unsigned N>
|
||||||
std::vector<Primitive<N>> apply(std::vector<Primitive<N>> prims, const tc::Cosets &table, int gen) {
|
class Mesh {
|
||||||
for (auto &prim : prims) {
|
public:
|
||||||
prim.apply(table, gen);
|
const tc::Group *g; // todo this needs to be handled more consistently
|
||||||
|
std::vector<int> ctx;
|
||||||
|
Prims<N> prims;
|
||||||
|
|
||||||
|
Mesh(const tc::Group &g_, std::vector<int> ctx_, size_t cols);
|
||||||
|
|
||||||
|
static Mesh<N> fill(const tc::Group &g, const std::vector<int> &ctx);
|
||||||
|
|
||||||
|
// template<class SC>
|
||||||
|
// static Mesh<N> hull(const tc::Group &g, const std::vector<int> &ctx, const SC &sub_ctxs);
|
||||||
|
|
||||||
|
Mesh<N> recontext(std::vector<int> ctx_);
|
||||||
|
|
||||||
|
Mesh<N> tile(const std::vector<int> &ctx_);
|
||||||
|
|
||||||
|
std::vector<Mesh<N>> each_tile(const std::vector<int> &ctx_);
|
||||||
|
|
||||||
|
Mesh<N + 1> fan(unsigned root);
|
||||||
|
|
||||||
|
[[nodiscard]] size_t size() const { return prims.size(); }
|
||||||
|
|
||||||
|
[[nodiscard]] size_t rows() const { return prims.rows(); }
|
||||||
|
|
||||||
|
[[nodiscard]] size_t cols() const { return prims.cols(); }
|
||||||
|
|
||||||
|
[[nodiscard]] unsigned *data() { return prims.data(); }
|
||||||
|
|
||||||
|
[[nodiscard]] const unsigned *data() const { return prims.data(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class M>
|
||||||
|
M merge(const std::vector<M> &meshes) {
|
||||||
|
if (meshes.empty()) throw std::logic_error("cannot merge an empty list of meshes");
|
||||||
|
|
||||||
|
auto g = meshes[0].g;
|
||||||
|
auto ctx = meshes[0].ctx;
|
||||||
|
|
||||||
|
size_t cols = 0;
|
||||||
|
for (const auto &mesh : meshes) {
|
||||||
|
cols += mesh.prims.cols();
|
||||||
}
|
}
|
||||||
return prims;
|
|
||||||
|
M res(*g, ctx, cols);
|
||||||
|
|
||||||
|
size_t offset = 0;
|
||||||
|
for (const auto &mesh : meshes) {
|
||||||
|
res.prims.middleCols(offset, mesh.prims.cols()) = mesh.prims;
|
||||||
|
offset += mesh.prims.cols();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert the indexes of this mesh to those of a different context, using g_gens to build the parent context and sg_gens to build this context.
|
|
||||||
*/
|
|
||||||
template<unsigned N>
|
template<unsigned N>
|
||||||
[[nodiscard]]
|
Mesh<N> Mesh<N>::recontext(std::vector<int> ctx_) {
|
||||||
std::vector<Primitive<N>> recontext(
|
Mesh<N> res = *this;
|
||||||
std::vector<Primitive<N>> prims,
|
res.ctx = ctx_;
|
||||||
const tc::Group &context,
|
|
||||||
const std::vector<int> &g_gens,
|
const auto proper_sg_gens = recontext_gens(*g, res.ctx, ctx);
|
||||||
const std::vector<int> &sg_gens
|
const auto table = solve(*g, res.ctx, {});
|
||||||
) {
|
const auto path = solve(*g, ctx, {}).path;
|
||||||
const auto proper_sg_gens = recontext_gens(context, g_gens, sg_gens);
|
|
||||||
const auto table = solve(context, g_gens, {});
|
|
||||||
const auto path = solve(context, sg_gens, {}).path;
|
|
||||||
|
|
||||||
auto map = path.template walk<int, int>(0, proper_sg_gens, [table](int coset, int gen) {
|
auto map = path.template walk<int, int>(0, proper_sg_gens, [table](int coset, int gen) {
|
||||||
return table.get(coset, gen);
|
return table.get(coset, gen);
|
||||||
});
|
});
|
||||||
|
|
||||||
std::vector<Primitive<N>> res(prims);
|
auto data = res.prims.data();
|
||||||
for (Primitive<N> &prim : res) {
|
for (int i = 0; i < res.prims.size(); ++i) {
|
||||||
for (auto &ind : prim.inds) {
|
data[i] = map[data[i]];
|
||||||
ind = map[ind];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Union several meshes of the same dimension
|
|
||||||
*/
|
|
||||||
template<unsigned N>
|
|
||||||
std::vector<Primitive<N>> merge(const std::vector<std::vector<Primitive<N>>> &meshes) {
|
|
||||||
size_t size = 0;
|
|
||||||
for (const auto &mesh : meshes) {
|
|
||||||
size += mesh.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Primitive<N>> res;
|
|
||||||
res.reserve(size);
|
|
||||||
for (const auto &mesh : meshes) {
|
|
||||||
res.insert(res.end(), mesh.begin(), mesh.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<unsigned N>
|
template<unsigned N>
|
||||||
[[nodiscard]]
|
Mesh<N> Mesh<N>::tile(const std::vector<int> &ctx_) {
|
||||||
std::vector<std::vector<Primitive<N>>> each_tile(
|
return merge(each_tile(ctx_));
|
||||||
std::vector<Primitive<N>> prims,
|
|
||||||
const tc::Group &context,
|
|
||||||
const std::vector<int> &g_gens,
|
|
||||||
const std::vector<int> &sg_gens
|
|
||||||
) {
|
|
||||||
std::vector<Primitive<N>> base = recontext(prims, context, g_gens, sg_gens);
|
|
||||||
const auto proper_sg_gens = recontext_gens(context, g_gens, sg_gens);
|
|
||||||
|
|
||||||
const auto table = solve(context, g_gens, {});
|
|
||||||
const auto path = solve(context, g_gens, sg_gens).path;
|
|
||||||
|
|
||||||
auto _gens = generators(context);
|
|
||||||
|
|
||||||
auto res = path.walk<std::vector<Primitive<N>>, int>(base, generators(context), [&](auto from, auto gen) {
|
|
||||||
return apply(from, table, gen);
|
|
||||||
});
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<unsigned N>
|
template<unsigned N>
|
||||||
[[nodiscard]]
|
std::vector<Mesh<N>> Mesh<N>::each_tile(const std::vector<int> &ctx_) {
|
||||||
std::vector<Primitive<N>> tile(
|
auto base = recontext(ctx_);
|
||||||
std::vector<Primitive<N>> prims,
|
|
||||||
const tc::Group &context,
|
|
||||||
const std::vector<int> &g_gens,
|
|
||||||
const std::vector<int> &sg_gens
|
|
||||||
) {
|
|
||||||
auto res = each_tile<N>(prims, context, g_gens, sg_gens);
|
|
||||||
|
|
||||||
return merge(res);
|
auto table = solve(*g, base.ctx, {});
|
||||||
}
|
auto path = solve(*g, base.ctx, ctx).path;
|
||||||
|
|
||||||
/**
|
std::vector<Mesh<N>> res = path.template walk<Mesh<N>, int>(
|
||||||
* Produce a mesh of higher dimension by fanning a single point to all primitives in this mesh.
|
base, generators(*g),
|
||||||
*/
|
[&](Mesh<N> mesh, int gen) {
|
||||||
template<unsigned N>
|
apply<N>(table, gen, mesh.prims);
|
||||||
[[nodiscard]]
|
return mesh;
|
||||||
std::vector<Primitive<N + 1>> fan(std::vector<Primitive<N>> prims, int root) {
|
|
||||||
std::vector<Primitive<N + 1>> res(prims.size());
|
|
||||||
std::transform(prims.begin(), prims.end(), res.begin(),
|
|
||||||
[root](const Primitive<N> &prim) {
|
|
||||||
return Primitive<N + 1>(prim, root);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Produce a mesh of primitives that fill out the volume of the subgroup generated by generators g_gens within the group context
|
|
||||||
*/
|
|
||||||
template<unsigned N>
|
template<unsigned N>
|
||||||
std::vector<Primitive<N>> triangulate(
|
Mesh<N + 1> Mesh<N>::fan(unsigned root) {
|
||||||
const tc::Group &context,
|
Mesh<N + 1> res(*g, ctx, prims.cols());
|
||||||
const std::vector<int> &g_gens
|
|
||||||
) {
|
|
||||||
if (g_gens.size() + 1 != N) // todo make static assert
|
|
||||||
throw std::logic_error("g_gens size must be one less than N");
|
|
||||||
|
|
||||||
const auto &combos = Combos(g_gens, g_gens.size() - 1);
|
res.prims.topRows(1) = Prims<1>::Constant(1, prims.cols(), root);
|
||||||
|
res.prims.bottomRows(N) = prims;
|
||||||
|
|
||||||
std::vector<std::vector<Primitive<N>>> meshes;
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto &sg_gens : combos) {
|
template<unsigned N>
|
||||||
auto base = triangulate<N - 1>(context, sg_gens);
|
Mesh<N>::Mesh(const tc::Group &g_, std::vector<int> ctx_, size_t cols)
|
||||||
auto raised = tile(base, context, g_gens, sg_gens);
|
: g(&g_), ctx(std::move(ctx_)) {
|
||||||
raised.erase(raised.begin(), raised.begin() + base.size());
|
prims.setZero(N, cols);
|
||||||
meshes.push_back(fan(raised, 0));
|
}
|
||||||
|
|
||||||
|
template<unsigned N>
|
||||||
|
Mesh<N> Mesh<N>::fill(const tc::Group &g, const std::vector<int> &ctx) {
|
||||||
|
if (ctx.size() + 1 != N)
|
||||||
|
throw std::logic_error("ctx size must be one less than N");
|
||||||
|
|
||||||
|
// const auto &combos = Combos(ctx, (int)ctx.size() - 1);
|
||||||
|
const auto &combos = combinations(ctx, (int) ctx.size() - 1);
|
||||||
|
|
||||||
|
std::vector<Mesh<N>> meshes;
|
||||||
|
|
||||||
|
for (const auto &sub_ctx : combos) {
|
||||||
|
auto base = Mesh<N - 1>::fill(g, sub_ctx);
|
||||||
|
auto parts = base.each_tile(ctx);
|
||||||
|
parts.erase(parts.begin(), parts.begin() + 1);
|
||||||
|
|
||||||
|
if (parts.empty()) continue;
|
||||||
|
auto raised = merge(parts);
|
||||||
|
|
||||||
|
auto fanned = raised.fan(0);
|
||||||
|
meshes.push_back(fanned);
|
||||||
}
|
}
|
||||||
|
|
||||||
return merge(meshes);
|
return merge(meshes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Single-index primitives should not be further triangulated.
|
|
||||||
*/
|
|
||||||
template<>
|
template<>
|
||||||
std::vector<Primitive<1>> triangulate(
|
Mesh<1> Mesh<1>::fill(const tc::Group &g, const std::vector<int> &ctx) {
|
||||||
const tc::Group &context,
|
if (not ctx.empty())
|
||||||
const std::vector<int> &g_gens
|
throw std::logic_error("ctx must be empty for a trivial Mesh.");
|
||||||
) {
|
|
||||||
if (not g_gens.empty()) // todo make static assert
|
|
||||||
throw std::logic_error("g_gens must be empty for a trivial Mesh");
|
|
||||||
|
|
||||||
std::vector<Primitive<1>> res;
|
return Mesh<1>(g, ctx, 1);
|
||||||
res.emplace_back();
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<unsigned N, class T>
|
template<unsigned N, class C, class SC>
|
||||||
auto hull(const tc::Group &group, T all_sg_gens, const std::vector<std::vector<int>> &exclude) {
|
Mesh<N> fill_each_tile_merge(const tc::Group &g, const C &ctx, const SC &sub_ctxs) {
|
||||||
std::vector<std::vector<Primitive<N>>> parts;
|
std::vector<Mesh<N>> parts;
|
||||||
auto g_gens = generators(group);
|
|
||||||
for (const std::vector<int> &sg_gens : all_sg_gens) {
|
|
||||||
bool excluded = false;
|
|
||||||
for (const auto &test : exclude) {
|
|
||||||
if (sg_gens == test) {
|
|
||||||
excluded = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (excluded) continue;
|
|
||||||
|
|
||||||
const auto &base = triangulate<N>(group, sg_gens);
|
for (const auto &sub_ctx : sub_ctxs) {
|
||||||
const auto &tiles = each_tile(base, group, g_gens, sg_gens);
|
auto root = Mesh<N>::fill(g, sub_ctx);
|
||||||
for (const auto &tile : tiles) {
|
auto faces = root.each_tile(ctx);
|
||||||
parts.push_back(tile);
|
parts.insert(parts.end(), faces.begin(), faces.end());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return parts;
|
|
||||||
|
return merge(parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<unsigned N, class SC>
|
||||||
|
Mesh<N> fill_each_tile_merge(const tc::Group &g, const SC &sub_ctxs) {
|
||||||
|
return fill_each_tile_merge<N>(g, generators(g), sub_ctxs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<unsigned N, class C, class SC>
|
||||||
|
Mesh<N> fill_each_recontext_merge(const tc::Group &g, const C &ctx, const SC &sub_ctxs) {
|
||||||
|
std::vector<Mesh<N>> parts;
|
||||||
|
|
||||||
|
for (const auto &sub_ctx : sub_ctxs) {
|
||||||
|
auto root = Mesh<N>::fill(g, sub_ctx);
|
||||||
|
auto face = root.recontext(ctx);
|
||||||
|
parts.insert(parts.end(), face);
|
||||||
|
}
|
||||||
|
|
||||||
|
return merge(parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<unsigned N, class SC>
|
||||||
|
Mesh<N> fill_each_recontext_merge(const tc::Group &g, const SC &sub_ctxs) {
|
||||||
|
return fill_each_recontext_merge<N>(g, generators(g), sub_ctxs);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ float unmix(float u, float v) {
|
|||||||
void emit(vec4 v) {
|
void emit(vec4 v) {
|
||||||
pos = v;
|
pos = v;
|
||||||
col = vCol[0];
|
col = vCol[0];
|
||||||
gl_Position = proj * vec4(v.xyz, 1);
|
gl_Position = proj * view * vec4(v.xyz, 1);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,189 +0,0 @@
|
|||||||
/*
|
|
||||||
src/example4.cpp -- C++ version of an example application that shows
|
|
||||||
how to use the OpenGL widget. For a Python implementation, see
|
|
||||||
'../python/example4.py'.
|
|
||||||
|
|
||||||
NanoGUI was developed by Wenzel Jakob <wenzel.jakob@epfl.ch>.
|
|
||||||
The widget drawing code is based on the NanoVG demo application
|
|
||||||
by Mikko Mononen.
|
|
||||||
|
|
||||||
All rights reserved. Use of this source code is governed by a
|
|
||||||
BSD-style license that can be found in the LICENSE.txt file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <nanogui/opengl.h>
|
|
||||||
#include <nanogui/nanogui.h>
|
|
||||||
#include <nanogui/glutil.h>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <geometry.hpp>
|
|
||||||
#include <solver.hpp>
|
|
||||||
#include <rendering.hpp>
|
|
||||||
#include <mirror.hpp>
|
|
||||||
#include <util.hpp>
|
|
||||||
#include <tc/groups.hpp>
|
|
||||||
|
|
||||||
struct Matrices {
|
|
||||||
mat4 proj = mat4::Identity();
|
|
||||||
mat4 view = mat4::Identity();
|
|
||||||
|
|
||||||
Matrices() = default;
|
|
||||||
|
|
||||||
Matrices(mat4 proj, mat4 view) : proj(std::move(proj)), view(std::move(view)) {}
|
|
||||||
|
|
||||||
static Matrices build(const nanogui::Screen &screen) {
|
|
||||||
auto aspect = (float) screen.width() / (float) screen.height();
|
|
||||||
auto pheight = 1.4f;
|
|
||||||
auto pwidth = aspect * pheight;
|
|
||||||
mat4 proj = ortho(-pwidth, pwidth, -pheight, pheight, -10.0f, 10.0f);
|
|
||||||
|
|
||||||
// if (!glfwGetKey(window, GLFW_KEY_LEFT_SHIFT)) {
|
|
||||||
// state.st += state.time_delta / 8;
|
|
||||||
// }
|
|
||||||
|
|
||||||
auto view = mat4::Identity();
|
|
||||||
return Matrices(proj, view);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class C>
|
|
||||||
std::vector<vec4> points(const tc::Group &group, const C &coords, const float time) {
|
|
||||||
auto cosets = group.solve();
|
|
||||||
auto mirrors = mirror<5>(group);
|
|
||||||
|
|
||||||
auto corners = plane_intersections(mirrors);
|
|
||||||
auto start = barycentric(corners, coords);
|
|
||||||
|
|
||||||
auto higher = cosets.path.walk<vec5, vec5>(start, mirrors, reflect<vec5>);
|
|
||||||
|
|
||||||
mat5 r = mat5::Identity();
|
|
||||||
r *= rot<5>(0, 2, time * .21f);
|
|
||||||
// r *= rot<5>(1, 4, time * .27f);
|
|
||||||
|
|
||||||
r *= rot<5>(0, 3, time * .17f);
|
|
||||||
r *= rot<5>(1, 3, time * .25f);
|
|
||||||
r *= rot<5>(2, 3, time * .12f);
|
|
||||||
|
|
||||||
std::transform(higher.begin(), higher.end(), higher.begin(), [&](vec5 v) { return r * v; });
|
|
||||||
|
|
||||||
std::vector<vec4> lower(higher.size());
|
|
||||||
std::transform(higher.begin(), higher.end(), lower.begin(), stereo<4>);
|
|
||||||
return lower;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int N, class T, class C>
|
|
||||||
Prop<4, vec4> make_slice(
|
|
||||||
const tc::Group &g,
|
|
||||||
const C &coords,
|
|
||||||
vec3 color,
|
|
||||||
T all_sg_gens,
|
|
||||||
const std::vector<std::vector<int>> &exclude
|
|
||||||
) {
|
|
||||||
Prop<N, vec4> res{};
|
|
||||||
|
|
||||||
// res.vbo.put(points(g, coords));
|
|
||||||
res.ibo.put(merge<N>(hull<N>(g, all_sg_gens, exclude)));
|
|
||||||
res.vao.ipointer(0, res.ibo, 4, GL_UNSIGNED_INT);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
class ExampleApplication : public nanogui::Screen {
|
|
||||||
public:
|
|
||||||
vec5 root;
|
|
||||||
std::unique_ptr<tc::Group> group;
|
|
||||||
std::unique_ptr<Prop<4, vec4>> prop;
|
|
||||||
std::unique_ptr<cgl::Buffer<Matrices>> ubo;
|
|
||||||
std::unique_ptr<SliceRenderer<4, vec4>> ren;
|
|
||||||
|
|
||||||
float glfw_time = 0;
|
|
||||||
float last_frame = 0;
|
|
||||||
float frame_time = 0;
|
|
||||||
float time = 0;
|
|
||||||
|
|
||||||
bool paused = false;
|
|
||||||
|
|
||||||
ExampleApplication() : nanogui::Screen(
|
|
||||||
Eigen::Vector2i(1920, 1080),
|
|
||||||
"Coset Visualization",
|
|
||||||
true, false,
|
|
||||||
8, 8, 24, 8,
|
|
||||||
4,
|
|
||||||
4, 5) {
|
|
||||||
using namespace nanogui;
|
|
||||||
|
|
||||||
Window *window = new Window(this, "Sample Window");
|
|
||||||
window->setPosition(Vector2i(15, 15));
|
|
||||||
window->setFixedWidth(250);
|
|
||||||
window->setLayout(new BoxLayout(Orientation::Vertical));
|
|
||||||
|
|
||||||
auto pause = new ToolButton(window, ENTYPO_ICON_CONTROLLER_PAUS);
|
|
||||||
pause->setFlags(Button::ToggleButton);
|
|
||||||
pause->setChangeCallback([&](bool value) { this->paused = value; });
|
|
||||||
|
|
||||||
performLayout();
|
|
||||||
|
|
||||||
std::cout << utilInfo();
|
|
||||||
|
|
||||||
std::vector<int> symbol = {5, 3, 3, 2};
|
|
||||||
root << .80, .02, .02, .02, .02;
|
|
||||||
|
|
||||||
group = std::make_unique<tc::Group>(tc::schlafli(symbol));
|
|
||||||
auto gens = generators(*group);
|
|
||||||
std::vector<std::vector<int>> exclude = {{0, 1, 2}};
|
|
||||||
auto combos = Combos<int>(gens, 3);
|
|
||||||
|
|
||||||
prop = std::make_unique<Prop<4, vec4>>(make_slice<4>(*group, root, {}, combos, exclude));
|
|
||||||
|
|
||||||
ubo = std::make_unique<cgl::Buffer<Matrices>>();
|
|
||||||
glBindBufferBase(GL_UNIFORM_BUFFER, 1, *ubo);
|
|
||||||
|
|
||||||
ren = std::make_unique<SliceRenderer<4, vec4>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
void drawContents() override {
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
|
||||||
|
|
||||||
glEnable(GL_BLEND);
|
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
||||||
|
|
||||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
||||||
|
|
||||||
glViewport(0, 0, width(), height());
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
||||||
|
|
||||||
glfw_time = (float) glfwGetTime();
|
|
||||||
frame_time = glfw_time - last_frame;
|
|
||||||
last_frame = glfw_time;
|
|
||||||
if (!paused) time += frame_time;
|
|
||||||
|
|
||||||
std::get<0>(prop->vbos).put(points(*group, root, time));
|
|
||||||
|
|
||||||
Matrices mats = Matrices::build(*this);
|
|
||||||
ubo->put(mats);
|
|
||||||
ren->draw(*prop);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int /* argc */, char ** /* argv */) {
|
|
||||||
try {
|
|
||||||
nanogui::init();
|
|
||||||
|
|
||||||
/* scoped variables */ {
|
|
||||||
nanogui::ref<ExampleApplication> app = new ExampleApplication();
|
|
||||||
app->drawAll();
|
|
||||||
app->setVisible(true);
|
|
||||||
nanogui::mainloop(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
nanogui::shutdown();
|
|
||||||
} catch (const std::runtime_error &e) {
|
|
||||||
std::string error_msg = std::string("Caught a fatal error: ") + std::string(e.what());
|
|
||||||
std::cerr << error_msg << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
278
vis/src/main.cpp
278
vis/src/main.cpp
@@ -1,196 +1,138 @@
|
|||||||
#include <glad/glad.h>
|
#include <nanogui/opengl.h>
|
||||||
#include <GLFW/glfw3.h>
|
#include <nanogui/nanogui.h>
|
||||||
|
#include <nanogui/glutil.h>
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <cmath>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <random>
|
#include <string>
|
||||||
|
|
||||||
#include <yaml-cpp/yaml.h>
|
|
||||||
|
|
||||||
|
#include <geometry.hpp>
|
||||||
|
#include <solver.hpp>
|
||||||
|
#include <rendering.hpp>
|
||||||
|
#include <mirror.hpp>
|
||||||
|
#include <util.hpp>
|
||||||
#include <tc/groups.hpp>
|
#include <tc/groups.hpp>
|
||||||
|
|
||||||
#include <cgl/vertexarray.hpp>
|
mat5 wander(float time) {
|
||||||
#include <cgl/shaderprogram.hpp>
|
mat5 r = mat5::Identity();
|
||||||
#include <cgl/pipeline.hpp>
|
r *= rot<5>(0, 2, time * .15f);
|
||||||
|
r *= rot<5>(1, 2, time * .13f);
|
||||||
|
r *= rot<5>(0, 1, time * .20f);
|
||||||
|
|
||||||
#include <util.hpp>
|
r *= rot<5>(0, 3, time * .17f);
|
||||||
#include <mirror.hpp>
|
r *= rot<5>(1, 3, time * .25f);
|
||||||
#include <rendering.hpp>
|
r *= rot<5>(2, 3, time * .12f);
|
||||||
#include <solver.hpp>
|
|
||||||
#include <geometry.hpp>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
// r *= rot<5>(1, 4, time * .27f);
|
||||||
extern "C" {
|
|
||||||
__attribute__((unused)) __declspec(dllexport) int NvOptimusEnablement = 0x00000001;
|
return r;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
struct Matrices {
|
class ExampleApplication : public nanogui::Screen {
|
||||||
mat4 proj;
|
public:
|
||||||
mat4 view;
|
std::unique_ptr<SliceRenderer<4>> ren;
|
||||||
|
std::unique_ptr<cgl::Buffer<Matrices>> ubo;
|
||||||
|
|
||||||
Matrices() = default;
|
std::vector<Slice<4>> slices;
|
||||||
|
|
||||||
Matrices(const mat4 &proj, const mat4 &view)
|
float glfw_time = 0;
|
||||||
: proj(proj), view(view) {
|
float last_frame = 0;
|
||||||
}
|
float frame_time = 0;
|
||||||
};
|
float time = 0;
|
||||||
|
|
||||||
struct State {
|
bool paused = false;
|
||||||
float time;
|
|
||||||
float time_delta;
|
|
||||||
|
|
||||||
float st;
|
ExampleApplication() : nanogui::Screen(
|
||||||
|
Eigen::Vector2i(1920, 1080),
|
||||||
|
"Coset Visualization",
|
||||||
|
true, false,
|
||||||
|
8, 8, 24, 8,
|
||||||
|
4,
|
||||||
|
4, 5) {
|
||||||
|
using namespace nanogui;
|
||||||
|
|
||||||
int dimension;
|
// auto *window = new Window(this, "Sample Window");
|
||||||
};
|
// window->setPosition(Vector2i(15, 15));
|
||||||
|
// window->setFixedWidth(250);
|
||||||
|
// window->setLayout(new BoxLayout(Orientation::Vertical));
|
||||||
|
|
||||||
Matrices build(GLFWwindow *window, State &state) {
|
// auto pause = new ToolButton(window, ENTYPO_ICON_CONTROLLER_PAUS);
|
||||||
int width, height;
|
// pause->setFlags(Button::ToggleButton);
|
||||||
glfwGetFramebufferSize(window, &width, &height);
|
// pause->setChangeCallback([&](bool value) { this->paused = value; });
|
||||||
|
|
||||||
auto aspect = (float) width / (float) height;
|
performLayout();
|
||||||
auto pheight = 1.4f;
|
|
||||||
auto pwidth = aspect * pheight;
|
|
||||||
mat4 proj = ortho(-pwidth, pwidth, -pheight, pheight, -10.0f, 10.0f);
|
|
||||||
|
|
||||||
if (!glfwGetKey(window, GLFW_KEY_LEFT_SHIFT)) {
|
std::cout << utilInfo();
|
||||||
state.st += state.time_delta / 8;
|
|
||||||
|
{
|
||||||
|
std::vector<int> symbol = {3, 4, 3, 2};
|
||||||
|
auto group = tc::schlafli(symbol);
|
||||||
|
auto ctx = generators(group);
|
||||||
|
auto selected_ctxs = set_difference(
|
||||||
|
combinations(ctx, 3),
|
||||||
|
{
|
||||||
|
{0, 1, 2},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
auto mesh = fill_each_tile_merge<4>(group, selected_ctxs);
|
||||||
|
|
||||||
|
auto &slice = slices.emplace_back(group);
|
||||||
|
slice.setMesh(mesh);
|
||||||
|
slice.root << .80, .02, .02, .02, .02;
|
||||||
|
}
|
||||||
|
|
||||||
|
ren = std::make_unique<SliceRenderer<4>>();
|
||||||
|
|
||||||
|
ubo = std::make_unique<cgl::Buffer<Matrices>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto view = identity<4>();
|
void drawContents() override {
|
||||||
return Matrices(proj, view);
|
glEnable(GL_DEPTH_TEST);
|
||||||
}
|
|
||||||
|
|
||||||
template<class C>
|
glEnable(GL_BLEND);
|
||||||
std::vector<vec4> points(const tc::Group &group, const C &coords, const float time) {
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
auto cosets = group.solve();
|
|
||||||
auto mirrors = mirror<5>(group);
|
|
||||||
|
|
||||||
auto corners = plane_intersections(mirrors);
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
auto start = barycentric(corners, coords);
|
|
||||||
|
|
||||||
auto higher = cosets.path.walk<vec5, vec5>(start, mirrors, reflect<vec5>);
|
|
||||||
|
|
||||||
auto r = identity<5>();
|
|
||||||
r = mul(r, rot<5>(0, 2, time * .21f));
|
|
||||||
r = mul(r, rot<5>(1, 4, time * .27f));
|
|
||||||
|
|
||||||
r = mul(r, rot<5>(0, 3, time * .17f));
|
|
||||||
r = mul(r, rot<5>(1, 3, time * .25f));
|
|
||||||
r = mul(r, rot<5>(2, 3, time * .12f));
|
|
||||||
|
|
||||||
std::transform(higher.begin(), higher.end(), higher.begin(), [&](vec5 v) { return mul(v, r); });
|
|
||||||
|
|
||||||
std::vector<vec4> lower(higher.size());
|
|
||||||
std::transform(higher.begin(), higher.end(), lower.begin(), stereo<4>);
|
|
||||||
return lower;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int N, class T, class C>
|
|
||||||
Prop<4, vec4> make_slice(
|
|
||||||
const tc::Group &g,
|
|
||||||
const C &coords,
|
|
||||||
vec3 color,
|
|
||||||
T all_sg_gens,
|
|
||||||
const std::vector<std::vector<int>> &exclude
|
|
||||||
) {
|
|
||||||
Prop<N, vec4> res{};
|
|
||||||
|
|
||||||
// res.vbo.put(points(g, coords));
|
|
||||||
res.ibo.put(merge<N>(hull<N>(g, all_sg_gens, exclude)));
|
|
||||||
res.vao.ipointer(0, res.ibo, 4, GL_UNSIGNED_INT);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void run(const std::string &config_file, GLFWwindow *window) {
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
|
||||||
|
|
||||||
glEnable(GL_BLEND);
|
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
||||||
|
|
||||||
std::vector<int> symbol = {4, 3, 3, 3};
|
|
||||||
vec5 root = {.80, .02, .02, .02, .02};
|
|
||||||
|
|
||||||
auto group = tc::schlafli(symbol);
|
|
||||||
auto gens = generators(group);
|
|
||||||
std::vector<std::vector<int>> exclude = {{0, 1, 2}};
|
|
||||||
auto combos = Combos<int>(gens, 3);
|
|
||||||
|
|
||||||
SliceRenderer<4, vec4> ren{};
|
|
||||||
Prop<4, vec4> prop = make_slice<4>(group, root, {}, combos, exclude);
|
|
||||||
|
|
||||||
State state{};
|
|
||||||
state.dimension = 4;
|
|
||||||
glfwSetWindowUserPointer(window, &state);
|
|
||||||
|
|
||||||
auto ubo = cgl::Buffer<Matrices>();
|
|
||||||
glBindBufferBase(GL_UNIFORM_BUFFER, 1, ubo);
|
|
||||||
|
|
||||||
while (!glfwWindowShouldClose(window)) {
|
|
||||||
auto time = (float) glfwGetTime();
|
|
||||||
state.time_delta = state.time - time;
|
|
||||||
state.time = time;
|
|
||||||
|
|
||||||
int width, height;
|
|
||||||
glfwGetFramebufferSize(window, &width, &height);
|
|
||||||
|
|
||||||
|
glViewport(0, 0, width(), height());
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
glLineWidth(1.5);
|
glfw_time = (float) glfwGetTime();
|
||||||
|
frame_time = glfw_time - last_frame;
|
||||||
|
last_frame = glfw_time;
|
||||||
|
if (!paused) time += frame_time;
|
||||||
|
|
||||||
std::get<0>(prop.vbos).put(points(group, root, time));
|
auto rotation = wander(time);
|
||||||
|
for (auto &slice : slices) {
|
||||||
|
slice.transform = rotation;
|
||||||
|
slice.setPoints();
|
||||||
|
}
|
||||||
|
|
||||||
Matrices mats{};
|
Matrices mats = Matrices::build(*this);
|
||||||
|
glBindBufferBase(GL_UNIFORM_BUFFER, 1, *ubo);
|
||||||
glViewport(0, 0, width, height);
|
ubo->put(mats);
|
||||||
mats = build(window, state);
|
for (const auto &slice : slices) {
|
||||||
ubo.put(mats);
|
ren->draw(slice);
|
||||||
ren.draw(prop);
|
}
|
||||||
|
|
||||||
glfwSwapInterval(2);
|
|
||||||
glfwSwapBuffers(window);
|
|
||||||
|
|
||||||
glfwPollEvents();
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char **argv) {
|
||||||
if (!glfwInit()) {
|
try {
|
||||||
std::cerr << "Failed to initialize GLFW" << std::endl;
|
nanogui::init();
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
/* scoped variables */ {
|
||||||
|
nanogui::ref<ExampleApplication> app = new ExampleApplication();
|
||||||
glfwWindowHint(GLFW_VERSION_MAJOR, 4);
|
app->drawAll();
|
||||||
glfwWindowHint(GLFW_VERSION_MAJOR, 5);
|
app->setVisible(true);
|
||||||
// glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
nanogui::mainloop(1);
|
||||||
|
}
|
||||||
auto window = glfwCreateWindow(
|
|
||||||
1920, 1080,
|
nanogui::shutdown();
|
||||||
"Coset Visualization",
|
} catch (const std::runtime_error &e) {
|
||||||
nullptr, nullptr);
|
std::string error_msg = std::string("Caught a fatal error: ") + std::string(e.what());
|
||||||
|
std::cerr << error_msg << std::endl;
|
||||||
if (!window) {
|
return -1;
|
||||||
std::cerr << "Failed to create window" << std::endl;
|
}
|
||||||
glfwTerminate();
|
|
||||||
exit(EXIT_FAILURE);
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
glfwMakeContextCurrent(window);
|
|
||||||
gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
|
|
||||||
glfwSwapInterval(1);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
glfwSwapBuffers(window);
|
|
||||||
|
|
||||||
std::cout << utilInfo();
|
|
||||||
|
|
||||||
std::string config_file = "presets/default.yaml";
|
|
||||||
if (argc > 1) config_file = std::string(argv[1]);
|
|
||||||
|
|
||||||
run(config_file, window);
|
|
||||||
|
|
||||||
glfwTerminate();
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user