From da2ba88798b784485468ce52f435ab37fdadd83a Mon Sep 17 00:00:00 2001 From: Justin Lin Date: Mon, 28 Mar 2022 11:39:55 +0800 Subject: [PATCH] add mz_cube --- src/experimental/_impl/_mz_cube_comm.scad | 24 +++ src/experimental/_impl/_mz_cube_impl.scad | 180 ++++++++++++++++++ .../_impl/_mz_cube_initialize.scad | 47 +++++ src/experimental/mz_cube.scad | 107 +++++++++++ src/experimental/mz_cube_get.scad | 20 ++ src/experimental/mz_cube_initialize.scad | 4 + 6 files changed, 382 insertions(+) create mode 100644 src/experimental/_impl/_mz_cube_comm.scad create mode 100644 src/experimental/_impl/_mz_cube_impl.scad create mode 100644 src/experimental/_impl/_mz_cube_initialize.scad create mode 100644 src/experimental/mz_cube.scad create mode 100644 src/experimental/mz_cube_get.scad create mode 100644 src/experimental/mz_cube_initialize.scad diff --git a/src/experimental/_impl/_mz_cube_comm.scad b/src/experimental/_impl/_mz_cube_comm.scad new file mode 100644 index 00000000..9be46ae9 --- /dev/null +++ b/src/experimental/_impl/_mz_cube_comm.scad @@ -0,0 +1,24 @@ +// NO_WALL = 0; +// Y_WALL = 1; +// X_WALL = 2; +// Y_X_WALL = 3; +// Z_WALL = 4; +// Z_Y_WALL = 5; +// Z_X_WALL = 6; +// Z_Y_X_WALL = 7; +// MASK = 8; + +function no_wall(cell) = get_type(cell) == 0; +function y_wall(cell) = get_type(cell) == 1; +function x_wall(cell) = get_type(cell) == 2; +function y_x_wall(cell) = get_type(cell) == 3; +function z_wall(cell) = get_type(cell) == 4; +function z_y_wall(cell) = get_type(cell) == 5; +function z_x_wall(cell) = get_type(cell) == 6; +function z_y_x_wall(cell) = get_type(cell) == 7; + +function cell(x, y, z, type, visited) = [x, y, z, type, visited]; +function get_x(cell) = cell.x; +function get_y(cell) = cell.y; +function get_z(cell) = cell.z; +function get_type(cell) = cell[3]; \ No newline at end of file diff --git a/src/experimental/_impl/_mz_cube_impl.scad b/src/experimental/_impl/_mz_cube_impl.scad new file mode 100644 index 00000000..d2437129 --- /dev/null +++ b/src/experimental/_impl/_mz_cube_impl.scad @@ -0,0 +1,180 @@ +use <_mz_cube_comm.scad>; +use <../../util/shuffle.scad>; + +function update(cells, cell) = + let( + x = cell.x, + y = cell.y, + z = cell.z, + rowY = [for(c = cells[z][y]) if(c.x == x) cell else c], + layerZ = [ + for(r = [0:len(cells[z]) - 1]) + if(r == y) rowY + else cells[z][r] + ] + ) + [ + for(layer = [0:len(cells) - 1]) + if(layer == z) layerZ + else cells[layer] + ]; + +// is (x, y) visited? +function visited(x, y, z, cells) = cells[z][y][x][4]; + +// is (x, y) visitable? +function visitable(x, y, z, cells, layers, rows, columns) = + z >= 0 && z < layers && // z bound + y >= 0 && y < rows && // y bound + x >= 0 && x < columns && // x bound + !visited(x, y, z, cells); // unvisited + +// setting (x, y) as being visited +function set_visited(x, y, z, cells) = update(cells, [x, y, z, get_type(cells[z][y][x]), true]); + +// 0(right), 1(top), 2(left), 3(bottom), 4(up), 5(down) +function rand_dirs(c, seed) = + let(dirs = [0, 1, 2, 3, 4, 5]) + is_undef(seed) ? shuffle(dirs) : shuffle(dirs, c + seed); + +// get x value by dir +_next_x_table = [1, 0, -1, 0, 0, 0]; +function next_x(x, dir, columns, wrapping) = + let(nx = x + _next_x_table[dir]) + wrapping ? (nx + columns) % columns : nx; + +// get y value by dir +_next_y_table = [0, 1, 0, -1, 0, 0]; +function next_y(y, dir, rows, wrapping) = + let(ny = y + _next_y_table[dir]) + wrapping ? (ny + rows) % rows : ny; + +// get z value by dir +_next_z_table = [0, 0, 0, 0, 1, -1]; +function next_z(z, dir, layers, wrapping) = + let(nz = z + _next_z_table[dir]) + wrapping ? (nz + layers) % layers : nz; + +// go right and carve the right wall +function carve_right(x, y, z, cells) = + let(cell = cells[z][y][x]) + update( + cells, + z_y_x_wall(cell) ? [x, y, z, 5, true] : + z_x_wall(cell) ? [x, y, z, 4, true] : + y_x_wall(cell) ? [x, y, z, 1, true] : [x, y, z, 0, true] + ); + +// go top and carve the top wall +function carve_top(x, y, z, cells) = + let(cell = cells[z][y][x]) + update( + cells, + z_y_x_wall(cell) ? [x, y, z, 6, true] : + z_y_wall(cell) ? [x, y, z, 4, true] : + y_x_wall(cell) ? [x, y, z, 2, true] : [x, y, z, 0, true] + ); + +// go up and carve the up wall +function carve_up(x, y, z, cells) = + let(cell = cells[z][y][x]) + update( + cells, + z_y_x_wall(cell) ? [x, y, z, 3, true] : + z_y_wall(cell) ? [x, y, z, 1, true] : + z_x_wall(cell) ? [x, y, z, 6, true] : [x, y, z, 0, true] + ); + +// go left and carve the right wall of the left cell +function carve_left(x, y, z, cells, columns) = + let( + x_minus_one = x - 1, + nx = x_minus_one < 0 ? x_minus_one + columns : x_minus_one + ) + update( + cells, + [nx, y, z, 5, false] + ); + +// go bottom and carve the top wall of the bottom cell +function carve_bottom(x, y, z, cells, rows) = + let( + y_minus_one = y - 1, + ny = y_minus_one < 0 ? y_minus_one + rows : y_minus_one + ) + update( + cells, + [x, ny, z, 6, false] + ); + +// go down and carve the up wall of the down cell +function carve_down(x, y, z, cells, layers) = + let( + z_minus_one = z - 1, + nz = z_minus_one < 0 ? z_minus_one + layers : z_minus_one + ) + update( + cells, + [x, y, nz, 3, false] + ); + +// 0(right), 1(top), 2(left), 3(bottom), 4(up), 5(down) +function carve(dir, x, y, z, cells, layers, rows, columns) = + dir == 0 ? carve_right(x, y, z, cells) : + dir == 1 ? carve_top(x, y, z, cells) : + dir == 2 ? carve_left(x, y, z, cells, columns) : + dir == 3 ? carve_bottom(x, y, z, cells, rows) : + dir == 4 ? carve_up(x, y, z, cells) : + /*dir 5*/ carve_down(x, y, z, cells, layers); + +// find out visitable dirs from (x, y) +function visitable_dirs(r_dirs, x, y, z, cells, layers, rows, columns, x_wrapping, y_wrapping, z_wrapping) = [ + for(dir = r_dirs) + if( + visitable( + next_x(x, dir, columns, x_wrapping), + next_y(y, dir, rows, y_wrapping), + next_z(z, dir, layers, z_wrapping), + cells, layers, rows, columns + ) + ) + dir +]; + +// go maze from (x, y, z) +function go_maze(x, y, z, cells, layers, rows, columns, x_wrapping = false, y_wrapping = false, z_wrapping = false, seed) = + let( + r_dirs = rand_dirs(x + y * columns + z * rows * columns, seed), + v_dirs = visitable_dirs(r_dirs, x, y, z, cells, layers, rows, columns, x_wrapping, y_wrapping, z_wrapping), + nx_cells0 = set_visited(x, y, z, cells), + leng_v_dirs = len(v_dirs) + ) + // have visitable dirs? + leng_v_dirs == 0 ? nx_cells0 : // road closed + // try four directions + let(nxcells1 = next_cells(x, y, z, v_dirs[0], nx_cells0, layers, rows, columns, x_wrapping, y_wrapping, z_wrapping, seed)) + leng_v_dirs == 1 ? nxcells1 : + let(nxcells2 = next_cells(x, y, z, v_dirs[1], nxcells1, layers, rows, columns, x_wrapping, y_wrapping, z_wrapping, seed)) + leng_v_dirs == 2 ? nxcells2 : + let(nxcells3 = next_cells(x, y, z, v_dirs[2], nxcells2, layers, rows, columns, x_wrapping, y_wrapping, z_wrapping, seed)) + leng_v_dirs == 3 ? nxcells3 : + let(nxcells4 = next_cells(x, y, z, v_dirs[3], nxcells3, layers, rows, columns, x_wrapping, y_wrapping, z_wrapping, seed)) + leng_v_dirs == 4 ? nxcells4 : + let(nxcells5 = next_cells(x, y, z, v_dirs[4], nxcells4, layers, rows, columns, x_wrapping, y_wrapping, z_wrapping, seed)) + leng_v_dirs == 5 ? nxcells5 : next_cells(x, y, v_dirs[5], nxcells5, layers, rows, columns, x_wrapping, y_wrapping, z_wrapping, seed); + +function next_cells(x, y, z, dir, cells, layers, rows, columns, x_wrapping, y_wrapping, z_wrapping, seed) = + let( + nx = next_x(x, dir, columns, x_wrapping), + ny = next_y(y, dir, rows, y_wrapping), + nz = next_z(z, dir, layers, z_wrapping) + ) + !visitable(nx, ny, nz, cells, layers, rows, columns) ? // is the dir visitable? + cells : // road closed so return cells directly + go_maze( // try the cell + nx, ny, nz, + carve(dir, x, y, z, cells, layers, rows, columns), + layers, rows, columns, + x_wrapping, y_wrapping, z_wrapping, + seed + ); \ No newline at end of file diff --git a/src/experimental/_impl/_mz_cube_initialize.scad b/src/experimental/_impl/_mz_cube_initialize.scad new file mode 100644 index 00000000..a2a8ab2f --- /dev/null +++ b/src/experimental/_impl/_mz_cube_initialize.scad @@ -0,0 +1,47 @@ +use <_mz_cube_comm.scad>; + +// create a starting maze for being visited later. +function _lrc_maze(layers, rows, columns) = + [ + for(z = [0:layers - 1]) + [ + for(y = [0:rows - 1]) + [ + for(x = [0:columns - 1]) + cell( + x, y, z, + // all cells have up/top/right walls + 7, + // unvisited + false + ) + ] + ] + ]; + +function _mz_mask(mask) = + let( + layers = len(mask), + rows = len(mask[0]), + columns = len(mask[0][1]) + ) + [ + for(z = [0:layers - 1]) [ + for(y = [0:rows - 1]) [ + for(x = [0:columns - 1]) + mask[layers - z - 1][rows - y - 1][x] == 0 ? + cell( + x, y, z, + 8, // mask + true // visited + ) + : + cell( + x, y, z, + // all cells have up/top/right walls + 7, // unvisited + false + ) + ] + ] + ]; \ No newline at end of file diff --git a/src/experimental/mz_cube.scad b/src/experimental/mz_cube.scad new file mode 100644 index 00000000..f3621f35 --- /dev/null +++ b/src/experimental/mz_cube.scad @@ -0,0 +1,107 @@ +/** +* mz_cube.scad +* +* @copyright Justin Lin, 2020 +* @license https://opensource.org/licenses/lgpl-3.0.html +* +* @see https://openhome.cc/eGossip/OpenSCAD/lib3x-mz_cube.html +* +**/ + +use <_impl/_mz_cube_impl.scad>; +use ; + +function mz_cube(layers, rows, columns, start = [0, 0, 0], init_cells, x_wrapping = false, y_wrapping = false, z_wrapping = false, seed) = + let( + init_undef = is_undef(init_cells), + mz = init_undef ? mz_cube_initialize(layers, rows, columns) : init_cells + ) + go_maze( + start.x, + start.y, + start.z, + mz, + len(mz), + len(mz[0]), + len(mz[0][0]), + x_wrapping, + y_wrapping, + z_wrapping, + seed + ); + +/* +use ; +use ; +use ; + +layers = 3; +rows = 4; +columns = 5; +cell_width = 5; +wall_thickness = 3; + +cells = mz_cube(layers, rows, columns); + +difference() { + translate([wall_thickness, wall_thickness, wall_thickness] * 0.95 / -2) + cube([ + columns * cell_width + wall_thickness * .95, + rows * cell_width + wall_thickness * .95, + layers * cell_width + wall_thickness * .95 + ]); + + union() { + for(layer = cells, row = layer, cell = row) { + x = mz_cube_get(cell, "x"); + y = mz_cube_get(cell, "y"); + z = mz_cube_get(cell, "z"); + type = mz_cube_get(cell, "t"); + + translate([x, y, z] * cell_width) { + if(has(["Z_WALL", "Z_Y_WALL", "Z_X_WALL", "Z_Y_X_WALL"], type)) { + translate([0, 0, cell_width]) + cell_wall(cell_width, wall_thickness); + + } + + if(has(["Y_WALL", "Y_X_WALL", "Z_Y_WALL", "Z_Y_X_WALL"], type)) { + translate([0, cell_width, 0]) + rotate([90, 0, 0]) + cell_wall(cell_width, wall_thickness); + } + + if(has(["X_WALL", "Y_X_WALL", "Z_X_WALL", "Z_Y_X_WALL"], type)) { + translate([cell_width, 0, 0]) + rotate([0, -90, 0]) + cell_wall(cell_width, wall_thickness); + } + } + } + + translate([-wall_thickness / 2, -wall_thickness / 2, -wall_thickness / 2]) + cube([cell_width * columns + wall_thickness, cell_width * rows + wall_thickness, wall_thickness]); + + translate([-wall_thickness / 2, -wall_thickness / 2, -wall_thickness / 2]) + cube([wall_thickness, cell_width * rows + wall_thickness, cell_width * layers + wall_thickness]); + + translate([-wall_thickness / 2, -wall_thickness / 2, -wall_thickness / 2]) + cube([cell_width * columns + wall_thickness, wall_thickness, cell_width * layers + wall_thickness]); + } +} + +module cell_wall(cell_width, wall_thickness) { + hull() { + cube(wall_thickness, center = true); + + translate([cell_width, 0]) + cube(wall_thickness, center = true); + + translate([cell_width, cell_width]) + cube(wall_thickness, center = true); + + translate([0, cell_width]) + cube(wall_thickness, center = true); + } +} +*/ diff --git a/src/experimental/mz_cube_get.scad b/src/experimental/mz_cube_get.scad new file mode 100644 index 00000000..34966e62 --- /dev/null +++ b/src/experimental/mz_cube_get.scad @@ -0,0 +1,20 @@ +// NO_WALL = 0; +// Y_WALL = 1; +// X_WALL = 2; +// Y_X_WALL = 3; +// Z_WALL = 4; +// Z_Y_WALL = 5; +// Z_X_WALL = 6; +// Z_Y_X_WALL = 7; +// MASK = 8; + +function mz_cube_get(cell, query) = + let( + i = search(query, [ + ["x", 0], + ["y", 1], + ["z", 2], + ["t", 3] + ])[0] + ) + i != 3 ? cell[i] : ["NO_WALL", "Y_WALL", "X_WALL", "Y_X_WALL", "Z_WALL", "Z_Y_WALL", "Z_X_WALL", "Z_Y_X_WALL", "MASK"][cell[i]]; \ No newline at end of file diff --git a/src/experimental/mz_cube_initialize.scad b/src/experimental/mz_cube_initialize.scad new file mode 100644 index 00000000..b8c4f9bb --- /dev/null +++ b/src/experimental/mz_cube_initialize.scad @@ -0,0 +1,4 @@ +use <_impl/_mz_cube_initialize.scad>; + +function mz_cube_initialize(layers, rows, columns, mask) = + is_undef(mask) ? _lrc_maze(layers, rows, columns) : _mz_mask(mask); \ No newline at end of file