forked from mirror/toddcox-faster
171 lines
4.9 KiB
C++
171 lines
4.9 KiB
C++
#pragma once
|
|
|
|
#include <tc/group.hpp>
|
|
#include <tc/solver.hpp>
|
|
#include <cmath>
|
|
#include <optional>
|
|
#include <numeric>
|
|
#include <iostream>
|
|
#include <utility>
|
|
|
|
namespace tc {
|
|
std::vector<Symbol> combinations(const Symbol &symbol, size_t srank) {
|
|
size_t rank = symbol.size();
|
|
|
|
std::vector<bool> mask(rank, false);
|
|
std::fill_n(mask.begin(), srank, true);
|
|
|
|
std::vector<Symbol> combos;
|
|
combos.reserve(choose(rank, srank));
|
|
|
|
Symbol row(srank);
|
|
do {
|
|
for (int j = 0, k = 0; j < rank; ++j) {
|
|
if (mask[j]) {
|
|
row(k++) = symbol(j);
|
|
}
|
|
}
|
|
combos.emplace_back(row);
|
|
} while (std::prev_permutation(mask.begin(), mask.end()));
|
|
|
|
return combos;
|
|
}
|
|
|
|
// todo remove
|
|
ArrayXui fan(const ArrayXui &prims, int root) {
|
|
ArrayXui res(prims.rows() + 1, prims.cols());
|
|
res.topRows(prims.rows()) << prims;
|
|
res.bottomRows(1).fill(root);
|
|
return res;
|
|
}
|
|
|
|
// todo visitor
|
|
void apply(const tc::Cosets &table, unsigned int gen, ArrayXui &prims) {
|
|
for (Eigen::Index i = 0; i < prims.size(); ++i) {
|
|
prims(i) = table.get(prims(i), gen);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
void recontext(
|
|
ArrayXui &prims,
|
|
const tc::Group &context,
|
|
const Symbol &g_gens,
|
|
const Symbol &sg_gens
|
|
) {
|
|
const auto proper_sg_gens = recontext_gens(context.rank(), g_gens, sg_gens);
|
|
|
|
const auto table = solve(context, g_gens, Symbol(0));
|
|
const auto path = solve(context, sg_gens, Symbol(0)).path();
|
|
|
|
auto map = path.walk(0U, proper_sg_gens, [&table](auto coset, auto gen) {
|
|
return table.get(coset, gen);
|
|
});
|
|
|
|
// todo visitor
|
|
for (Eigen::Index i = 0; i < prims.size(); ++i) {
|
|
prims(i) = map[prims(i)];
|
|
}
|
|
}
|
|
|
|
// todo remove
|
|
ArrayXui merge(const std::vector<ArrayXui> &meshes) {
|
|
Eigen::Index cols = 0;
|
|
for (const auto &mesh: meshes) {
|
|
cols += mesh.cols();
|
|
}
|
|
|
|
ArrayXui res(meshes[0].rows(), cols);
|
|
Eigen::Index offset = 0;
|
|
for (const auto &mesh: meshes) {
|
|
res.middleCols(offset, mesh.cols()) << mesh;
|
|
offset += mesh.cols();
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
std::vector<ArrayXui> each_tile(
|
|
ArrayXui base,
|
|
const tc::Group &context,
|
|
const Symbol &g_gens,
|
|
const Symbol &sg_gens
|
|
) {
|
|
recontext(base, context, g_gens, sg_gens);
|
|
|
|
const auto table = solve(context, g_gens, Symbol(0));
|
|
const auto path = solve(context, g_gens, sg_gens).path();
|
|
|
|
auto _gens = context.gens;
|
|
|
|
auto res = path.walk(base, _gens, [&table](auto from, auto &gen) {
|
|
apply(table, gen, from);
|
|
return from;
|
|
});
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Produce a mesh of primitives that fill out the volume of the subgroup generated by generators g_gens within the group context
|
|
*/
|
|
ArrayXui triangulate(
|
|
const tc::Group &context,
|
|
const Symbol &g_gens
|
|
) {
|
|
if (g_gens.size() == 0) {
|
|
return ArrayXui::Zero(1, 1);
|
|
}
|
|
|
|
const auto &combos = combinations(g_gens, g_gens.size() - 1);
|
|
|
|
std::vector<ArrayXui> meshes;
|
|
|
|
// todo inline logic
|
|
// erase, merge, and fan can be inlined
|
|
// for 1..#tiles
|
|
// cols += cols
|
|
// parts.append(cols)
|
|
// result(rows, cols)
|
|
// for 1..#parts
|
|
// result.middlecols << part
|
|
// result.bottomrow.fill(0)
|
|
|
|
// todo subgroup/coset metadata
|
|
// would be good to also output which coset of which subgroup each primitive is a part of.
|
|
// this could be used in shaders etc to make rendering things easier
|
|
|
|
for (const auto &sg_gens: combos) {
|
|
auto base = triangulate(context, sg_gens);
|
|
auto tiles = each_tile(base, context, g_gens, sg_gens);
|
|
tiles.erase(tiles.begin(), tiles.begin() + 1);
|
|
auto raised = merge(tiles);
|
|
auto fanned = fan(raised, 0);
|
|
meshes.push_back(fanned);
|
|
}
|
|
|
|
const ArrayXui &result = merge(meshes);
|
|
return result;
|
|
}
|
|
|
|
template<class T>
|
|
auto hull(const tc::Group &group, T all_sg_gens) {
|
|
std::vector<ArrayXui> parts;
|
|
auto g_gens = group.gens;
|
|
|
|
// todo inline logic
|
|
// should be able to inline in a similar way as is possible in triangulate
|
|
|
|
for (const Symbol &sg_gens: all_sg_gens) {
|
|
const auto &base = triangulate(group, sg_gens);
|
|
const auto &tiles = each_tile(base, group, g_gens, sg_gens);
|
|
for (const auto &tile: tiles) {
|
|
parts.push_back(tile);
|
|
}
|
|
}
|
|
return parts;
|
|
}
|
|
}
|