diff --git a/.gitmodules b/.gitmodules
index 18e4562..ecc2720 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,3 +4,6 @@
[submodule "vendor/glm"]
path = vendor/glm
url = https://github.com/g-truc/glm.git
+[submodule "vendor/toddcox"]
+ path = vendor/toddcox
+ url = https://github.com/JCRaymond/toddcox-faster.git
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..e8c6c13
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/toddcox-visualize.iml b/.idea/toddcox-visualize.iml
new file mode 100644
index 0000000..f08604b
--- /dev/null
+++ b/.idea/toddcox-visualize.iml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1d43078..505fccf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,10 +3,11 @@ project(toddcox-faster)
set(CMAKE_CXX_STANDARD 17)
+add_subdirectory(vendor/toddcox)
add_subdirectory(vendor/glad)
add_subdirectory(vendor/glfw)
add_subdirectory(vendor/glm)
-add_subdirectory(tc)
add_subdirectory(vis)
-add_subdirectory(example)
+
+add_subdirectory(examples)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..10a684a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,9 @@
+# toddcox-visualize
+A new (basic) implementation of the Todd-Coxeter algorithm for Coxeter groups in C++ that beats GAP.
+
+---
+
+Example output with group F4.
+
+
+
diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt
deleted file mode 100644
index 8d87503..0000000
--- a/example/CMakeLists.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-add_executable(bench bench.cpp)
-target_link_libraries(bench PRIVATE tc)
-
-add_executable(path path.cpp)
-target_link_libraries(path PRIVATE tc)
diff --git a/example/bench.cpp b/example/bench.cpp
deleted file mode 100644
index 51e7a98..0000000
--- a/example/bench.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#include "tc/solver.h"
-#include "tc/groups.h"
-
-#include
-#include
-
-int main() {
- std::vector groups = {
- tc::group::H(2),
- tc::group::H(3),
- tc::group::H(4),
- tc::group::T(100),
- tc::group::T(500),
- tc::group::T(1000),
- tc::group::E(6),
- tc::group::E(7),
- };
-
- for (const auto &group : groups) {
- auto s = std::clock(); // to measure CPU time
- auto cosets = tc::solve(group);
- auto e = std::clock();
-
- double diff = (double) (e - s) / CLOCKS_PER_SEC;
- int order = cosets.len;
-
- std::cout << group.name << "," << order << "," << diff << std::endl;
- }
-
- return 0;
-}
diff --git a/example/path.cpp b/example/path.cpp
deleted file mode 100644
index 53029c0..0000000
--- a/example/path.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-#include "tc/solver.h"
-#include "tc/groups.h"
-
-#include
-#include
-
-int main() {
- auto cube = tc::group::B(3);
- auto vars = tc::solve(cube, {});
-
- for (int target = 1; target < vars.len; target++) {
- auto &action = vars.path[target];
- std::cout << action.coset << " " << action.gen << " " << target << std::endl;
- }
-
- return 0;
-}
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 0000000..8f8485a
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_executable(memo memotest.cpp)
+target_link_libraries(memo PRIVATE tc vis-util)
+
+add_executable(geom geomtest.cpp)
+target_link_libraries(geom PRIVATE tc vis-util)
+
+add_executable(sub subtest.cpp)
+target_link_libraries(sub PRIVATE vis-util)
diff --git a/examples/geomtest.cpp b/examples/geomtest.cpp
new file mode 100644
index 0000000..b3c0a9f
--- /dev/null
+++ b/examples/geomtest.cpp
@@ -0,0 +1,46 @@
+//
+// Created by raymo on 1/20/2020.
+//
+
+#include
+#include
+#include
+
+int main() {
+ auto g = tc::schlafli({3, 2});
+ GeomGen gg(g);
+
+ auto path = gg.solve().path;
+
+ //std::vector = {"a", "b", "c"};
+ std::string base = "";
+ auto words = path.walk(base, {"a", "b", "c"}, [](auto s1, auto g) { return s1 + g; });
+ for (const auto word : words) {
+ std::cout << word << std::endl;
+ }
+
+ std::vector gens = {0, 1, 2};
+ auto s = gg.triangulate(gens);
+ s.print();
+ return 0;
+
+ auto g_gens = gg.group_gens();
+ std::vector sg_gens = {1, 2};
+ auto ns = gg.tile(g_gens, sg_gens, s);
+
+ std::cout << "Before: " << std::endl;
+ std::cout << '\t';
+ for (int val : s.vals) {
+ std::cout << val << " ";
+ }
+ std::cout << std::endl;
+
+ std::cout << " After: " << std::endl;
+ std::cout << '\t';
+ for (int val : ns.vals) {
+ std::cout << val << " ";
+ }
+ std::cout << std::endl;
+
+ return 0;
+}
\ No newline at end of file
diff --git a/examples/memotest.cpp b/examples/memotest.cpp
new file mode 100644
index 0000000..15b93ae
--- /dev/null
+++ b/examples/memotest.cpp
@@ -0,0 +1,70 @@
+#include
+#include
+#include
+#include
+
+int main() {
+ tc::Group g = tc::group::B(3);
+ GeomGen m(g);
+
+ m.solve({}, {});
+ m.solve({0}, {});
+ m.solve({0}, {0});
+ m.solve({1}, {});
+ m.solve({1}, {1});
+ m.solve({2}, {});
+ m.solve({2}, {2});
+ m.solve({0, 1}, {});
+ m.solve({0, 1}, {0});
+ m.solve({0, 1}, {1});
+ m.solve({0, 1}, {0, 1});
+ m.solve({1, 2}, {});
+ m.solve({1, 2}, {1});
+ m.solve({1, 2}, {2});
+ m.solve({1, 2}, {1, 2});
+ m.solve({0, 2}, {});
+ m.solve({0, 2}, {0});
+ m.solve({0, 2}, {2});
+ m.solve({0, 2}, {0, 2});
+ m.solve({0, 1, 2}, {});
+ m.solve({0, 1, 2}, {0});
+ m.solve({0, 1, 2}, {1});
+ m.solve({0, 1, 2}, {2});
+ m.solve({0, 1, 2}, {0, 1});
+ m.solve({0, 1, 2}, {1, 2});
+ m.solve({0, 1, 2}, {0, 2});
+ m.solve({0, 1, 2}, {0, 1, 2});
+
+ tc::Group big = tc::group::B(8);
+ GeomGen mbig(big);
+
+ auto s1 = std::chrono::system_clock::now();
+ auto res1 = mbig.solve({0, 1, 2, 3, 4, 7}, {2, 4, 7});
+ auto e1 = std::chrono::system_clock::now();
+
+ std::chrono::duration t1 = e1 - s1;
+ std::cout << t1.count() << ": " << res1.size() << std::endl;
+
+ auto s2 = std::chrono::system_clock::now();
+ auto res2 = mbig.solve({0, 2, 4, 7, 1, 3}, {4, 7, 2});
+ auto e2 = std::chrono::system_clock::now();
+
+ std::chrono::duration t2 = e2 - s2;
+ std::cout << t2.count() << ": " << res2.size() << std::endl;
+
+ std::vector gens = {0, 1, 2, 3, 4, 5};
+ auto s3 = std::chrono::system_clock::now();
+ auto res3 = mbig.triangulate(gens);
+ auto e3 = std::chrono::system_clock::now();
+
+ std::chrono::duration t3 = e3 - s3;
+ std::cout << t3.count() << ": " << res3.size() << std::endl;
+
+ auto s4 = std::chrono::system_clock::now();
+ auto res4 = mbig.triangulate(gens);
+ auto e4 = std::chrono::system_clock::now();
+
+ std::chrono::duration t4 = e4 - s4;
+ std::cout << t4.count() << ": " << res4.size() << std::endl;
+
+}
diff --git a/examples/subtest.cpp b/examples/subtest.cpp
new file mode 100644
index 0000000..76f6a68
--- /dev/null
+++ b/examples/subtest.cpp
@@ -0,0 +1,24 @@
+#include
+#include
+
+#include
+#include
+
+template
+std::ostream &operator<<(std::ostream &o, const std::vector &v) {
+ for (const auto &e : v) o << e << " ";
+ return o;
+}
+
+int main() {
+ std::vector gens(5);
+ std::iota(gens.begin(), gens.end(), 0);
+
+ const Combos &combos = Combos(gens, 2);
+
+ for (const auto &e : combos) {
+ std::cout << e << std::endl;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/screen.png b/screen.png
new file mode 100644
index 0000000..61a5d8f
Binary files /dev/null and b/screen.png differ
diff --git a/tc/CMakeLists.txt b/tc/CMakeLists.txt
deleted file mode 100644
index b7ef511..0000000
--- a/tc/CMakeLists.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-add_library(tc STATIC
- src/groups.cpp
- src/solver.cpp
- src/cosets.cpp)
-
-target_include_directories(tc PUBLIC include)
diff --git a/tc/include/tc/cosets.h b/tc/include/tc/cosets.h
deleted file mode 100644
index 552e330..0000000
--- a/tc/include/tc/cosets.h
+++ /dev/null
@@ -1,33 +0,0 @@
-#pragma once
-
-#include "groups.h"
-#include
-
-namespace tc {
- struct Action {
- int coset = -1;
- int gen = -1;
- int target = -1;
- };
-
- struct Cosets {
- int ngens;
- std::vector data;
- std::vector path;
- int len;
-
- explicit Cosets(int ngens);
-
- void add_row();
-
- void put(int coset, int gen, int target);
-
- void put(int idx, int target);
-
- [[nodiscard]] int get(int coset, int gen) const;
-
- [[nodiscard]] int get(int idx) const;
-
- [[nodiscard]] int size() const;
- };
-}
\ No newline at end of file
diff --git a/tc/include/tc/groups.h b/tc/include/tc/groups.h
deleted file mode 100644
index 5bfad37..0000000
--- a/tc/include/tc/groups.h
+++ /dev/null
@@ -1,96 +0,0 @@
-#pragma once
-
-#include
-#include
-
-namespace tc {
- struct Rel {
- std::array gens;
- int mult;
- };
-
- /**
- * A presentation of a coxeter group. Contains a number of generators and some relations of the form (ab)^n = e
- */
- struct Group {
- const int ngens;
- std::vector> _mults; // lookup table for multiplicities
- std::string name;
-
- explicit Group(int ngens, const std::vector &rels = {}, std::string name = "G");
-
- void setmult(Rel rel);
-
- [[nodiscard]] Rel rel(int a, int b) const;
-
- [[nodiscard]] std::vector get_rels() const;
-
- [[nodiscard]] Group product(const Group &other) const;
-
- [[nodiscard]] Group power(int p) const;
-
- [[nodiscard]] bool trivial() const;
- };
-
- Group operator*(const Group &g, const Group &h);
-
- Group operator^(const Group &g, const int &p);
-
- /**
- * Construct a group from a (simplified) Schlafli Symbol of the form [a, b, ..., c]
- * @param mults: The sequence of multiplicites between adjacent generators.
- */
- Group schlafli(const std::vector &mults);
-
- namespace group {
- /**
- * Simplex
- */
- Group A(int dim);
-
- /**
- * Cube, Orthoplex
- */
- Group B(int dim);
-
- /**
- * Demicube, Orthoplex
- */
- Group D(int dim);
-
- /**
- * E groups
- */
- Group E(int dim);
-
- /**
- * 24 Cell
- */
- Group F4();
-
- /**
- * Hexagon
- */
- Group G2();
-
- /**
- * Icosahedron
- */
- Group H(int dim);
-
- /**
- * Polygonal
- */
- Group I2(int n);
-
- /**
- * Toroidal. I2(n) * I2(m)
- */
- Group T(int n, int m);
-
- /**
- * Toroidal. T(n, n)
- */
- Group T(int n);
- }
-}
\ No newline at end of file
diff --git a/tc/include/tc/solver.h b/tc/include/tc/solver.h
deleted file mode 100644
index d4f8424..0000000
--- a/tc/include/tc/solver.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#pragma once
-
-#include "cosets.h"
-
-namespace tc {
- Cosets solve(const Group &g, const std::vector &sub_gens = {});
-}
diff --git a/tc/src/cosets.cpp b/tc/src/cosets.cpp
deleted file mode 100644
index 5815fa9..0000000
--- a/tc/src/cosets.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-#include "tc/cosets.h"
-
-namespace tc {
- Cosets::Cosets(int ngens) : ngens(ngens), len(0) {
- }
-
- void Cosets::add_row() {
- len++;
- data.resize(data.size() + ngens, -1);
- path.resize(path.size() + 1);
- }
-
- void Cosets::put(int coset, int gen, int target) {
- data[coset * ngens + gen] = target;
- data[target * ngens + gen] = coset;
-
- if (path[target].coset == -1) {
- path[target] = {coset, gen, target};
- }
- }
-
- void Cosets::put(int idx, int target) {
- int coset = idx / ngens;
- int gen = idx % ngens;
- data[idx] = target;
- data[target * ngens + gen] = coset;
-
- if (path[target].coset == -1) {
- path[target] = {coset, gen, target};
- }
- }
-
- int Cosets::get(int coset, int gen) const {
- return data[coset * ngens + gen];
- }
-
- int Cosets::get(int idx) const {
- return data[idx];
- }
-
- int Cosets::size() const {
- return len;
- }
-}
\ No newline at end of file
diff --git a/tc/src/groups.cpp b/tc/src/groups.cpp
deleted file mode 100644
index b1896d4..0000000
--- a/tc/src/groups.cpp
+++ /dev/null
@@ -1,207 +0,0 @@
-#include "tc/groups.h"
-
-#include
-#include
-
-namespace tc {
- Group::Group(int ngens, const std::vector &rels, std::string name) : ngens(ngens), name(std::move(name)) {
- _mults.resize(ngens);
- for (int i = 0; i < ngens; i++) {
- _mults[i].resize(ngens, 2);
- }
-
- for (Rel rel : rels) {
- setmult(rel);
- }
- }
-
- void Group::setmult(Rel rel) {
- _mults[rel.gens[0]][rel.gens[1]] = rel.mult;
- _mults[rel.gens[1]][rel.gens[0]] = rel.mult;
- }
-
- Rel Group::rel(int a, int b) const {
- return {a, b, _mults[a][b]};
- }
-
- std::vector Group::get_rels() const {
- std::vector rels;
- for (int i = 0; i < ngens - 1; i++) {
- for (int j = i + 1; j < ngens; j++) {
- rels.push_back({i, j, _mults[i][j]});
- }
- }
- return rels;
- }
-
- Group Group::product(const Group &other) const {
- int off = ngens;
-
- Group g(ngens + other.ngens, get_rels());
-
- for (Rel rel : other.get_rels()) {
- g.setmult({off + rel.gens[0], off + rel.gens[1], rel.mult});
- }
-
- std::stringstream ss;
- ss << name << "*" << other.name;
- g.name = ss.str();
-
- return g;
- }
-
- Group Group::power(int p) const {
- Group g(ngens * p);
-
- for (Rel rel : get_rels()) {
- for (int off = 0; off < g.ngens; off += ngens) {
- g.setmult({off + rel.gens[0], off + rel.gens[1], rel.mult});
- }
- }
-
- std::stringstream ss;
- ss << name << "^" << p;
- g.name = ss.str();
-
- return g;
- }
-
- bool Group::trivial() const {
- for (int i = 0; i < ngens; ++i) {
- for (int j = 0; j < ngens; ++j) {
- if (_mults[i][j] != 2) {
- return false;
- }
- }
- }
-
- return true;
- }
-
- Group operator*(const Group &g, const Group &h) {
- return g.product(h);
- }
-
- Group operator^(const Group &g, const int &p) {
- return g.power(p);
- }
-
- Group schlafli(const std::vector &mults, const std::string &name) {
- int ngens = (int) mults.size() + 1;
-
- Group g(ngens, {}, name);
-
- for (int i = 0; i < (int) mults.size(); i++) {
- g.setmult({i, i + 1, mults[i]});
- }
-
- return g;
- }
-
- Group schlafli(const std::vector &mults) {
- std::stringstream ss;
- ss << "[";
- if (!mults.empty()) {
- copy(mults.begin(), mults.end() - 1, std::ostream_iterator(ss, ","));
- ss << mults.back();
- }
- ss << "]";
-
- return schlafli(mults, ss.str());
- }
-
- namespace group {
- Group A(const int dim) {
- if (dim == 0)
- return Group(0, {}, "A(0)");
-
- const std::vector &mults = std::vector(dim - 1, 3);
-
- std::stringstream ss;
- ss << "A(" << dim << ")";
-
- return schlafli(mults, ss.str());
- }
-
- Group B(const int dim) {
- std::vector mults(dim - 1, 3);
- mults[0] = 4;
-
- std::stringstream ss;
- ss << "B(" << dim << ")";
-
- return schlafli(mults, ss.str());
- }
-
- Group D(const int dim) {
- std::vector mults(dim - 1, 3);
- mults[dim - 2] = 2;
- Group g = schlafli(mults);
- g.setmult({1, dim - 1, 3});
-
- std::stringstream ss;
- ss << "D(" << dim << ")";
- g.name = ss.str();
-
- return g;
- }
-
- Group E(const int dim) {
- std::vector mults(dim - 1, 3);
- mults[dim - 2] = 2;
- Group g = schlafli(mults);
- g.setmult({2, dim - 1, 3});
-
- std::stringstream ss;
- ss << "E(" << dim << ")";
- g.name = ss.str();
-
- return g;
- }
-
- Group F4() {
- return schlafli({3, 4, 3}, "F4");
- }
-
- Group G2() {
- return schlafli({6}, "G2");
- }
-
- Group H(const int dim) {
- std::vector mults(dim - 1, 3);
- mults[0] = 5;
-
- std::stringstream ss;
- ss << "H(" << dim << ")";
-
- return schlafli(mults, ss.str());
- }
-
- Group I2(const int n) {
- std::stringstream ss;
- ss << "I2(" << n << ")";
-
- return schlafli({n}, ss.str());
- }
-
- Group T(const int n, const int m) {
- Group g = I2(n) * I2(m);
-
- std::stringstream ss;
- ss << "T(" << n << "," << m << ")";
- g.name = ss.str();
-
- return g;
- }
-
- Group T(const int n) {
- Group g = I2(n) ^2;
-
- std::stringstream ss;
- ss << "T(" << n << ")";
- g.name = ss.str();
-
- return g;
- }
- }
-}
\ No newline at end of file
diff --git a/tc/src/solver.cpp b/tc/src/solver.cpp
deleted file mode 100644
index f03ae21..0000000
--- a/tc/src/solver.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-#include "tc/solver.h"
-
-#include
-
-namespace tc {
- struct RelTable {
- Rel rel;
-
- std::vector lst_ptr;
- std::vector gen;
-
- int &mult = rel.mult;
- std::array &gens = rel.gens;
-
- explicit RelTable(Rel rel) : rel(rel) {
- }
-
- int add_row() {
- int idx = lst_ptr.size();
- lst_ptr.push_back(nullptr);
- gen.push_back(-1);
- return idx;
- }
- };
-
- struct RelationSet {
- const Cosets &cosets;
-
- std::vector tables;
- std::vector> gen_map; // which relations involve which generators
-
- explicit RelationSet(const Group &g, const Cosets &cosets) : gen_map(g.ngens), cosets(cosets) {
- const std::vector &rels = g.get_rels();
- tables.reserve(rels.size());
- for (const auto &rel : rels) {
- RelTable &table = tables.emplace_back(rel);
- gen_map[rel.gens[0]].push_back(&table);
- gen_map[rel.gens[1]].push_back(&table);
- }
- }
-
- void add_row() {
- for (auto &table : tables) {
- table.add_row();
- }
- }
-
- void fill_row(int idx) {
- for (auto &table : tables) {
- if (table.lst_ptr[idx] != nullptr) continue;
-
- table.lst_ptr[idx] = new int;
-
- if ((cosets.get(idx, table.gens[0]) != idx) and
- (cosets.get(idx, table.gens[1]) != idx)) {
- table.gen[idx] = 0;
- } else {
- table.gen[idx] = -1;
- }
- }
- }
- };
-
- Cosets solve(const Group &group, const std::vector &sub_gens) {
- Cosets cosets(group.ngens);
- cosets.add_row();
- for (const auto &i : sub_gens) {
- cosets.put(0, i, 0);
- }
-
- RelationSet rels(group, cosets);
- rels.add_row();
- rels.fill_row(0);
-
- int idx = 0;
- int coset, gen, target, fact_idx, lst, gen_;
- while (true) {
- while (idx < cosets.data.size() and cosets.get(idx) >= 0)
- idx++;
-
- if (idx == cosets.data.size())
- break;
-
- target = cosets.len;
-
- cosets.add_row();
- rels.add_row();
-
- std::vector facts = {idx};
-
- while (!facts.empty()) {
- fact_idx = facts.back();
- facts.pop_back();
-
- if (cosets.get(fact_idx) != -1)
- continue;
-
- cosets.put(fact_idx, target);
-
- coset = fact_idx / group.ngens;
- gen = fact_idx % group.ngens;
-
- for (RelTable *pTable : rels.gen_map[gen]) {
- RelTable &table = *pTable;
-
- if (table.lst_ptr[target] == nullptr) {
- table.lst_ptr[target] = table.lst_ptr[coset];
- table.gen[target] = table.gen[coset] + 1;
-
- if (table.gen[coset] < 0)
- table.gen[target] -= 2;
-
- if (table.gen[target] == table.rel.mult) {
- lst = *(table.lst_ptr[target]);
- delete table.lst_ptr[target];
- gen_ = table.gens[table.gens[0] == gen];
- facts.push_back(lst * group.ngens + gen_);
- } else if (table.gen[target] == -table.mult) {
- gen_ = table.gens[table.gens[0] == gen];
- facts.push_back(target * group.ngens + gen_);
- } else if (table.gen[target] == table.mult - 1) {
- *(table.lst_ptr[target]) = target;
- }
- }
- }
-
- std::sort(facts.begin(), facts.end(), std::greater<>());
- }
-
- rels.fill_row(target);
- }
-
- return cosets;
- }
-}
\ No newline at end of file
diff --git a/vendor/toddcox b/vendor/toddcox
new file mode 160000
index 0000000..16c9d7d
--- /dev/null
+++ b/vendor/toddcox
@@ -0,0 +1 @@
+Subproject commit 16c9d7d62f728196c5d2c078d268fe46d81a17be
diff --git a/vis/CMakeLists.txt b/vis/CMakeLists.txt
index 9b9faae..12f2dd9 100644
--- a/vis/CMakeLists.txt
+++ b/vis/CMakeLists.txt
@@ -1,7 +1,3 @@
-add_executable(vis src/main.cpp)
-target_include_directories(vis PRIVATE include)
-target_link_libraries(vis PRIVATE tc glad glm glfw)
-
add_custom_target(shaders ALL DEPENDS shader_output)
add_custom_command(
OUTPUT shader_output
@@ -9,4 +5,10 @@ add_custom_command(
COMMENT "copied shaders"
)
+add_library(vis-util INTERFACE)
+target_include_directories(vis-util INTERFACE include)
+
+add_executable(vis src/main.cpp)
+target_include_directories(vis PRIVATE include)
+target_link_libraries(vis PRIVATE tc glad glm glfw)
add_dependencies(vis shaders)
diff --git a/vis/include/combo_iterator.hpp b/vis/include/combo_iterator.hpp
new file mode 100644
index 0000000..f7aa005
--- /dev/null
+++ b/vis/include/combo_iterator.hpp
@@ -0,0 +1,94 @@
+#pragma once
+
+#include
+#include
+#include
+
+template
+struct ComboIterator {
+ typedef ComboIterator self_type;
+ typedef const std::vector value_type;;
+ typedef const std::vector *pointer;
+ typedef const std::vector &reference;
+ typedef size_t difference_type;
+ typedef std::forward_iterator_tag iterator_category;
+
+ const std::vector &vals;
+ const size_t k;
+ size_t n;
+ std::vector bits;
+ std::vector curr;
+
+ ComboIterator(ComboIterator &) = default;
+
+ ComboIterator(const std::vector &vals, const size_t k, const size_t n)
+ : vals(vals), k(k), n(n), curr(k), bits(vals.size()) {
+ for (size_t i = 0; i < vals.size(); ++i) {
+ bits[i] = i < k;
+ }
+ std::reverse(bits.begin(), bits.end());
+ set_curr();
+ }
+
+ ~ComboIterator() = default;
+
+ void set_curr() {
+ for (size_t i = 0, j = 0; i < vals.size(); ++i) {
+ if (bits[i]) curr[j++] = vals[i];
+ }
+ }
+
+ [[nodiscard]] bool operator==(const ComboIterator &o) const {
+ return n == o.n;
+ }
+
+ [[nodiscard]] bool operator!=(const ComboIterator &o) const {
+ return n != o.n;
+ }
+
+ reference operator*() const {
+ return curr;
+ }
+
+ pointer operator->() const {
+ return &curr;
+ }
+
+ self_type operator++(int) {
+ std::next_permutation(bits.begin(), bits.end());
+ set_curr();
+ ++n;
+ return *this;
+ }
+
+ self_type operator++() {
+ self_type r = *this;
+ std::next_permutation(bits.begin(), bits.end());
+ set_curr();
+ ++n;
+ return r;
+ }
+};
+
+size_t choose(size_t n, size_t k) {
+ if (k == 0) return 1;
+ return n * choose(n - 1, k - 1) / k;
+}
+
+template
+struct Combos {
+ const std::vector &vals;
+ size_t k;
+
+ Combos(const std::vector &vals, size_t k) : vals(vals), k(k) {
+ }
+
+ [[nodiscard]] ComboIterator begin() const {
+ return ComboIterator(vals, k, 0);
+ }
+
+ [[nodiscard]] ComboIterator end() const {
+ int j = choose(vals.size(), k);
+ return ComboIterator(vals, k, j);
+ }
+};
diff --git a/vis/include/geometry.hpp b/vis/include/geometry.hpp
new file mode 100644
index 0000000..faf7c33
--- /dev/null
+++ b/vis/include/geometry.hpp
@@ -0,0 +1,284 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include "combo_iterator.hpp"
+
+size_t get_key_from_gens(std::vector &gens) {
+ size_t key = 0;
+ for (const auto gen : gens) {
+ key += (1u << (unsigned) gen);
+ }
+ return key;
+}
+
+size_t num_gens_from_key(size_t key) {
+ size_t mask = 1;
+ size_t count = 0;
+ while (mask <= key) {
+ if (key & mask)
+ count++;
+ mask <<= 1u;
+ }
+ return count;
+}
+
+template
+struct Primitive {
+ std::array inds;
+
+ Primitive() = default;
+
+ Primitive(const Primitive &) = default;
+
+ Primitive(const Primitive &sub, unsigned root) {
+ std::copy(sub.inds.begin(), sub.inds.end(), inds.begin());
+ inds[N - 1] = root;
+ }
+
+ ~Primitive() = default;
+
+ inline void flip() {
+ if (N > 1) std::swap(inds[0], inds[1]);
+ }
+
+ void apply(const tc::Cosets &table, int gen) {
+ for (auto &ind : inds) {
+ ind = table.get(ind, gen);
+ }
+ flip();
+ }
+};
+
+std::vector gens(const tc::Group &context) {
+ std::vector g_gens(context.ngens);
+ std::iota(g_gens.begin(), g_gens.end(), 0);
+ return g_gens;
+}
+
+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;
+
+// std::sort(g_gens.begin(), g_gens.end());
+//
+// std::vector inv_g_map(g_gens.size());
+// for (int i = 0; i < g_gens.size(); ++i) {
+// inv_g_map[g_gens[i]] = i;
+// }
+//
+// std::transform(sg_gens.begin(), sg_gens.end(), sg_gens.begin(),
+// [inv_g_map](const auto &gen) {
+// return inv_g_map[gen];
+// }
+// );
+//
+// std::sort(sg_gens.begin(), sg_gens.end());
+// return sg_gens;
+}
+
+int get_parity(
+ const tc::Group &context,
+ const std::vector &g_gens,
+ const std::vector &sg_gens
+) {
+ if (g_gens.size() != sg_gens.size() + 1) return 0;
+
+ const auto proper_sg_gens = recontext_gens(context, g_gens, sg_gens);
+
+ int i = 0;
+ for (; i < sg_gens.size(); ++i) {
+ if (proper_sg_gens[i] != i) {
+ break;
+ }
+ }
+
+ return i & 1;
+}
+
+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);
+}
+
+tc::Cosets solve_sg(
+ const tc::Group &context,
+ const std::vector &sg_gens
+) {
+ return solve(context, gens(context), sg_gens);
+}
+
+tc::Cosets solve_g(
+ const tc::Group &context,
+ const std::vector &g_gens
+) {
+ std::vector sg_gens;
+ return solve(context, g_gens, sg_gens);
+}
+
+tc::Cosets solve(
+ const tc::Group &context
+) {
+ std::vector sg_gens;
+ return solve_sg(context, sg_gens);
+}
+
+template
+struct Mesh {
+ std::vector> prims;
+
+ Mesh() : prims() {}
+
+ Mesh(const Mesh &) = default;
+
+ explicit Mesh(std::vector> &prims) : prims(prims) {}
+
+ [[nodiscard]] size_t size() const {
+ return prims.size();
+ }
+
+ void apply(const tc::Cosets &table, int gen) {
+ for (auto &prim : prims) {
+ prim.apply(table, gen);
+ }
+ }
+
+ void flip() {
+ for (auto &prim : prims) {
+ prim.flip();
+ }
+ }
+
+ [[nodiscard]]
+ Mesh recontext(
+ const tc::Group &context,
+ const std::vector &g_gens,
+ const std::vector &sg_gens
+ ) const {
+ const auto proper_sg_gens = recontext_gens(context, g_gens, sg_gens);
+ const auto table = solve_g(context, g_gens);
+ const auto path = solve_g(context, sg_gens).path;
+
+ auto map = path.template walk(0, proper_sg_gens, [table](int coset, int gen) {
+ return table.get(coset, gen);
+ });
+
+ Mesh res = *this;
+ for (Primitive &prim : res.prims) {
+ for (auto &ind : prim.inds) {
+ ind = map[ind];
+ }
+ }
+
+ if (get_parity(context, g_gens, sg_gens) == 1)
+ res.flip();
+
+ return res;
+ }
+
+ [[nodiscard]]
+ Mesh tile(
+ const tc::Group &context,
+ const std::vector &g_gens,
+ const std::vector &sg_gens
+ ) const {
+ Mesh base = recontext(context, g_gens, sg_gens);
+ const auto proper_sg_gens = recontext_gens(context, g_gens, sg_gens);
+
+ const auto table = solve_g(context, g_gens);
+ const auto path = solve(context, g_gens, sg_gens).path;
+
+ const auto all = path.template walk, int>(base, gens(context), [table](Mesh from, int gen) {
+ from.apply(table, gen);
+ return from;
+ });
+
+ return merge(all);
+ }
+
+ [[nodiscard]]
+ Mesh fan(int root) const {
+ std::vector> res(prims.size());
+ std::transform(prims.begin(), prims.end(), res.begin(),
+ [root](const Primitive &prim) {
+ return Primitive(prim, root);
+ }
+ );
+ return Mesh(res);
+ }
+};
+
+template
+Mesh merge(const std::vector> &meshes) {
+ size_t size = 0;
+ for (const auto &mesh : meshes) {
+ size += mesh.size();
+ }
+
+ std::vector> prims;
+ prims.reserve(size);
+ for (const auto &mesh : meshes) {
+ prims.insert(prims.end(), mesh.prims.begin(), mesh.prims.end());
+ }
+
+ return Mesh(prims);
+}
+
+template
+Mesh triangulate(
+ const tc::Group &context,
+ const std::vector &g_gens
+) {
+ if (g_gens.size() + 1 != N)
+ 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) {
+ Mesh base = triangulate(context, sg_gens);
+ Mesh raised = base.tile(context, g_gens, sg_gens);
+ raised.prims.erase(raised.prims.begin(), raised.prims.begin() + base.size());
+ Mesh fan = raised.fan(0);
+ meshes.push_back(fan);
+ }
+
+ return merge(meshes);
+}
+
+template<>
+Mesh<1> triangulate<1>(
+ const tc::Group &context,
+ const std::vector &g_gens
+) {
+ if (not g_gens.empty())
+ throw std::logic_error("g_gens must be empty for a trivial Mesh");
+
+ Mesh<1> res;
+ res.prims.emplace_back();
+ return res;
+}
diff --git a/vis/include/geom.h b/vis/include/mirror.hpp
similarity index 57%
rename from vis/include/geom.h
rename to vis/include/mirror.hpp
index 153ad36..7b6b1fe 100644
--- a/vis/include/geom.h
+++ b/vis/include/mirror.hpp
@@ -1,6 +1,6 @@
#pragma once
-#include
+#include
#include
#include
#include
@@ -20,28 +20,49 @@ float dot(int n, const glm::vec4 &a, const glm::vec4 &b) {
return sum;
}
+float dot(int n, const std::vector &a, const std::vector &b) {
+ float sum = 0;
+ for (int i = 0; i < n; ++i) {
+ sum += a[i] * b[i];
+ }
+ return sum;
+}
+
std::vector mirror(const tc::Group &group) {
- std::vector mirrors;
+ std::vector> mirrors;
for (int p = 0; p < group.ngens; ++p) {
- glm::vec4 vp{};
+ std::vector vp;
for (int m = 0; m < p; ++m) {
- glm::vec4 vq = mirrors[m];
- vp[m] = (cos(M_PI / group.rel(p, m).mult) - dot(m, vp, vq)) / vq[m];
+ auto &vq = mirrors[m];
+ vp.push_back((cos(M_PI / group.get(p, m)) - dot(m, vp, vq)) / vq[m]);
}
- vp[p] = std::sqrt(1 - glm::dot(vp, vp));
+ vp.push_back(std::sqrt(1 - dot(p, vp, vp)));
for (const auto &v : mirrors) {
- if (glm::dot(vp, v) > 0) {
- vp *= -1;
+ if (dot(p, vp, vp) > 0) {
+ for (auto &e : vp) {
+ e *= -1;
+ }
break;
}
}
- mirrors.push_back(round(vp, 15));
+ mirrors.push_back(vp);
}
- return mirrors;
+ std::vector res;
+ for (const auto &vec : mirrors) {
+ glm::vec4 rvec{};
+
+ // ortho proj
+ for (int i = 0; i < std::min(vec.size(), (size_t) 4); ++i) {
+ rvec[i] = vec[i];
+ }
+
+ res.push_back(rvec);
+ }
+ return res;
}
glm::vec4 project(const glm::vec4 &vec, const glm::vec4 &target) {
@@ -52,6 +73,10 @@ glm::vec4 reflect(const glm::vec4 &vec, const glm::vec4 axis) {
return vec - 2.f * project(vec, axis);
}
+glm::vec4 reflect_scaled(const glm::vec4 &vec, const glm::vec4 axis) {
+ return vec - 2.f * glm::length(axis) * project(vec, axis);
+}
+
glm::vec4 gram_schmidt_last(std::vector vecs) {
int N = vecs.size();
for (int i = 0; i < N; ++i) {
@@ -84,3 +109,12 @@ std::vector plane_intersections(std::vector normals) {
return results;
}
+
+glm::mat4 utilRotate(const int u, const int v, const float theta) {
+ auto res = glm::identity();
+ res[u][u] = std::cos(theta);
+ res[u][v] = std::sin(theta);
+ res[v][u] = -std::sin(theta);
+ res[v][v] = std::cos(theta);
+ return res;
+}
\ No newline at end of file
diff --git a/vis/include/util.hpp b/vis/include/util.hpp
new file mode 100644
index 0000000..17d30ab
--- /dev/null
+++ b/vis/include/util.hpp
@@ -0,0 +1,161 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+class gl_error : public std::domain_error {
+public:
+ explicit gl_error(const std::string &arg) : domain_error(arg) {}
+
+ explicit gl_error(const char *string) : domain_error(string) {}
+};
+
+class shader_error : public gl_error {
+public:
+ explicit shader_error(const std::string &arg) : gl_error(arg) {}
+
+ explicit shader_error(const char *string) : gl_error(string) {}
+};
+
+class program_error : public gl_error {
+public:
+ explicit program_error(const std::string &arg) : gl_error(arg) {}
+
+ explicit program_error(const char *string) : gl_error(string) {}
+};
+
+template
+T utilGetShader(GLuint shader) {
+ GLint res;
+ glGetShaderiv(shader, prop, &res);
+ return static_cast(res);
+}
+
+#define getShaderInfoLogLength utilGetShader
+#define getShaderCompileStatus utilGetShader
+
+template
+T utilGetProgram(GLuint program) {
+ GLint res;
+ glGetProgramiv(program, prop, &res);
+ return static_cast(res);
+}
+
+#define getProgramInfoLogLength utilGetProgram
+#define getProgramLinkStatus utilGetProgram
+
+void utilShaderSource(GLuint shader, const std::vector &sources) {
+ char const *ptrs[sources.size()];
+ for (size_t i = 0; i < sources.size(); ++i) {
+ ptrs[i] = sources[i].c_str();
+ }
+ glShaderSource(shader, sources.size(), ptrs, nullptr);
+}
+
+std::string getShaderInfoLog(GLuint shader) {
+ int len = getShaderInfoLogLength(shader);
+ char buffer[len];
+ glGetShaderInfoLog(shader, len, nullptr, buffer);
+ return std::string(buffer);
+}
+
+std::string getProgramInfoLog(GLuint program) {
+ int len = getProgramInfoLogLength(program);
+ char buffer[len];
+ glGetProgramInfoLog(program, len, nullptr, buffer);
+ return std::string(buffer);
+}
+
+std::string utilInfo() {
+ std::stringstream ss;
+ ss
+ << "Graphics Information:" << std::endl
+ << " Vendor: " << glGetString(GL_VENDOR) << std::endl
+ << " Renderer: " << glGetString(GL_RENDERER) << std::endl
+ << " OpenGL version: " << glGetString(GL_VERSION) << std::endl
+ << " Shading version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
+ return ss.str();
+}
+
+std::string utilReadFile(const std::string &filename) {
+ std::ifstream in(filename, std::ios::in | std::ios::binary);
+ if (in) {
+ std::ostringstream contents;
+ contents << in.rdbuf();
+ in.close();
+ return (contents.str());
+ }
+ throw std::system_error(errno, std::generic_category());
+}
+
+GLuint utilCompileFiles(const GLenum type, const std::vector &files) {
+ std::vector sources;
+ sources.reserve(files.size());
+ for (const auto &file : files) {
+ sources.push_back(utilReadFile(file));
+ }
+
+ GLuint shader = glCreateShader(type);
+ utilShaderSource(shader, sources);
+ glCompileShader(shader);
+
+ if (getShaderCompileStatus(shader)) return shader;
+
+ throw shader_error(getShaderInfoLog(shader));
+}
+
+GLuint utilLinkProgram(const std::vector &shaders) {
+ GLuint program = glCreateProgram();
+ for (const auto &shader : shaders) {
+ glAttachShader(program, shader);
+ }
+ glLinkProgram(program);
+
+ if (getProgramLinkStatus(program)) return program;
+
+ throw program_error(getProgramInfoLog(program));
+}
+
+GLuint utilCreateShaderProgram(GLenum type, const std::vector &src) {
+ std::vector c_str(src.size());
+ std::transform(src.begin(), src.end(), c_str.begin(), [](auto &str) {
+ return str.c_str();
+ });
+
+ GLuint program = glCreateShaderProgramv(type, src.size(), &c_str[0]);
+
+ if (getProgramLinkStatus(program)) return program;
+
+ throw program_error(getProgramInfoLog(program));
+}
+
+GLuint utilCreateShaderProgramFile(GLenum type, const std::vector &files) {
+ std::vector sources(files.size());
+ std::transform(files.begin(), files.end(), sources.begin(), utilReadFile);
+ return utilCreateShaderProgram(type, sources);
+}
+
+std::vector utilCreateVertexArrays(int n) {
+ std::vector res(n);
+ glCreateVertexArrays(n, &res[0]);
+ return res;
+}
+
+GLuint utilCreateVertexArray() {
+ return utilCreateVertexArrays(1)[0];
+}
+
+std::vector utilCreateBuffers(int n) {
+ std::vector res(n);
+ glCreateBuffers(n, &res[0]);
+ return res;
+}
+
+GLuint utilCreateBuffer() {
+ return utilCreateBuffers(1)[0];
+}
\ No newline at end of file
diff --git a/vis/shaders/4d/4d.gm.glsl b/vis/shaders/4d/4d.gm.glsl
new file mode 100644
index 0000000..3fafcb6
--- /dev/null
+++ b/vis/shaders/4d/4d.gm.glsl
@@ -0,0 +1,65 @@
+#version 440 core
+
+layout(points) in;
+layout(triangle_strip, max_vertices=4) out;
+
+layout(std430, binding=1) buffer Positions {
+ vec4 verts[];
+};
+
+layout(std140, binding=1) uniform Matrices {
+ mat4 proj;
+ mat4 view;
+};
+
+in ivec4 vInds[];
+
+out vec4 pos;
+
+out gl_PerVertex {
+ vec4 gl_Position;
+};
+
+float unmix(float u, float v) {
+ return (u) / (u - v);
+}
+
+void emit(vec4 v) {
+ pos = v;
+ gl_Position = proj * vec4(v.xyz, 1);
+ EmitVertex();
+}
+
+void main() {
+ vec4 pos4[4];
+ for (int i = 0; i < 4; ++i) pos4[i] = view * verts[vInds[0][i]];
+
+ int lo[4], L = 0;
+ int hi[4], H = 0;
+
+ float x = 0.7;
+
+ for (int i = 0; i < 4; ++i) {
+ if (pos4[i].w < 0) {
+ lo[L++] = i;
+ } else {
+ hi[H++] = i;
+ }
+ }
+
+ vec4 sect[4]; int S = 0;
+ for (int l = 0; l < L; ++l)
+ for (int h = H-1; h >=0; --h) {
+ vec4 a = pos4[lo[l]];
+ vec4 b = pos4[hi[h]];
+
+ float t = unmix(a.w, b.w);
+ sect[S++] = mix(a, b, t);
+ }
+
+ for (int s = 0; s < S; ++s) {
+ emit(sect[s]);
+ }
+
+ EndPrimitive();
+}
diff --git a/vis/shaders/4d/4d.vs.glsl b/vis/shaders/4d/4d.vs.glsl
new file mode 100644
index 0000000..a411c07
--- /dev/null
+++ b/vis/shaders/4d/4d.vs.glsl
@@ -0,0 +1,27 @@
+#version 440 core
+
+layout(std430, binding=1) buffer Positions {
+ vec4 verts[];
+};
+
+layout(std140, binding=1) uniform Matrices {
+ mat4 proj;
+ mat4 view;
+};
+
+layout(location=0) in ivec4 inds;
+
+out ivec4 vInds;
+
+out gl_PerVertex {
+ vec4 gl_Position;
+ float gl_PointSize;
+};
+
+void main() {
+ vInds = inds;
+
+ vec4 pos = view * verts[vInds.x];
+ gl_Position = proj * vec4(pos.xyz, 1);
+ gl_PointSize = 5;
+}
diff --git a/vis/shaders/one-color.fs.glsl b/vis/shaders/one-color.fs.glsl
new file mode 100644
index 0000000..11f3ec0
--- /dev/null
+++ b/vis/shaders/one-color.fs.glsl
@@ -0,0 +1,12 @@
+#version 430
+
+layout(location=2) uniform vec3 c;
+
+in vec4 pos;
+
+out vec4 color;
+
+void main() {
+ float d = smoothstep(-2, 2, pos.z);
+ color = vec4(c * d, 1);
+}
diff --git a/vis/shaders/ortho.vs.glsl b/vis/shaders/ortho.vs.glsl
new file mode 100644
index 0000000..94accfa
--- /dev/null
+++ b/vis/shaders/ortho.vs.glsl
@@ -0,0 +1,17 @@
+#version 430
+
+out gl_PerVertex {
+ vec4 gl_Position;
+};
+
+layout(location=0) uniform mat4 proj;
+layout(location=1) uniform mat4 view;
+
+layout(location=0) in vec4 pos;
+
+out vec4 vpos;
+
+void main() {
+ vpos = view * pos;
+ gl_Position = proj * vec4(vpos.xyz / (1), 1);
+}
diff --git a/vis/shaders/stereo-proper.gm.glsl b/vis/shaders/stereo-proper.gm.glsl
new file mode 100644
index 0000000..a2fd50c
--- /dev/null
+++ b/vis/shaders/stereo-proper.gm.glsl
@@ -0,0 +1,25 @@
+#version 430
+
+#define SUBS 20
+
+layout(lines) in;
+layout(line_strip, max_vertices = SUBS) out;
+
+layout(location=0) uniform mat4 proj;
+
+in vec4 gpos[];
+out vec4 vpos;
+
+vec4 stereo(vec4 v) {
+ return vec4(v.xyz / (1 - v.w), 1);
+}
+
+void main() {
+ for (int i = 0; i < SUBS; i++) {
+ vpos = mix(gpos[0], gpos[1], i * 1.0f / (SUBS - 1));
+ vpos = normalize(vpos);
+ gl_Position = proj * stereo(vpos);
+ EmitVertex();
+ }
+ EndPrimitive();
+}
diff --git a/vis/shaders/stereo-proper.vs.glsl b/vis/shaders/stereo-proper.vs.glsl
new file mode 100644
index 0000000..f7d7752
--- /dev/null
+++ b/vis/shaders/stereo-proper.vs.glsl
@@ -0,0 +1,14 @@
+#version 430
+
+layout(location=0) uniform mat4 proj;
+layout(location=1) uniform mat4 view;
+
+layout(location=0) in vec4 pos;
+
+out vec4 vpos;
+
+void main() {
+ vpos = view * pos;
+ gl_Position = proj * vec4(vpos.xyz / (1), 1);
+ gl_PointSize = 5;
+}
diff --git a/vis/shaders/stereo.vs.glsl b/vis/shaders/stereo.vs.glsl
new file mode 100644
index 0000000..3829473
--- /dev/null
+++ b/vis/shaders/stereo.vs.glsl
@@ -0,0 +1,15 @@
+#version 430
+
+layout(location=0) uniform mat4 proj;
+layout(location=1) uniform mat4 view;
+
+layout(location=0) in vec4 pos;
+
+out vec4 vpos;
+
+void main() {
+ int i = gl_VertexID;
+ vpos = view * pos;
+ gl_Position = proj * vec4(vpos.xyz / (1 - vpos.w), 1);
+ gl_PointSize = 5 * smoothstep(-2, 2, gl_Position.z);
+}
diff --git a/vis/shaders/w-axis-hue.fs.glsl b/vis/shaders/w-axis-hue.fs.glsl
new file mode 100644
index 0000000..99646e4
--- /dev/null
+++ b/vis/shaders/w-axis-hue.fs.glsl
@@ -0,0 +1,11 @@
+#version 430
+
+in vec4 vpos;
+
+out vec4 color;
+
+void main() {
+ float d = smoothstep(-2, 2, vpos.z);
+ vec3 off = 1.04 * vec3(0, 2, 4) + 2 * vec3(vpos.w);
+ color = vec4(d * cos(off), 1);
+}
diff --git a/vis/src/main.cpp b/vis/src/main.cpp
index 32dccd9..966e17c 100644
--- a/vis/src/main.cpp
+++ b/vis/src/main.cpp
@@ -1,14 +1,15 @@
#include
#include
+#include
#include
-#include
-#include
-
-#include "geom.h"
-
#include
-#include
+
+#include
+
+#include "util.hpp"
+#include "mirror.hpp"
+#include "geometry.hpp"
#ifdef _WIN32
extern "C" {
@@ -16,92 +17,33 @@ __attribute__((unused)) __declspec(dllexport) int NvOptimusEnablement = 0x000000
}
#endif
-void utilShaderSource(GLuint shader, const std::vector &sources) {
- char const *ptrs[sources.size()];
- for (size_t i = 0; i < sources.size(); ++i) {
- ptrs[i] = sources[i].c_str();
- }
- glShaderSource(shader, sources.size(), ptrs, nullptr);
+struct Matrices {
+ glm::mat4 proj;
+ glm::mat4 view;
+};
+
+Matrices build(GLFWwindow *window, float st) {
+ int width, height;
+ glfwGetFramebufferSize(window, &width, &height);
+
+ auto aspect = (float) width / (float) height;
+ auto pheight = 1.4f;
+ auto pwidth = aspect * pheight;
+ glm::mat4 proj = glm::ortho(-pwidth, pwidth, -pheight, pheight, -10.0f, 10.0f);
+
+ auto view = glm::identity();
+ view *= utilRotate(0, 1, st * .40f);
+ view *= utilRotate(0, 2, st * .20f);
+ view *= utilRotate(0, 3, st * 1.30f);
+ view *= utilRotate(1, 2, st * .50f);
+ view *= utilRotate(1, 3, st * .25f);
+ view *= utilRotate(2, 3, st * 1.42f);
+
+ return {proj, view};
}
-std::string utilShaderInfoLog(GLuint shader) {
- int len;
- glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
- char buffer[len];
- glGetShaderInfoLog(shader, len, nullptr, buffer);
- return std::string(buffer);
-}
-
-std::string utilProgramInfoLog(GLuint program) {
- int len;
- glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len);
- char buffer[len];
- glGetProgramInfoLog(program, len, nullptr, buffer);
- return std::string(buffer);
-}
-
-
-template
-tc::Group shrink(const tc::Group &g, const std::array &sub) {
- tc::Group h(D);
- for (int i = 0; i < D; ++i) {
- for (int j = 0; j < D; ++j) {
- h.setmult({i, j, g.rel(sub[i], sub[j]).mult});
- }
- }
- return h;
-}
-
-template
-std::vector raise(
- const tc::Cosets &g_res,
- const tc::Cosets &h_res,
- const std::array &gen_map,
- const std::vector &path
-) {
- std::vector res(path.size(), 0);
- for (size_t i = 1; i < path.size(); ++i) {
- auto action = h_res.path[path[i]];
-
- res[i] = g_res.get(res[action.coset], gen_map[action.gen]);
- }
- return res;
-}
-
-std::vector targets(const std::vector &path) {
- std::vector res(path.size(), 0);
- for (size_t i = 0; i < path.size(); ++i) {
- res[i] = path[i].target;
- }
- return res;
-}
-
-std::vector tile(const tc::Cosets &map, std::vector geom) {
- int K = geom.size();
- geom.resize(K * map.size());
- for (int i = 1; i < map.size(); ++i) { // tile
- auto gaction = map.path[i];
- for (int j = 0; j < K; ++j) {
- geom[i * K + j] = map.get(geom[gaction.coset * K + j], gaction.gen);
- }
- }
- return geom;
-}
-
-template
-std::vector build(const tc::Group &g, const std::array &sub) {
- tc::Group h = shrink(g, sub);
- auto hres = tc::solve(h); // recursion would happen here
-
- auto gres = tc::solve(g);
- std::vector geom = targets(hres.path);
- geom = raise(gres, hres, sub, geom);
-
- return tile(gres, geom);
-}
-
-
int main(int argc, char *argv[]) {
+ //region init window
if (!glfwInit()) {
std::cerr << "Failed to initialize GLFW" << std::endl;
return EXIT_FAILURE;
@@ -121,175 +63,126 @@ int main(int argc, char *argv[]) {
glfwMakeContextCurrent(window);
gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
glfwSwapInterval(0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glfwSwapBuffers(window);
+ //endregion
- std::cout
- << "Graphics Information:" << std::endl
- << " Vendor: " << glGetString(GL_VENDOR) << std::endl
- << " Renderer: " << glGetString(GL_RENDERER) << std::endl
- << " OpenGL version: " << glGetString(GL_VERSION) << std::endl
- << " Shading version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
+ std::cout << utilInfo();
- auto group = tc::group::B(2);
- auto res = tc::solve(group);
- auto mirrors = mirror(group);
- auto corners = plane_intersections(mirrors);
+ glEnable(GL_PROGRAM_POINT_SIZE);
+ glEnable(GL_POINT_SMOOTH);
+ glEnable(GL_DEPTH_TEST);
+// glEnable(GL_CULL_FACE);
+ glCullFace(GL_BACK);
- auto points = std::vector(res.size());
- points[0] = barycentric(corners, {1.00f, 0.50f, 0.50f, 0.50f});
- for (int i = 1; i < res.size(); ++i) {
- auto action = res.path[i];
- points[i] = reflect(points[action.coset], mirrors[action.gen]);
- }
+ //region shaders
+ GLuint pipe;
+ glCreateProgramPipelines(1, &pipe);
- auto h = shrink<2>(group, {0, 1});
- auto i = shrink<1>(h, {0});
+ GLuint vs, gm, fs;
- auto g_map = res;
- auto h_map = tc::solve(h);
- auto i_map = tc::solve(i);
+ try {
+ vs = utilCreateShaderProgramFile(GL_VERTEX_SHADER, {"shaders/4d/4d.vs.glsl"});
+ gm = utilCreateShaderProgramFile(GL_GEOMETRY_SHADER, {"shaders/4d/4d.gm.glsl"});
+ fs = utilCreateShaderProgramFile(GL_FRAGMENT_SHADER, {"shaders/one-color.fs.glsl"});
- auto g0 = targets(i_map.path);
- g0 = raise<1>(h_map, i_map, {0}, g0);
- g0 = tile(h_map, g0);
- g0 = raise<2>(g_map, h_map, {0, 1}, g0);
-
-// auto g0 = build<2>(group, {0});
-// auto g1 = build<2>(group, {1});
-// auto g2 = build<2>(group, {2});
-// auto g3 = build<2>(group, {3});
-
- GLuint vs = glCreateShader(GL_VERTEX_SHADER);
- utilShaderSource(vs, {
- "#version 430\n",
-
- "layout(location=0) uniform mat4 proj;"
- "layout(location=1) uniform mat4 view;"
- ""
- "layout(location=0) in vec4 pos;"
- ""
- "out vec4 vpos;"
- ""
- "void main() {"
- " int i = gl_VertexID;"
- " vpos = view * pos;"
- // " gl_Position = proj * vec4(vpos.xyz / (1), 1);"
- " gl_Position = proj * vec4(vpos.xyz / (1 - vpos.w), 1);"
- " gl_PointSize = 5;"
- "}"
- });
- glCompileShader(vs);
-
- GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
- utilShaderSource(fs, {
- "#version 430\n",
-
- "layout(location=2) uniform vec3 c;"
- ""
- "in vec4 vpos;"
- ""
- "out vec4 color;"
- ""
- "void main() {"
- " float d = smoothstep(-2, 2, vpos.z);"
- " color = vec4(c * d, 1);"
- "}"
- });
-
- GLuint pgm = glCreateProgram();
- glAttachShader(pgm, vs);
- glAttachShader(pgm, fs);
- glLinkProgram(pgm);
-
- GLint status;
-
- glGetShaderiv(vs, GL_COMPILE_STATUS, &status);
- if (!status) {
- std::cerr << utilShaderInfoLog(vs) << "\n=========\n" << std::endl;
- }
-
- glGetShaderiv(fs, GL_COMPILE_STATUS, &status);
- if (!status) {
- std::cerr << utilShaderInfoLog(fs) << "\n=========\n" << std::endl;
- }
-
- glGetProgramiv(pgm, GL_LINK_STATUS, &status);
- if (!status) {
- std::cerr << utilProgramInfoLog(pgm) << "\n=========\n" << std::endl;
+ glUseProgramStages(pipe, GL_VERTEX_SHADER_BIT, vs);
+ glUseProgramStages(pipe, GL_GEOMETRY_SHADER_BIT, gm);
+ glUseProgramStages(pipe, GL_FRAGMENT_SHADER_BIT, fs);
+ } catch (const gl_error &e) {
+ std::cerr << e.what() << std::endl;
glfwTerminate();
- return EXIT_FAILURE;
+ exit(EXIT_FAILURE);
}
+ //endregion
+
+ //region points
+ auto group = tc::group::H(4);
+ auto res = group.solve();
+ auto mirrors = mirror(group);
+
+ auto corners = plane_intersections(mirrors);
+// auto start = barycentric(corners, {1.0f, 1.0f, 1.0f, 1.0f});
+ auto start = barycentric(corners, {1.00f, 0.2f, 0.1f, 0.05f});
+// auto start = barycentric(corners, {0.05f, 0.1f, 0.2f, 1.00f});
+ auto points = res.path.walk(start, mirrors, reflect);
+
+ auto g_gens = gens(group);
+
+ std::vector vaos;
+ std::vector ibos;
+ std::vector counts;
+
+ auto combos = Combos(g_gens, 3);
+// std::vector> chosen = {
+// {1, 2, 3},
+// {0, 2, 3},
+// };
+ auto chosen = combos;
+
+ for (const auto &sg_gens : chosen) {
+ const auto s = triangulate<4>(group, sg_gens)
+ .tile(group, g_gens, sg_gens);
+
+ GLuint vao = utilCreateVertexArray();
+ GLuint ibo = utilCreateBuffer();
+ unsigned count = s.size();
+
+ glBindVertexArray(vao);
+ glBindBuffer(GL_ARRAY_BUFFER, ibo);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(Primitive<4>) * s.size(), &s.prims[0], GL_STATIC_DRAW);
+ glEnableVertexAttribArray(0);
+ glVertexAttribIPointer(0, 4, GL_INT, 0, nullptr);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ vaos.push_back(vao);
+ ibos.push_back(ibo);
+ counts.push_back(count);
+ }
+ //endregion
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec4) * points.size(), &points[0], GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
- glEnableVertexAttribArray(0);
- glVertexAttribPointer(0, 4, GL_FLOAT, false, 0, nullptr);
+ GLuint ubo;
+ glGenBuffers(1, &ubo);
- GLuint ibo0;
- glGenBuffers(1, &ibo0);
- glBindBuffer(GL_ARRAY_BUFFER, ibo0);
- glBufferData(GL_ARRAY_BUFFER, sizeof(int) * g0.size(), &g0[0], GL_STATIC_DRAW);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, vbo);
+ glBindBufferBase(GL_UNIFORM_BUFFER, 1, ubo);
-// GLuint ibo1;
-// glGenBuffers(1, &ibo1);
-// glBindBuffer(GL_ARRAY_BUFFER, ibo1);
-// glBufferData(GL_ARRAY_BUFFER, sizeof(int) * g1.size(), &g1[0], GL_STATIC_DRAW);
-//
-// GLuint ibo2;
-// glGenBuffers(1, &ibo2);
-// glBindBuffer(GL_ARRAY_BUFFER, ibo2);
-// glBufferData(GL_ARRAY_BUFFER, sizeof(int) * g2.size(), &g2[0], GL_STATIC_DRAW);
-//
-// GLuint ibo3;
-// glGenBuffers(1, &ibo3);
-// glBindBuffer(GL_ARRAY_BUFFER, ibo3);
-// glBufferData(GL_ARRAY_BUFFER, sizeof(int) * g3.size(), &g3[0], GL_STATIC_DRAW);
+ glBindVertexArray(0);
while (!glfwWindowShouldClose(window)) {
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- glUseProgram(pgm);
- glEnable(GL_PROGRAM_POINT_SIZE);
- glEnable(GL_POINT_SMOOTH);
- glEnable(GL_DEPTH_TEST);
-
int width, height;
glfwGetFramebufferSize(window, &width, &height);
glViewport(0, 0, width, height);
- auto aspect = (float) width / (float) height;
- auto pheight = 1.4f;
- auto pwidth = aspect * pheight;
- glm::mat4 proj = glm::ortho(-pwidth, pwidth, -pheight, pheight);
- glUniformMatrix4fv(0, 1, false, glm::value_ptr(proj));
- auto t = (float) glfwGetTime() / 3;
- auto view = glm::identity();
- view = glm::rotate(view, t / 1, glm::vec3(0, 1, 0));
- view = glm::rotate(view, t / 3, glm::vec3(0, 0, 1));
- view = glm::rotate(view, t / 4, glm::vec3(1, 0, 0));
- glUniformMatrix4fv(1, 1, false, glm::value_ptr(view));
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- glUniform3f(2, 1.0f, 1.0f, 1.0f);
- glDrawArrays(GL_POINTS, 0, points.size());
+ auto st = (float) glfwGetTime() / 8;
+ Matrices mats = build(window, st);
+ glBindBuffer(GL_UNIFORM_BUFFER, ubo);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(mats), &mats, GL_STATIC_DRAW);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
- glLineWidth(2.0f);
- glUniform3f(2, 1.0f, 0.0f, 0.0f);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo0);
- glDrawElements(GL_LINES, g0.size(), GL_UNSIGNED_INT, nullptr);
+ for (int i = 0; i < vaos.size(); ++i) {
+ auto c = glm::mix(
+ glm::vec3(.3, .2, .5),
+ glm::vec3(.9, .9, .95),
+ (float) (i) / (vaos.size() - 1.f)
+ );
-// glUniform3f(2, 0.0f, 1.0f, 0.0f);
-// glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo1);
-// glDrawElements(GL_LINES, g1.size(), GL_UNSIGNED_INT, nullptr);
-//
-// glUniform3f(2, 0.0f, 0.0f, 1.0f);
-// glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo2);
-// glDrawElements(GL_LINES, g2.size(), GL_UNSIGNED_INT, nullptr);
-//
-// glUniform3f(2, 0.7f, 0.7f, 0.0f);
-// glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo3);
-// glDrawElements(GL_LINES, g3.size(), GL_UNSIGNED_INT, nullptr);
+ glBindProgramPipeline(pipe);
+ glBindVertexArray(vaos[i]);
+ glProgramUniform3f(fs, 2, c.r, c.g, c.b);
+ glDrawArrays(GL_POINTS, 0, counts[i]);
+ }
+
+ glBindProgramPipeline(0);
+ glBindVertexArray(0);
glfwSwapBuffers(window);