#pragma once #include #include #include #include #include #include #include "combo_iterator.hpp" /** * Produce a list of all generators for the group context. The range [0..group.rank()). */ std::vector generators(const tc::Group &context) { std::vector g_gens(context.rank()); std::iota(g_gens.begin(), g_gens.end(), 0); return g_gens; } /** * Determine which of g_gens are the correct names for sg_gens within the current context */ std::vector recontext_gens(const tc::Group &context, std::vector g_gens, std::vector sg_gens) { std::sort(g_gens.begin(), g_gens.end()); int inv_gen_map[context.rank()]; for (size_t i = 0; i < g_gens.size(); i++) { inv_gen_map[g_gens[i]] = i; } std::vector 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; } /** * 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 [[nodiscard]] Indices recontext(Indices prims, const tc::Group &context, const std::vector &g_gens, const std::vector &sg_gens) { const auto proper_sg_gens = recontext_gens(context, g_gens, sg_gens); const auto table = context.sub(g_gens).solve({}); const auto cosets = context.sub(sg_gens).solve({}); tc::Path path(cosets, proper_sg_gens); std::vector map(path.order()); path.walk(0, [&table](size_t coset, size_t gen) { return table.get(coset, gen); }, map.begin()); Indices res(prims); auto data = res.data(); for (int i = 0; i < prims.size(); ++i) { data[i] = map[data[i]]; } return res; } /** * Union several meshes of the same dimension */ template Indices merge(const std::vector> &meshes) { size_t cols = 0; for (const auto &mesh: meshes) { cols += mesh.cols(); } Indices res(N, cols); size_t offset = 0; for (const Indices &mesh: meshes) { res.middleCols(offset, mesh.cols()) = mesh; offset += mesh.cols(); } return res; } template [[nodiscard]] std::vector> tile(Indices prims, const tc::Group &context, const std::vector &g_gens, const std::vector &sg_gens) { Indices base = recontext(prims, context, g_gens, sg_gens); const auto proper_sg_gens = recontext_gens(context, g_gens, sg_gens); const auto &table = context.sub(g_gens).solve({}); const auto &cosets = context.sub(g_gens).solve(proper_sg_gens); tc::Path<> path(cosets); std::vector> res(path.order()); path.walk(base, [&](Indices from, auto gen) { for (int i = 0; i < from.size(); ++i) { from(i) = table.get(from(i), gen); } return from; }, res.begin()); return res; } /** * Produce a mesh of higher dimension by fanning a single point to all primitives in this mesh. */ template [[nodiscard]] Indices fan(Indices prims, int root) { Indices res(N + 1, prims.cols()); res.topRows(1) = Indices<1>::Constant(1, prims.cols(), root); res.bottomRows(N) = prims; 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 Indices triangulate(const tc::Group &context, const std::vector &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 = combinations(g_gens, g_gens.size() - 1); std::vector> meshes; for (const auto &sg_gens: combos) { auto base = triangulate(context, sg_gens); auto parts = tile(base, context, g_gens, sg_gens); parts.erase(parts.begin(), parts.begin() + 1); auto raised = merge(parts); auto fanned = fan(raised, 0); meshes.push_back(fanned); } return merge(meshes); } /** * Single-index primitives should not be further triangulated. */ template<> Indices<1> triangulate<1>(const tc::Group &context, const std::vector &g_gens) { if (not g_gens.empty()) // todo make static assert throw std::logic_error("g_gens must be empty for a trivial Mesh"); return Indices<1>::Zero(1, 1); } template auto hull(const tc::Group &group, T all_sg_gens, const std::vector> &exclude) { std::vector> parts; auto g_gens = generators(group); for (const std::vector &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(group, sg_gens); const auto &tiles = tile(base, group, g_gens, sg_gens); for (const auto &tile: tiles) { parts.push_back(tile); } } return parts; }