#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 * * For example if g_gens contains {a, b, c, d} and sg_gens contains {b, d, a} then the result is {1, 3, 0} */ std::vector _recontext_gens( std::vector g_gens, std::vector sg_gens ) { auto orig = sg_gens; for (size_t &gen: sg_gens) { gen = std::distance( g_gens.begin(), std::find(g_gens.begin(), g_gens.end(), gen) ); } return 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(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(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; }