diff --git a/examples/paths.py b/examples/paths.py new file mode 100644 index 0000000..bf6d338 --- /dev/null +++ b/examples/paths.py @@ -0,0 +1,143 @@ + +from markov import * +from markov.wfc import TaggingTileset, Tags, wave_from_tiles, collapse_all_with_callback +from pprint import pprint + +dim = 16 + +tileset = TaggingTileset() + +empty = tileset.add(2.0, "empty", symmetry="X_3d") +line = tileset.add_mul( + 1.0, 2, {"x": "line", "y": Tags(outgoing="empty"), "z": "empty"}, symmetry="I_3d" +) +turn = tileset.add_mul( + 1.0, 4, {"x": "line", "negx": "empty", "z": "empty"}, symmetry="L_3d" +) +x = tileset.add( + 0.1, {"x": Tags(incoming="cross", outgoing="line"), "z": "empty"}, symmetry="X_3d" +) + +down = tileset.add_mul( + 4.0, + 4, + { + "x": "empty", + "negx": Tags(incoming="down", outgoing=["line", "up", "cross"]), + "y": "empty", + "negy": "empty", + "z": lambda i: f"stairs_{i}", + "negz": "empty", + }, +) +up = tileset.add_mul( + 4.0, + 4, + { + "x": Tags(incoming="up", outgoing=["line", "down", "cross"]), + "negx": "empty", + "y": "empty", + "negy": "empty", + "negz": lambda i: f"stairs_{i}", + "z": "empty", + }, +) + +tiles = np.zeros((tileset.tileset.num_tiles(), 5, 5, 5), dtype=np.uint8) +line_vox = load_mkjr_vox("MarkovJunior/resources/tilesets/Paths/Line.vox") +corner_vox = load_mkjr_vox("MarkovJunior/resources/tilesets/Paths/Turn.vox") +down_vox = load_mkjr_vox("MarkovJunior/resources/tilesets/Paths/Down.vox") +up_vox = load_mkjr_vox("MarkovJunior/resources/tilesets/Paths/Up.vox") + + +tiles[x] = load_mkjr_vox("MarkovJunior/resources/tilesets/Paths/X.vox") +#tiles[empty] = load_mkjr_vox("MarkovJunior/resources/tilesets/Paths/Empty.vox") + +for i, slot in enumerate(line): + tiles[slot] = np.rot90(line_vox, axes=(1, 2), k=i) + +for i, slot in enumerate(turn): + tiles[slot] = np.rot90(corner_vox, axes=(1, 2), k=i) + +for i, slot in enumerate(down): + tiles[slot] = np.rot90(down_vox, axes=(1, 2), k=i) + +for i, slot in enumerate(up): + tiles[slot] = np.rot90(up_vox, axes=(1, 2), k=i + 2) + +output = np.zeros((dim * 5, dim * 5, dim * 5), dtype=np.uint8) + + +iters = 0 +while True: + iters += 1 + wfc = tileset.tileset.create_wfc((dim, dim, dim)) + for j in range(dim): + for i in range(dim): + #wfc.collapse((j, i, 0), empty) + wfc.collapse((i, j, dim - 1), empty) + wfc.collapse((i, 0, j), empty) + wfc.collapse((i, dim - 1, j), empty) + wfc.collapse((0, i, j), empty) + wfc.collapse((dim - 1, i, j), empty) + + wfc.partial_collapse( + (i, j, 0), wave_from_tiles([empty, x] + line + down + turn) + ) + found_contradiction = wfc.collapse_all() + if not found_contradiction: + print(iters) + break + + +output = map_3d(wfc.values(), output, tiles) +output[0, :, :] = index_for_colour("N") +output[1, :, :] = index_for_colour("E") +write_usd("pipes.usdc", output) +''' + +rep(output, Prl(Pattern("Y=C", chance=0.25), settings=ONCE)) +rep(output, "Y=B") +print(line_vox, index_for_colour("D")) +print(list(PICO8_PALETTE.srgb[index_for_colour("D")])) +#print(list(PICO8_PALETTE.srgb[80])) + +print(chr(PALETTE_CHARS_TO_INDEX.index(40))) + +rep(output, Pattern('DB=*F', shuffles=[[2,1,0]], flips=[[True, False, False]])) +rep(output, Pattern('FB=FF', shuffles=[[2,1,0]], flips=[[True, False, False]])) +#writer.write(output) + +def chr_for_val(val): + return chr(PALETTE_CHARS_TO_INDEX.index(val)) + +rep(output, One( + "FF,EE=BB,EE", + "pF=*B", + Pattern('FB=BB', shuffles=[[2,1,0]], flips=TOGGLE_X) +)) + +print(up_vox, chr_for_val(18)) + +#rep(output, Pattern('FF,EE=BB,EE')) +#writer.write(output) +#rep(output, Pattern('FF,FD,EE=FF,BD,EE')) +#writer.write(output) +#rep(output, Pattern('FB=BB', shuffles=[[2,1,0]], flips=[[True, False, False]])) +#writer.write(output) +#rep(output, Pattern('BF=BB', shuffles=[[2,1,0]], flips=[[True, False, False]])) +writer.write(output) + + +rep(output, Pattern(( + np.array([0, 42], dtype=np.uint8).reshape((2, 1, 1)), + np.array([index_for_colour("F"), 42], dtype=np.uint8).reshape((2, 1, 1)) +), shuffles=NO_SHUFFLES, flips = NO_FLIPS)) + +rep(output, Pattern(( + np.array([0, index_for_colour("F")], dtype=np.uint8).reshape((2, 1, 1)), + np.array([index_for_colour("F"), index_for_colour("F")], dtype=np.uint8).reshape((2, 1, 1)) +), shuffles=NO_SHUFFLES, flips = NO_FLIPS)) +''' + +#write_usd("pipes.usdc", output) diff --git a/examples/test.py b/examples/test.py new file mode 100644 index 0000000..973de84 --- /dev/null +++ b/examples/test.py @@ -0,0 +1,79 @@ +from markov.wfc import * +import unittest + + +def mk_conns(updates): + conns = { + "x": Tags(), + "negx": Tags(), + "z": Tags(), + "negz": Tags(), + "y": Tags(), + "negy": Tags(), + } + conns.update(updates) + return conns + + +class Tester(unittest.TestCase): + def test_t_i(self): + c = MkJrConnector() + c.add("T", "T_3d") + c.add("I", "I_3d") + + c.connect("I", 1, "T", 1) + c.tiles["T"][0].apply_symmetry() + self.assertEqual( + c.tiles["T"][0].conns, + mk_conns( + { + "negy": Tags(incoming="T_y_0", outgoing="I_negy_0"), + "y": Tags(incoming="T_y_0", outgoing="I_negy_0"), + } + ), + # "{'x': (), 'y': (in: {'T_y_0'} out: {'I_negy_0'}), 'negx': (), 'negy': (in: {'T_y_0'} out: {'I_negy_0'}), 'z': (), 'negz': ()}" + ) + + def test_i_i(self): + c = MkJrConnector() + c.add("I", "I_3d") + + c.connect("I", 1, "I", 1) + c.tiles["I"][0].apply_symmetry() + self.assertEqual( + c.tiles["I"][0].conns, + mk_conns( + { + "negy": Tags("I_y_0", "I_negy_0"), + "y": Tags("I_y_0", "I_negy_0"), + } + ), + ) + + def test_i_i_flipped(self): + c = MkJrConnector() + c.add("I", "I_3d") + + c.connect("I", 1, "I", 0, on_z=True) + self.assertEqual( + c.tiles["I"][0].conns, + mk_conns( + { + "z": Tags(incoming="I_z_0", outgoing="I_negz_1"), + "negz": Tags(incoming="I_negz_0", outgoing="I_z_1"), + } + ), + ) + + """def test_b(self): + c = MkJrConnector() + c.add("I", "I_3d") + + c.connect("I", 0, "I", 1) + self.assertEqual( + str(c.tiles["I"][0].conns), + "{'x': (in: {'I_x_0'} out: {'I_negx_0'}), 'y': (), 'negx': (in: {'I_negx_0'} out: {'I_x_0'}), 'negy': (), 'z': (), 'negz': ()}" + )""" + + +unittest.main() diff --git a/markov/wfc.py b/markov/wfc.py index a2965d7..c484e95 100644 --- a/markov/wfc.py +++ b/markov/wfc.py @@ -1,6 +1,7 @@ from markov import Tileset, load_mkjr_vox, map_3d import os import numpy as np +import types FLIPPED = {"x": "negx", "y": "negy", "negx": "x", "negy": "y", "z": "negz", "negz": "z"} @@ -85,7 +86,7 @@ def merge(self, other): other.outgoing = self.outgoing def __repr__(self): - return "{}" if len(self.outgoing) == 0 else str(self.outgoing) + # return "{}" if len(self.outgoing) == 0 else str(self.outgoing) bidirectional = self.incoming & self.outgoing string = "(" if bidirectional != set(): @@ -116,7 +117,7 @@ def wave_from_tiles(tiles): def apply_symmetry(tags, symmetry): if type(tags) is not dict: - tags = {"x": tags} + tags = {"x": tags, "y": tags, "z": tags} for dir in ["x", "y", "negx", "negy", "z", "negz"]: if not dir in tags: @@ -124,32 +125,20 @@ def apply_symmetry(tags, symmetry): if type(tags[dir]) is str: tags[dir] = Tags(tags[dir]) - if symmetry == "X": + if symmetry.startswith("X"): for dir in ["x", "y", "negx", "negy"]: - for other in tags.values(): - tags[dir].merge(other) - if symmetry == "I": + for other in ["x", "y", "negx", "negy"]: + tags[dir].merge(tags[other]) + if symmetry.startswith("I"): tags["x"].merge(tags["negx"]) tags["y"].merge(tags["negy"]) - if symmetry == "L": + if symmetry.startswith("L"): tags["x"].merge(tags["y"]) tags["negx"].merge(tags["negy"]) - if symmetry == "T": - tags["y"].merge(tags["negy"]) - if symmetry == "X_3d": - for dir in ["x", "y", "negx", "negy", "z", "negz"]: - for other in tags.values(): - tags[dir].merge(other) - if symmetry == "I_3d": - tags["x"].merge(tags["negx"]) - tags["y"].merge(tags["negy"]) - tags["z"].merge(tags["negz"]) - if symmetry == "L_3d": - tags["x"].merge(tags["y"]) - tags["negx"].merge(tags["negy"]) - tags["z"].merge(tags["negz"]) - if symmetry == "T_3d": + if symmetry.startswith("T"): tags["y"].merge(tags["negy"]) + + if symmetry.endswith("_3d"): tags["z"].merge(tags["negz"]) return tags @@ -195,7 +184,14 @@ def add_mul(self, prob, rots, tags, symmetry=""): prob /= rots tags = apply_symmetry(tags, symmetry) for i in range(rots): - res.append(self.add(prob, tags)) + resolved_tags = {} + for dir, tag in tags.items(): + if isinstance(tag, types.FunctionType): + resolved_tags[dir] = tag(i) + else: + resolved_tags[dir] = tag + + res.append(self.add(prob, resolved_tags)) tags = rot_z(tags) return res @@ -437,7 +433,6 @@ def add_and_get_tile_ids(self, tileset): print(" ", dir, variant.conns[dir]) print() - tile_ids[name] = [ tileset.add(variant.weight, variant.conns, symmetry="") for variant in variants diff --git a/src/wfc.rs b/src/wfc.rs index b0d50a6..b52c643 100644 --- a/src/wfc.rs +++ b/src/wfc.rs @@ -380,10 +380,13 @@ impl Wfc { } pub fn set_values(&self, values: &mut [u8]) { - self.array - .iter() - .zip(values) - .for_each(|(wave, value)| *value = wave.trailing_zeros() as u8); + self.array.iter().zip(values).for_each(|(wave, value)| { + *value = if wave.count_ones() == 1 { + wave.trailing_zeros() as u8 + } else { + u8::max_value() + } + }); } #[cfg(test)]