#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.ngens). */ std::vector generators(const tc::Group &context) { std::vector g_gens(context.ngens); 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.ngens]; 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; } /** * 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 &g_gens, const std::vector &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 void apply(const tc::Cosets &table, int gen, Prims &mat) { auto data = mat.data(); for (int i = 0; i < mat.size(); ++i) { data[i] = table.get(data[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. */ template [[nodiscard]] Prims recontext( Prims 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 = solve(context, g_gens, {}); const auto path = solve(context, sg_gens, {}).path; auto map = path.template walk(0, proper_sg_gens, [table](int coset, int gen) { return table.get(coset, gen); }); Prims 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 Prims merge(const std::vector> &meshes) { size_t cols = 0; for (const auto &mesh : meshes) { cols += mesh.cols(); } Prims res(N, cols); size_t offset = 0; for (const Prims &mesh : meshes) { res.middleCols(offset, mesh.cols()) = mesh; offset += mesh.cols(); } return res; } template [[nodiscard]] std::vector> tile( Prims prims, const tc::Group &context, const std::vector &g_gens, const std::vector &sg_gens ) { Prims 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; std::vector _gens = generators(context); std::vector> res = path.walk, int>( base, _gens, [&](Prims from, int gen) { apply(table, gen, from); return from; } ); return res; } /** * Produce a mesh of higher dimension by fanning a single point to all primitives in this mesh. */ template [[nodiscard]] Prims fan(Prims prims, int root) { Prims res(N + 1, prims.cols()); res.topRows(1) = Prims<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 Prims 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 = Combos(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<> Prims<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 Prims<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; }