diff --git a/src/mceditlib/blocktypes/__init__.py b/src/mceditlib/blocktypes/__init__.py index b1998b7..2aec2ca 100644 --- a/src/mceditlib/blocktypes/__init__.py +++ b/src/mceditlib/blocktypes/__init__.py @@ -288,35 +288,6 @@ class BlockTypeSet(object): return BlockType(ID, meta, self) - def matchingState(self, internalName, stateDict): - """ - Find the first block with the given name whose state matches all of the keys - and values in stateDict. - - Parameters - ---------- - internalName : unicode - block's internal name - stateDict : dict - the keys and values that the returned state must match - - Returns - ------- - - block: BlockType - - """ - for block in self: - if block.internalName == internalName: - bsd = block.stateDict - for k, v in stateDict.iteritems(): - if bsd.get(k) != v: - break - else: - return block - - return None - def discardIDs(self, blockIDs): blockIDs = set(blockIDs) blocktypes = [b for b in self.allBlocks if b.ID in blockIDs] diff --git a/src/mceditlib/blocktypes/rotation.py b/src/mceditlib/blocktypes/rotation.py index 6ecafa8..f627264 100644 --- a/src/mceditlib/blocktypes/rotation.py +++ b/src/mceditlib/blocktypes/rotation.py @@ -3,6 +3,7 @@ """ from __future__ import absolute_import, division, print_function, unicode_literals import logging +from collections import defaultdict import numpy @@ -19,15 +20,45 @@ def blankRotationTable(): return table -def yAxisTable(blocktypes): - mapping = { - 'north': 'east', - 'east': 'south', - 'south': 'west', - 'west': 'north', +class BlockRotations(object): + mappings = { + 'y': { + 'north': 'east', + 'east': 'south', + 'south': 'west', + 'west': 'north', + }, + 'x': { + 'up': 'south', + 'south': 'down', + 'down': 'north', + 'north': 'up', + }, + 'z': { + 'east': 'up', + 'up': 'west', + 'west': 'down', + 'down': 'east', + } } - rail_shapes = { + axisMappings = { + 'y': { + 'x': 'z', + 'z': 'x', + }, + 'x': { + 'y': 'z', + 'z': 'y', + }, + 'z': { + 'x': 'y', + 'y': 'x', + }, + + } + + railShapes = { 'ascending_north': 'ascending_east', 'ascending_east': 'ascending_south', 'ascending_south': 'ascending_west', @@ -39,84 +70,116 @@ def yAxisTable(blocktypes): 'north_west': 'north_east', } - table = blankRotationTable() - for block in blocktypes: - stateString = block.blockState - if not len(stateString): - continue + def __init__(self, blocktypes): + self.blocktypes = blocktypes - try: - state = parseBlockstate(stateString) - except: - log.exception("Error parsing blockstate: %s", stateString) - continue + self.blocksByInternalName = defaultdict(list) - # First pass: facing=north and similar - newState = {} - for k, v in state.items(): - n = mapping.get(v) - if n: - newState[k] = n + for block in self.blocktypes: + self.blocksByInternalName[block.internalName].append(block) + + self.rotateY90 = self.buildTable(axis='y') + self.rotateX90 = self.buildTable(axis='x') + self.rotateZ90 = self.buildTable(axis='z') + + def buildTable(self, axis): + mapping = self.mappings[axis] + axisMapping = self.axisMappings[axis] + + table = blankRotationTable() + + for block in self.blocktypes: + state = block.stateDict + if not len(state): + continue + + # First pass: facing=north and similar + newState = {} + for k, v in state.items(): + n = mapping.get(v) + if n: + newState[k] = n + else: + newState[k] = v + + state = newState + newState = dict(state) + + # Second pass: north=true and similar + for k, v in mapping.items(): + if k in state: + if state[k] == 'true': + newState[k] = 'false' + newState[v] = 'true' + + state = newState + + if axis == 'y': + # For signs and banners: rotation=10 and similar + + if 'rotation' in state: + rotation = (int(state['rotation']) + 4) % 16 + state['rotation'] = unicode(rotation) + + # For rails, powered rails, etc: shape=north_east + + if 'shape' in state: + shape = state['shape'] + + newShape = self.railShapes.get(shape) + if newShape: + state['shape'] = newShape + + # For logs and such: axis=x and similar + + if 'axis' in state: + axis = state['axis'] + axis = axisMapping.get(axis, axis) + state['axis'] = axis + + #print("Changed %s \nto %s" % (stateString, newStateString)) + + newBlock = self.matchingState(block.internalName, state) + if newBlock is block: + pass + # elif newBlock is None: + # newStateString = joinBlockstate(state) + # print("no mapping for %s%s" % (block.internalName, newStateString)) + elif newBlock is not None: + # print("Changed %s \nto %s" % (block, newBlock)) + table[block.ID, block.meta] = [newBlock.ID, newBlock.meta] + + return table + + def matchingState(self, internalName, stateDict): + """ + Find the first block with the given name whose state matches all of the keys + and values in stateDict. + + Parameters + ---------- + internalName : unicode + block's internal name + stateDict : dict + the keys and values that the returned state must match + + Returns + ------- + + block: BlockType + + """ + for b in self.blocksByInternalName[internalName]: + bsd = b.stateDict + for k, v in stateDict.iteritems(): + if bsd.get(k) != v: + break else: - newState[k] = v + return b - state = newState - newState = dict(state) + return None - # Second pass: north=true and similar - for k, v in mapping.items(): - if k in state: - if state[k] == 'true': - newState[k] = 'false' - newState[v] = 'true' - - # For signs and banners: rotation=10 and similar - # y-axis only - - state = newState - - if 'rotation' in state: - rotation = (int(state['rotation']) + 4) % 16 - state['rotation'] = unicode(rotation) - - # For logs and such: axis=x and similar - - if 'axis' in state: - axis = state['axis'] - - # y-axis only - if axis == 'x': - axis = 'z' - elif axis == 'z': - axis = 'x' - - state['axis'] = axis - - # For rails, powered rails, etc: shape=north_east - # y-axis only - - if 'shape' in state: - shape = state['shape'] - - newShape = rail_shapes.get(shape) - if newShape: - state['shape'] = newShape - - - #print("Changed %s \nto %s" % (stateString, newStateString)) - - newBlock = blocktypes.matchingState(block.internalName, state) - if newBlock is block: - pass - elif newBlock is None: - newStateString = joinBlockstate(state) - # print("no mapping for %s%s" % (block.internalName, newStateString)) - else: - # print("Changed %s \nto %s" % (block, newBlock)) - table[block.ID, block.meta] = [newBlock.ID, newBlock.meta] - - return table def xxxtest_yAxisTable(): from . import PCBlockTypeSet @@ -143,7 +206,7 @@ def main(): from timeit import timeit blocktypes = PCBlockTypeSet() - secs = timeit(lambda: yAxisTable(blocktypes), number=1) + secs = timeit(lambda: BlockRotations(blocktypes), number=1) print("Time: %0.3f" % secs) assert secs < 0.1