commit 0b7b6059c120d447600563db53651d97bd205e24 Author: dLedda Date: Sun Nov 13 22:08:20 2022 +0100 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/build diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..17c0402 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.18) +project(somaesque) + +#find_package(glfw3 3.3 REQUIRED) +#find_package(glm REQUIRED) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_FLAGS -I/usr/include/SDL2) + +add_executable(somaesque + main.cpp + VoxelSpace.cpp + VoxelSpace.h +) +#target_link_libraries(somaesque glfw GL X11 pthread Xrandr dl SDL2 glm::glm) +#target_include_directories(somaesque PRIVATE src/KHR src/glad) diff --git a/VoxelSpace.cpp b/VoxelSpace.cpp new file mode 100644 index 0000000..8206eca --- /dev/null +++ b/VoxelSpace.cpp @@ -0,0 +1,262 @@ +#include "VoxelSpace.h" +#include +#include +#include +#include + +namespace Voxel { + inline auto index(int dims[3], int x, int y, int z) -> int { + return (dims[1] * dims[2] * x + dims[2] * y + z); + } + + // [1, 0, 0] [x] [ x] + // [0, 0, -1] * [y] = [-z] + // [0, 1, 0] [z] [ y] + inline auto newIndexRotX(int dims[3], int x, int y, int z) -> int { + return dims[2] * dims[1] * x + dims[1] * (dims[2] - 1 - z) + y; + } + + // [ 0, 0, 1] [x] [ z] + // [ 0, 1, 0] * [y] = [ y] + // [-1, 0, 0] [z] [-x] + inline auto newIndexRotY(int dims[3], int x, int y, int z) -> int { + return dims[1] * dims[0] * z + dims[0] * y + (dims[0] - 1 - x); + } + + // [0, -1, 0] [x] [-y] + // [1, 0, 0] * [y] = [ x] + // [0, 0, 1] [z] [ z] + inline auto newIndexRotZ(int dims[3], int x, int y, int z) -> int { + return dims[0] * dims[2] * (dims[1] - 1 - y) + dims[2] * x + z; + } + + inline auto toggle(uint64_t space, int index) -> uint64_t { + space ^= 1ull << index; + return space; + } + + inline auto set(uint64_t *space, int index, bool val) -> void { + if (val) { + *space |= 1ull << index; + } else { + *space &= ~(1ull << index); + } + } + + inline auto collides(uint64_t a, uint64_t b) -> bool { + return (a | b) != (a ^ b); + } + + inline auto filledAt(uint64_t space, int dims[3], int x, int y, int z) -> bool { + auto mask = 1ull << (dims[1] * dims[2] * x + dims[2] * y + z); + return (space & mask) != 0ull; + } + + auto getExtrema(uint64_t space, int dims[3]) -> Extrema { + auto extrema = Extrema{ + .xMax=0, + .xMin=dims[0], + .yMax=0, + .yMin=dims[1], + .zMax=0, + .zMin=dims[2], + }; + + for (int x = 0; x < dims[0]; x++) { + for (int y = 0; y < dims[1]; y++) { + for (int z = 0; z < dims[2]; z++) { + if (filledAt(space, dims, x, y, z)) { + if (x > extrema.xMax) extrema.xMax = x; + if (x < extrema.xMin) extrema.xMin = x; + if (y > extrema.yMax) extrema.yMax = y; + if (y < extrema.yMin) extrema.yMin = y; + if (z > extrema.zMax) extrema.zMax = z; + if (z < extrema.zMin) extrema.zMin = z; + } + } + } + } + + return extrema; + } + + auto cullEmptySpace(Space *space) -> void { + auto extrema = getExtrema(space->space, space->dims); + auto space_index = 0; + auto newSpace = 0ull; + for (int x = extrema.xMin; x <= extrema.xMax; x++) { + for (int y = extrema.yMin; y <= extrema.yMax; y++) { + for (int z = extrema.zMin; z <= extrema.zMax; z++) { + if (filledAt(space->space, space->dims, x, y, z)) { + newSpace |= 1ull << space_index; + } + space_index++; + } + } + } + space->dims[0] = extrema.xMax - extrema.xMin + 1; + space->dims[1] = extrema.yMax - extrema.yMin + 1; + space->dims[2] = extrema.zMax - extrema.zMin + 1; + space->space = newSpace; + } + + auto rotate90X(Space *space) -> void { + for (int x = 0; x < space->dims[0]; x++) { + for (int y = 0; y < space->dims[1]; y++) { + for (int z = 0; z < space->dims[2]; z++) { + if (filledAt(space->space, space->dims, x, y, z)) { + space->space |= 1 << newIndexRotX(space->dims, x, y, z); + } + } + } + } + auto temp = space->dims[1]; + space->dims[1] = space->dims[2]; + space->dims[2] = temp; + } + + auto rotate90Y(Space *space) -> void { + for (int x = 0; x < space->dims[0]; x++) { + for (int y = 0; y < space->dims[1]; y++) { + for (int z = 0; z < space->dims[2]; z++) { + if (filledAt(space->space, space->dims, x, y, z)) { + space->space |= 1 << newIndexRotY(space->dims, x, y, z); + } + } + } + } + auto temp = space->dims[0]; + space->dims[0] = space->dims[2]; + space->dims[2] = temp; + } + + auto rotate90Z(Space *space) -> void { + for (int x = 0; x < space->dims[0]; x++) { + for (int y = 0; y < space->dims[1]; y++) { + for (int z = 0; z < space->dims[2]; z++) { + if (filledAt(space->space, space->dims, x, y, z)) { + space->space |= 1 << newIndexRotZ(space->dims, x, y, z); + } + } + } + } + auto temp = space->dims[0]; + space->dims[0] = space->dims[1]; + space->dims[1] = temp; + } + + inline auto isMatch(Space *a, Space *b) -> bool { + return a->space == b->space + && a->dims[0] == b->dims[0] + && a->dims[1] == b->dims[1] + && a->dims[2] == b->dims[2]; + } + + auto pushNewUniqueSpins(std::vector *existingSpaces, Space* spaceToSpin) -> void { + Space spins[4] = {}; + spins[0] = *spaceToSpin; + for (int i = 0; i < 3; i++) { + spins[i + 1] = spins[i]; + rotate90X(&spins[i + 1]); + } + for (int i = 0; i < 4; i++) { + auto matchFound = false; + for (auto &existingSpace : *existingSpaces) { + if (isMatch(&existingSpace, &spins[i])) { + matchFound = true; + break; + } + } + if (!matchFound) { + existingSpaces->push_back(spins[i]); + } + } + } + + auto getUniqueRotations(Space *space) -> std::vector { + auto rotations = std::vector(); + rotations.reserve(6*24); + auto dims = space->dims; + auto refSpace = *space; + pushNewUniqueSpins(&rotations, &refSpace); + rotate90Y(&refSpace); + pushNewUniqueSpins(&rotations, &refSpace); + rotate90Y(&refSpace); + pushNewUniqueSpins(&rotations, &refSpace); + rotate90Z(&refSpace); + pushNewUniqueSpins(&rotations, &refSpace); + rotate90Z(&refSpace); + rotate90Z(&refSpace); + pushNewUniqueSpins(&rotations, &refSpace); + return rotations; + } + + /* + getAllRotations(): Space[] { + let rotations: Space[] = new Array(); + const refSpace = this.clone(); + rotations = rotations.concat(refSpace.getXAxisSpins()); + refSpace.rot90Y(); + rotations = rotations.concat(refSpace.getXAxisSpins()); + refSpace.rot90Y(); + rotations = rotations.concat(refSpace.getXAxisSpins()); + refSpace.rot90Y(); + rotations = rotations.concat(refSpace.getXAxisSpins()); + refSpace.rot90Z(); + rotations = rotations.concat(refSpace.getXAxisSpins()); + refSpace.rot90Z(); + refSpace.rot90Z(); + rotations = rotations.concat(refSpace.getXAxisSpins()); + return rotations; + } + */ + + auto getAllPositionsInPrism(uint64_t space, int space_dims[3], int prism_dims[3]) -> std::vector { + auto cubePositions = std::vector(); + if (space_dims[0] > prism_dims[0] || space_dims[1] > prism_dims[1] || space_dims[2] > prism_dims[2]) { + return cubePositions; + } + auto xPositionCount = prism_dims[0] - space_dims[0] + 1; + auto yPositionCount = prism_dims[1] - space_dims[1] + 1; + auto zPositionCount = prism_dims[2] - space_dims[2] + 1; + cubePositions.reserve(xPositionCount + yPositionCount + zPositionCount); + for (int x = 0; x < xPositionCount; x++) { + for (int y = 0; y < yPositionCount; y++) { + for (int z = 0; z < zPositionCount; z++) { + auto new_space = space; + for (int posX = 0; posX < space_dims[0]; posX++) { + for (int posY = 0; posY < space_dims[1]; posY++) { + for (int posZ = 0; posZ < space_dims[2]; posZ++) { + auto set_val = filledAt(space, space_dims, x, y, z); + auto index_to_set = index(space_dims, x + posX, y + posY, z + posZ); + set(&new_space, index_to_set, set_val); + } + } + } + cubePositions.push_back(new_space); + } + } + } + return cubePositions; + } + + auto getAllPermutationsInPrism(Space *space, int prism_dims[3]) -> std::vector { + auto rotations = getUniqueRotations(space); + auto result = std::vector(); + for (auto &rotation : rotations) { + auto positions = getAllPositionsInPrism(rotation.space, rotation.dims, prism_dims); + result.insert(result.end(), positions.begin(), positions.end()); + } + return result; + } + + auto size(uint64_t space) -> int { + auto size = 0; + for (int i = 0; i < 64; i++) { + if ((space & (1ull << i)) != 0) { + size++; + } + } + return size; + } +} diff --git a/VoxelSpace.h b/VoxelSpace.h new file mode 100644 index 0000000..cb59459 --- /dev/null +++ b/VoxelSpace.h @@ -0,0 +1,63 @@ +#ifndef VOXELSPACE_H +#define VOXELSPACE_H + +#include +#include + +namespace Voxel { + struct Extrema { + int xMax; + int xMin; + int yMax; + int yMin; + int zMax; + int zMin; + }; + + struct Space { + uint64_t space; + int dims[3]; + }; + + inline auto index(int dims[3], int x, int y, int z) -> int; + + inline auto newIndexRotX(int dims[3], int x, int y, int z) -> int; + + inline auto newIndexRotY(int dims[3], int x, int y, int z) -> int; + + inline auto newIndexRotZ(int dims[3], int x, int y, int z) -> int; + + inline auto toggle(uint64_t space, int index) -> uint64_t; + + inline auto set(uint64_t space, int index, bool val) -> uint64_t; + + inline auto collides(Space *a, Space *b) -> bool; + + inline auto add(Space *a, Space *b) -> Space; + + inline auto filledAt(Space *space, int index) -> bool; + + auto getExtrema(uint64_t space, int dims[3]) -> Extrema; + + auto cullEmptySpace(Space *space) -> void; + + auto isMatch(Space *a, Space *b) -> bool; + + auto rotate90X(Space *space) -> void; + + auto rotate90Y(Space *space) -> void; + + auto rotate90Z(Space *space) -> void; + + auto pushNewUniqueSpins(std::vector *existingSpaces, Space* spaceToSpin) -> void; + + auto getUniqueRotations(Space *space) -> std::vector; + + auto getAllPositionsInPrism(uint64_t space, int space_dims[3], int prism_dims[3]) -> std::vector; + + auto getAllPermutationsInPrism(Space *space, int prism_dims[3]) -> std::vector; + + auto size(uint64_t space) -> int; +} + +#endif diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..68bb929 --- /dev/null +++ b/main.cpp @@ -0,0 +1,158 @@ +#include +#include +#include +#include "VoxelSpace.h" + +auto backtrack_solve(std::vector *polycube_input, std::vector *offsets)-> void { + auto num_inputs = offsets->size() - 1; + + auto solns = std::vector(); + + auto iter_stack = std::vector(); + auto curr_soln_stack = std::vector(); + auto soln_spaces_stack = std::vector(); + soln_spaces_stack.push_back(0ull); + + auto depth = 0; + + while (depth >= 0) { + if (depth >= iter_stack.size()) { + iter_stack.push_back(offsets->at(depth)); + } + auto end = offsets->at(depth + 1); + auto broke = false; + for (; iter_stack[depth] < end; iter_stack[depth]++) { + auto next_space = polycube_input->at(iter_stack[depth]); + auto soln_space = soln_spaces_stack[depth]; + std::cout << next_space << " " << soln_space << std::endl; + auto successful_fuse = (soln_space | next_space) == (soln_space ^ next_space); + if (successful_fuse) { + soln_spaces_stack.push_back(soln_space |= next_space); + curr_soln_stack.push_back(iter_stack[depth]); + depth++; + if (curr_soln_stack.size() == num_inputs) { + solns.push_back(1); + curr_soln_stack.pop_back(); + soln_spaces_stack.pop_back(); + depth--; + } else { + depth++; + auto broke = true; + break; + } + } + } + if (!broke) { + curr_soln_stack.pop_back(); + soln_spaces_stack.pop_back(); + depth--; + } + } + std::cout << "Done. Found " << solns.size() << " solutions." << std::endl; +} + +auto get_dims_input(int dims[3]) -> void { + std::cout << "Enter dimensions separated by newlines. (x*y*z must not exceed 64)\n"; + auto success = false; + while (!success) { + std::cout << "x: "; + std::cin >> dims[0]; + std::cout << "y: "; + std::cin >> dims[1]; + std::cout << "z: "; + std::cin >> dims[2]; + + auto size = dims[0]*dims[1]*dims[2]; + if (size <= 64) { + success = true; + } else { + std::cout << "That resulted in " << size << " units. Try again.\n"; + } + std::cin.ignore(); + } +} + +auto get_reprs_input(int units_required) -> std::vector { + std::cout << "Enter bit-representations (big endian, max 64 bits, total 1s must add up to " << units_required << "). press ENTER twice to finish input.\n"; + auto reprs = std::vector(); + auto total_units = 0; + while (true) { + auto input = std::string(); + std::getline(std::cin, input); + if (input.size() == 0) { + if (total_units == units_required) { + break; + } else { + std::cout << "Bad number of units. You entered: " << total_units << ", but exactly " << units_required << " were required.\n"; + total_units = 0; + continue; + } + } + auto bit_repr = 0ull; + auto i = 0; + auto good_repr = true; + for (auto it = input.rbegin(); it < input.rend(); it++, i++) { + if (*it == '1') { + bit_repr |= 1ull << i; + total_units++; + } else if (*it != '0' || i >= 64) { + std::cout << "Input invalid. Enter a binary string only with max 64 bits." << '\n'; + good_repr = false; + break; + } + } + if (good_repr) { + reprs.push_back(bit_repr); + } + } + return reprs; +} + +auto main() -> int { + int dims[3] = {}; + get_dims_input(dims); + std::cout << '\n'; + //auto reprs = get_reprs_input(dims[0]*dims[1]*dims[2]); + auto reprs = std::vector{ + 23ull, + 30ull, + 15ull, + 43ull, + 172ull, + 92ull, + 11ull, + }; + std::cout << "Great. Calculating solutions...\n"; + + auto offsets = std::vector(); + auto polycubes = std::vector(); + polycubes.reserve(reprs.size() * 10); + + auto model_space = Voxel::Space{ + .space={}, + .dims={dims[0], dims[1], dims[2]}, + }; + + offsets.push_back(polycubes.size()); + auto space = model_space; + space.space = reprs[0]; + Voxel::cullEmptySpace(&space); + std::cout << space.dims[0] << space.dims[1] << space.dims[2] << std::endl; + auto positions = Voxel::getUniqueRotations(&space); + polycubes.insert(polycubes.end(), positions.begin(), positions.end()); + + for (int i = 1; i < reprs.size(); i++) { + offsets.push_back(polycubes.size()); + auto space = model_space; + space.space = reprs[i]; + Voxel::cullEmptySpace(&space); + std::cout << space.dims[0] << space.dims[1] << space.dims[2] << std::endl; + auto perms = Voxel::getUniqueRotations(&space); + polycubes.insert(polycubes.end(), perms.begin(), perms.end()); + } + + offsets.push_back(polycubes.size()); + + //backtrack_solve(&polycubes, &offsets); + return 0; +}