diff --git a/pyffi/VERSION b/pyffi/VERSION index ccd8c05a..5a0cbf35 100644 --- a/pyffi/VERSION +++ b/pyffi/VERSION @@ -1 +1 @@ -2.2.4.dev4 +2.2.4.dev5 diff --git a/pyffi/formats/nif/__init__.py b/pyffi/formats/nif/__init__.py index d7def029..a8436c25 100644 --- a/pyffi/formats/nif/__init__.py +++ b/pyffi/formats/nif/__init__.py @@ -350,11 +350,10 @@ from itertools import repeat, chain import logging -import math # math.pi +import math # math.pi import os import re import struct -import sys import warnings import weakref @@ -364,7 +363,7 @@ import pyffi.object_models from pyffi.object_models.xml import FileFormat import pyffi.utils.inertia -from pyffi.utils.mathutils import * # XXX todo get rid of from XXX import * +from pyffi.utils.mathutils import * # XXX todo get rid of from XXX import * import pyffi.utils.mopp import pyffi.utils.tristrip import pyffi.utils.vertex_cache @@ -376,14 +375,12 @@ from pyffi.object_models.xml.struct_ import StructBase - class NifFormat(FileFormat): """This class contains the generated classes from the xml.""" xml_file_name = 'nif.xml' # where to look for nif.xml and in what order: NIFXMLPATH env var, # or NifFormat module directory - xml_file_path = [os.getenv('NIFXMLPATH'), - os.path.join(os.path.dirname(__file__), "nifxml")] + xml_file_path = [os.getenv('NIFXMLPATH'), os.path.join(os.path.dirname(__file__), "nifxml")] # filter for recognizing NIF files by extension # .kf are NIF files containing keyframes # .kfa are NIF files containing keyframes in DAoC style @@ -403,7 +400,7 @@ class NifFormat(FileFormat): ulittle32 = pyffi.object_models.common.ULittle32 int = pyffi.object_models.common.Int uint = pyffi.object_models.common.UInt - byte = pyffi.object_models.common.UByte # not a typo + byte = pyffi.object_models.common.UByte # not a typo char = pyffi.object_models.common.Char short = pyffi.object_models.common.Short ushort = pyffi.object_models.common.UShort @@ -416,6 +413,7 @@ class NifFormat(FileFormat): class StringOffset(pyffi.object_models.common.Int): """This is just an integer with -1 as default value.""" + def __init__(self, **kwargs): pyffi.object_models.common.Int.__init__(self, **kwargs) self.set_value(-1) @@ -432,6 +430,7 @@ class bool(BasicBase, EditableBoolComboBox): >>> i.get_value() True """ + def __init__(self, **kwargs): BasicBase.__init__(self, **kwargs) self.set_value(False) @@ -464,20 +463,16 @@ def get_hash(self, data=None): def read(self, stream, data): if data.version > 0x04000002: - value, = struct.unpack(data._byte_order + 'B', - stream.read(1)) + value, = struct.unpack(data._byte_order + 'B', stream.read(1)) else: - value, = struct.unpack(data._byte_order + 'I', - stream.read(4)) + value, = struct.unpack(data._byte_order + 'I', stream.read(4)) self._value = bool(value) def write(self, stream, data): if data.version > 0x04000002: - stream.write(struct.pack(data._byte_order + 'B', - int(self._value))) + stream.write(struct.pack(data._byte_order + 'B', int(self._value))) else: - stream.write(struct.pack(data._byte_order + 'I', - int(self._value))) + stream.write(struct.pack(data._byte_order + 'I', int(self._value))) class Flags(pyffi.object_models.common.UShort): def __str__(self): @@ -488,6 +483,7 @@ class Ref(BasicBase): _is_template = True _has_links = True _has_refs = True + def __init__(self, **kwargs): BasicBase.__init__(self, **kwargs) self._template = kwargs.get("template") @@ -500,7 +496,7 @@ def set_value(self, value): if value is None: self._value = None else: - if self._template != None: + if self._template is not None: if not isinstance(value, self._template): raise TypeError( 'expected an instance of %s but got instance of %s' @@ -517,9 +513,8 @@ def get_hash(self, data=None): return None def read(self, stream, data): - self.set_value(None) # fix_links will set this field - block_index, = struct.unpack(data._byte_order + 'i', - stream.read(4)) + self.set_value(None) # fix_links will set this field + block_index, = struct.unpack(data._byte_order + 'i', stream.read(4)) data._link_stack.append(block_index) def write(self, stream, data): @@ -531,36 +526,35 @@ def write(self, stream, data): try: block_index = data._block_index_dct[self.get_value()] except KeyError: - logging.getLogger("pyffi.nif.ref").warn( - "%s block is missing from the nif tree:" - " omitting reference" - % self.get_value().__class__.__name__) + logging.getLogger("pyffi.nif.ref").warning( + "{0} block is missing from the nif tree: omitting reference".format( + self.get_value().__class__.__name__)) # -1: link by number, 0: link by pointer block_index = -1 if data.version >= 0x0303000D else 0 - stream.write(struct.pack( - data._byte_order + 'i', block_index)) + + stream.write(struct.pack(data._byte_order + 'i', block_index)) def fix_links(self, data): """Fix block links.""" block_index = data._link_stack.pop(0) # case when there's no link if data.version >= 0x0303000D: - if block_index == -1: # link by block number + if block_index == -1: # link by block number self.set_value(None) return else: - if block_index == 0: # link by pointer + if block_index == 0: # link by pointer self.set_value(None) return + # other case: look up the link and check the link type block = data._block_dct[block_index] self.set_value(block) if self._template != None: if not isinstance(block, self._template): - #raise TypeError('expected an instance of %s but got instance of %s'%(self._template, block.__class__)) - logging.getLogger("pyffi.nif.ref").warn( - "Expected an %s but got %s: ignoring reference." - % (self._template, block.__class__)) + # raise TypeError('expected an instance of %s but got instance of %s'%(self._template, block.__class__)) + logging.getLogger("pyffi.nif.ref").warning( + "Expected an {0} but got {1}: ignoring reference".format(self._template, block.__class__)) def get_links(self, data=None): val = self.get_value() @@ -576,8 +570,7 @@ def get_refs(self, data=None): else: return [] - def replace_global_node(self, oldbranch, newbranch, - edge_filter=EdgeFilter()): + def replace_global_node(self, oldbranch, newbranch, edge_filter=EdgeFilter()): """ >>> from pyffi.formats.nif import NifFormat >>> x = NifFormat.NiNode() @@ -600,7 +593,7 @@ def replace_global_node(self, oldbranch, newbranch, if self.get_value() is oldbranch: # set_value takes care of template type self.set_value(newbranch) - #print("replacing", repr(oldbranch), "->", repr(newbranch)) + # print("replacing", repr(oldbranch), "->", repr(newbranch)) if self.get_value() is not None: self.get_value().replace_global_node(oldbranch, newbranch) @@ -626,16 +619,15 @@ def set_value(self, value): if value is None: self._value = None else: - if self._template != None: + if self._template is not None: if not isinstance(value, self._template): - raise TypeError( - 'expected an instance of %s but got instance of %s' - % (self._template, value.__class__)) + raise TypeError('expected an instance of {0} but got instance of {1}'.format(self._template, + value.__class__)) self._value = weakref.ref(value) def __str__(self): # avoid infinite recursion - return '%s instance at 0x%08X'%(self._value.__class__, id(self._value)) + return '%s instance at 0x%08X' % (self._value.__class__, id(self._value)) def get_refs(self, data=None): return [] @@ -643,12 +635,11 @@ def get_refs(self, data=None): def get_hash(self, data=None): return None - def replace_global_node(self, oldbranch, newbranch, - edge_filter=EdgeFilter()): + def replace_global_node(self, oldbranch, newbranch, edge_filter=EdgeFilter()): # overridden to avoid infinite recursion if self.get_value() is oldbranch: self.set_value(newbranch) - #print("replacing", repr(oldbranch), "->", repr(newbranch)) + # print("replacing", repr(oldbranch), "->", repr(newbranch)) class LineString(BasicBase): """Basic type for strings ending in a newline character (0x0a). @@ -674,6 +665,7 @@ class LineString(BasicBase): >>> str(m) 'Hi There' """ + def __init__(self, **kwargs): BasicBase.__init__(self, **kwargs) self.set_value('') @@ -688,7 +680,7 @@ def __str__(self): return pyffi.object_models.common._as_str(self._value) def get_size(self, data=None): - return len(self._value) + 1 # +1 for trailing endline + return len(self._value) + 1 # +1 for trailing endline def get_hash(self, data=None): return self.get_value() @@ -758,7 +750,7 @@ def version_string(version, modification=None): 'Joymaster HS1 Object Format - (JMI), Version 20.3.0.9' """ if version == -1 or version is None: - raise ValueError('No string for version %s.'%version) + raise ValueError('No string for version %s.' % version) if modification == "neosteam": if version != 0x0A010000: raise ValueError("NeoSteam must have version 0x0A010000.") @@ -770,9 +762,10 @@ def version_string(version, modification=None): if version == 0x03000300: v = "3.03" elif version <= 0x03010000: - v = "%i.%i"%((version >> 24) & 0xff, (version >> 16) & 0xff) + v = "%i.%i" % ((version >> 24) & 0xff, (version >> 16) & 0xff) else: - v = "%i.%i.%i.%i"%((version >> 24) & 0xff, (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff) + v = "%i.%i.%i.%i" % ( + (version >> 24) & 0xff, (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff) if modification == "ndoors": return "NDSNIF....@....@...., Version %s" % v elif modification == "jmihs1": @@ -781,6 +774,7 @@ def version_string(version, modification=None): return "%s File Format, Version %s" % (s, v) class FileVersion(pyffi.object_models.common.UInt): + def set_value(self): raise NotImplementedError("file version is specified via data") @@ -789,34 +783,27 @@ def __str__(self): def read(self, stream, data): modification = data.modification - ver, = struct.unpack(' 10000: - raise ValueError('string too long (0x%08X at 0x%08X)' - % (n, stream.tell())) + raise ValueError('string too long (0x%08X at 0x%08X)' % (n, stream.tell())) self._value = stream.read(n) def write(self, stream, data): @@ -911,11 +895,9 @@ def write(self, stream, data): data._byte_order + 'i', data._string_list.index(self._value))) except ValueError: - raise ValueError( - "string '%s' not in string list" % self._value) + raise ValueError("string '%s' not in string list" % self._value) else: - stream.write(struct.pack(data._byte_order + 'I', - len(self._value))) + stream.write(struct.pack(data._byte_order + 'I', len(self._value))) stream.write(self._value) def get_strings(self, data): @@ -931,6 +913,7 @@ def get_hash(self, data=None): class FilePath(string): """A file path.""" + def get_hash(self, data=None): """Returns a case insensitive hash value.""" return self.get_value().lower() @@ -938,9 +921,10 @@ def get_hash(self, data=None): class ByteArray(BasicBase): """Array (list) of bytes. Implemented as basic type to speed up reading and also to prevent data to be dumped by __str__.""" + def __init__(self, **kwargs): BasicBase.__init__(self, **kwargs) - self.set_value("".encode()) # b'' for > py25 + self.set_value("".encode()) # b'' for > py25 def get_value(self): return self._value @@ -955,13 +939,11 @@ def get_hash(self, data=None): return self._value.__hash__() def read(self, stream, data): - size, = struct.unpack(data._byte_order + 'I', - stream.read(4)) + size, = struct.unpack(data._byte_order + 'I', stream.read(4)) self._value = stream.read(size) def write(self, stream, data): - stream.write(struct.pack(data._byte_order + 'I', - len(self._value))) + stream.write(struct.pack(data._byte_order + 'I', len(self._value))) stream.write(self._value) def __str__(self): @@ -970,6 +952,7 @@ def __str__(self): class ByteMatrix(BasicBase): """Matrix of bytes. Implemented as basic type to speed up reading and to prevent data being dumped by __str__.""" + def __init__(self, **kwargs): BasicBase.__init__(self, **kwargs) self.set_value([]) @@ -978,14 +961,14 @@ def get_value(self): return self._value def set_value(self, value): - assert(isinstance(value, list)) + assert (isinstance(value, list)) if value: size1 = len(value[0]) for x in value: # TODO fix this for py3k - #assert(isinstance(x, basestring)) - assert(len(x) == size1) - self._value = value # should be a list of strings of bytes + # assert(isinstance(x, basestring)) + assert (len(x) == size1) + self._value = value # should be a list of strings of bytes def get_size(self, data=None): if len(self._value) == 0: @@ -994,25 +977,21 @@ def get_size(self, data=None): return len(self._value) * len(self._value[0]) + 8 def get_hash(self, data=None): - return tuple( x.__hash__() for x in self._value ) + return tuple(x.__hash__() for x in self._value) def read(self, stream, data): - size1, = struct.unpack(data._byte_order + 'I', - stream.read(4)) - size2, = struct.unpack(data._byte_order + 'I', - stream.read(4)) + size1, = struct.unpack(data._byte_order + 'I', stream.read(4)) + size2, = struct.unpack(data._byte_order + 'I', stream.read(4)) self._value = [] for i in range(size2): self._value.append(stream.read(size1)) def write(self, stream, data): if self._value: - stream.write(struct.pack(data._byte_order + 'I', - len(self._value[0]))) + stream.write(struct.pack(data._byte_order + 'I', len(self._value[0]))) else: stream.write(struct.pack(data._byte_order + 'I', 0)) - stream.write(struct.pack(data._byte_order + 'I', - len(self._value))) + stream.write(struct.pack(data._byte_order + 'I', len(self._value))) for x in self._value: stream.write(x) @@ -1050,12 +1029,12 @@ def version_number(version_str): try: ver_list = [int(x) for x in version_str.split('.')] except ValueError: - return -1 # version not supported (i.e. version_str '10.0.1.3a' would trigger this) + return -1 # version not supported (i.e. version_str '10.0.1.3a' would trigger this) if len(ver_list) > 4 or len(ver_list) < 1: - return -1 # version not supported + return -1 # version not supported for ver_digit in ver_list: if (ver_digit | 0xff) > 0xff: - return -1 # version not supported + return -1 # version not supported while len(ver_list) < 4: ver_list.append(0) return (ver_list[0] << 24) + (ver_list[1] << 16) + (ver_list[2] << 8) + ver_list[3] @@ -1135,16 +1114,19 @@ def __init__(self, version=0x04000002, user_version=0, user_version_2=0): def _getVersion(self): return self._version_value_.get_value() + def _setVersion(self, value): self._version_value_.set_value(value) - + def _getUserVersion(self): return self._user_version_value_.get_value() + def _setUserVersion(self, value): self._user_version_value_.set_value(value) def _getUserVersion2(self): return self._user_version_2_value_.get_value() + def _setUserVersion2(self, value): self._user_version_2_value_.set_value(value) @@ -1172,6 +1154,7 @@ def inspect_version_only(self, stream): s = stream.readline(64).rstrip() finally: stream.seek(pos) + self.modification = None if s.startswith("NetImmerse File Format, Version ".encode("ascii")): version_str = s[32:].decode("ascii") @@ -1189,12 +1172,15 @@ def inspect_version_only(self, stream): self.modification = "jmihs1" else: raise ValueError("Not a NIF file.") + try: ver = NifFormat.version_number(version_str) except: raise ValueError("Nif version %s not supported." % version_str) - if not ver in list(NifFormat.versions.values()): + + if ver not in list(NifFormat.versions.values()): raise ValueError("Nif version %s not supported." % version_str) + # check version integer and user version userver = 0 userver2 = 0 @@ -1215,12 +1201,12 @@ def inspect_version_only(self, stream): " 0x%08X." % (version_str, ver_int)) elif self.modification == "neosteam": if ver_int != 0x08F35232: - raise ValueError( - "Corrupted NIF file: invalid NeoSteam version.") + raise ValueError("Corrupted NIF file: invalid NeoSteam version.") + elif self.modification == "ndoors": if ver_int != 0x73615F67: - raise ValueError( - "Corrupted NIF file: invalid Ndoors version.") + raise ValueError("Corrupted NIF file: invalid Ndoors version.") + if ver >= 0x14000004: endian_type, = struct.unpack('= 0x0A010000: userver, = struct.unpack('= 10: - stream.read(4) # number of blocks + stream.read(4) # number of blocks userver2, = struct.unpack('= 0x05000001: # note the 0xfff mask: required for the NiPhysX blocks - block_type = self.header.block_types[ - self.header.block_type_index[block_num] & 0xfff] + block_type = self.header.block_types[self.header.block_type_index[block_num] & 0xfff] block_type = block_type.decode("ascii") + # handle data stream classes if block_type.startswith("NiDataStream\x01"): block_type, data_stream_usage, data_stream_access = block_type.split("\x01") data_stream_usage = int(data_stream_usage) data_stream_access = int(data_stream_access) + # read dummy integer # bhk blocks are *not* preceeded by a dummy if self.version <= 0x0A01006A and not block_type.startswith("bhk"): - dummy, = struct.unpack(self._byte_order + 'I', - stream.read(4)) + dummy, = struct.unpack(self._byte_order + 'I', stream.read(4)) if dummy != 0: - raise NifFormat.NifError( - 'non-zero block tag 0x%08X at 0x%08X)' - %(dummy, stream.tell())) + raise NifFormat.NifError('non-zero block tag 0x%08X at 0x%08X)' % (dummy, stream.tell())) else: block_type = NifFormat.SizedString() block_type.read(stream, self) @@ -1359,41 +1341,42 @@ def read(self, stream): # earlier versions # the number of blocks is not in the header # and a special block type string marks the end of the file - if block_type == "End Of File": break + if block_type == "End Of File": + break # read the block index, which is probably the memory # location of the object when it was written to # memory else: - block_index, = struct.unpack( - self._byte_order + 'I', stream.read(4)) + block_index, = struct.unpack(self._byte_order + 'I', stream.read(4)) if block_index in self._block_dct: raise NifFormat.NifError( - 'duplicate block index (0x%08X at 0x%08X)' - %(block_index, stream.tell())) + 'duplicate block index (0x%08X at 0x%08X)' % (block_index, stream.tell())) # create the block try: block = getattr(NifFormat, block_type)() except AttributeError: - raise ValueError( - "Unknown block type '%s'." % block_type) - logger.debug("Reading %s block at 0x%08X" - % (block_type, stream.tell())) + raise ValueError("Unknown block type '%s'." % block_type) + logger.debug("Reading %s block at 0x%08X" % (block_type, stream.tell())) + # read the block try: block.read(stream, self) except: logger.exception("Reading %s failed" % block.__class__) - #logger.error("link stack: %s" % self._link_stack) - #logger.error("block that failed:") - #logger.error("%s" % block) + # logger.error("link stack: %s" % self._link_stack) + # logger.error("block that failed:") + # logger.error("%s" % block) raise + # complete NiDataStream data if block_type == "NiDataStream": block.usage = data_stream_usage block.access.populate_attribute_values(data_stream_access, self) + # store block index self._block_dct[block_index] = block self.blocks.append(block) + # check block size if self.version >= 0x14020007: logger.debug("Checking block size") @@ -1407,9 +1390,11 @@ def read(self, stream): % (extra_size, block.__class__.__name__)) # skip bytes that were missed stream.seek(extra_size, 1) + # add block to roots if flagged as such if is_root: self.roots.append(block) + # check if we are done block_num += 1 if self.version >= 0x0303000D: @@ -1429,9 +1414,11 @@ def read(self, stream): for block in self.blocks: block.fix_links(self) ftr.fix_links(self) + # the link stack should be empty now if self._link_stack: raise NifFormat.NifError('not all links have been popped from the stack (bug?)') + # add root objects in footer to roots list if self.version >= 0x0303000D: for root in ftr.roots: @@ -1447,10 +1434,10 @@ def write(self, stream): """ logger = logging.getLogger("pyffi.nif.data") # set up index and type dictionary - self.blocks = [] # list of all blocks to be written - self._block_index_dct = {} # maps block to block index - block_type_list = [] # list of all block type strings - block_type_dct = {} # maps block to block type string index + self.blocks = [] # list of all blocks to be written + self._block_index_dct = {} # maps block to block index + block_type_list = [] # list of all block type strings + block_type_dct = {} # maps block to block type string index self._string_list = [] for root in self.roots: self._makeBlockList(root, @@ -1459,10 +1446,10 @@ def write(self, stream): for block in root.tree(): self._string_list.extend( block.get_strings(self)) - self._string_list = list(set(self._string_list)) # ensure unique elements - #print(self._string_list) # debug + self._string_list = list(set(self._string_list)) # ensure unique elements + # print(self._string_list) # debug - self.header.user_version = self.user_version # TODO dedicated type for user_version similar to FileVersion + self.header.user_version = self.user_version # TODO dedicated type for user_version similar to FileVersion # for oblivion CS; apparently this is the version of the bhk blocks self.header.user_version_2 = self.user_version_2 self.header.num_blocks = len(self.blocks) @@ -1484,7 +1471,7 @@ def write(self, stream): self.header.block_size.update_size() for i, block in enumerate(self.blocks): self.header.block_size[i] = block.get_size(data=self) - #if verbose >= 2: + # if verbose >= 2: # print(hdr) # set up footer @@ -1496,7 +1483,7 @@ def write(self, stream): # write the file logger.debug("Writing header") - #logger.debug("%s" % self.header) + # logger.debug("%s" % self.header) self.header.write(stream, self) for block in self.blocks: # signal top level object if block is a root object @@ -1511,8 +1498,7 @@ def write(self, stream): else: # write block type string s = NifFormat.SizedString() - assert(block_type_list[block_type_dct[block]] - == block.__class__.__name__) # debug + assert (block_type_list[block_type_dct[block]] == block.__class__.__name__) # debug s.set_value(block.__class__.__name__) s.write(stream, self) # write block index @@ -1528,8 +1514,7 @@ def write(self, stream): s.write(stream, self) ftr.write(stream, self) - def _makeBlockList( - self, root, block_index_dct, block_type_list, block_type_dct): + def _makeBlockList(self, root, block_index_dct, block_type_list, block_type_dct): """This is a helper function for write to set up the list of all blocks, the block index map, and the block type map. @@ -1556,8 +1541,7 @@ def _blockChildBeforeParent(block): :type block: L{NifFormat.NiObject} :return: ``True`` if child should come first, ``False`` otherwise. """ - return (isinstance(block, NifFormat.bhkRefObject) - and not isinstance(block, NifFormat.bhkConstraint)) + return isinstance(block, NifFormat.bhkRefObject) and not isinstance(block, NifFormat.bhkConstraint) # block already listed? if so, return if root in self.blocks: @@ -1566,8 +1550,7 @@ def _blockChildBeforeParent(block): block_type = root.__class__.__name__ # special case: NiDataStream stores part of data in block type list if block_type == "NiDataStream": - block_type = ("NiDataStream\x01%i\x01%i" - % (root.usage, root.access.get_attributes_values(self))) + block_type = ("NiDataStream\x01%i\x01%i" % (root.usage, root.access.get_attributes_values(self))) try: block_type_dct[root] = block_type_list.index(block_type) except ValueError: @@ -1579,8 +1562,7 @@ def _blockChildBeforeParent(block): if isinstance(root, NifFormat.bhkConstraint): for entity in root.entities: if entity is not None: - self._makeBlockList( - entity, block_index_dct, block_type_list, block_type_dct) + self._makeBlockList(entity, block_index_dct, block_type_list, block_type_dct) children_left = [] # add children that come before the block @@ -1613,16 +1595,13 @@ def read(self, stream, data): if modification == "neosteam": extrabyte, = struct.unpack(" NifFormat.EPSILON - or abs(self.m_12) > NifFormat.EPSILON - or abs(self.m_13) > NifFormat.EPSILON - or abs(self.m_21) > NifFormat.EPSILON - or abs(self.m_22 - 1.0) > NifFormat.EPSILON - or abs(self.m_23) > NifFormat.EPSILON - or abs(self.m_31) > NifFormat.EPSILON - or abs(self.m_32) > NifFormat.EPSILON - or abs(self.m_33 - 1.0) > NifFormat.EPSILON): + if (abs(self.m_11 - 1.0) > NifFormat.EPSILON + or abs(self.m_12) > NifFormat.EPSILON + or abs(self.m_13) > NifFormat.EPSILON + or abs(self.m_21) > NifFormat.EPSILON + or abs(self.m_22 - 1.0) > NifFormat.EPSILON + or abs(self.m_23) > NifFormat.EPSILON + or abs(self.m_31) > NifFormat.EPSILON + or abs(self.m_32) > NifFormat.EPSILON + or abs(self.m_33 - 1.0) > NifFormat.EPSILON): return False else: return True @@ -1749,9 +1726,7 @@ def is_scale_rotation(self): mat = self * self_transpose # off diagonal elements should be zero - if (abs(mat.m_12) + abs(mat.m_13) - + abs(mat.m_21) + abs(mat.m_23) - + abs(mat.m_31) + abs(mat.m_32)) > 0.01: + if (abs(mat.m_12) + abs(mat.m_13) + abs(mat.m_21) + abs(mat.m_23) + abs(mat.m_31) + abs(mat.m_32)) > 0.01: return False # diagonal elements should be equal (to scale^2) @@ -1773,20 +1748,20 @@ def is_rotation(self): def get_determinant(self): """Return determinant.""" - return (self.m_11*self.m_22*self.m_33 - +self.m_12*self.m_23*self.m_31 - +self.m_13*self.m_21*self.m_32 - -self.m_31*self.m_22*self.m_13 - -self.m_21*self.m_12*self.m_33 - -self.m_11*self.m_32*self.m_23) + return (self.m_11 * self.m_22 * self.m_33 + + self.m_12 * self.m_23 * self.m_31 + + self.m_13 * self.m_21 * self.m_32 + - self.m_31 * self.m_22 * self.m_13 + - self.m_21 * self.m_12 * self.m_33 + - self.m_11 * self.m_32 * self.m_23) def get_scale(self): """Gets the scale (assuming is_scale_rotation is true!).""" scale = self.get_determinant() if scale < 0: - return -((-scale)**(1.0/3.0)) + return -((-scale) ** (1.0 / 3.0)) else: - return scale**(1.0/3.0) + return scale ** (1.0 / 3.0) def get_scale_rotation(self): """Decompose the matrix into scale and rotation, where scale is a float @@ -1826,37 +1801,36 @@ def get_scale_quat(self): if trace > NifFormat.EPSILON: s = (trace ** 0.5) * 2 - quat.x = -( rot.m_32 - rot.m_23 ) / s - quat.y = -( rot.m_13 - rot.m_31 ) / s - quat.z = -( rot.m_21 - rot.m_12 ) / s + quat.x = -(rot.m_32 - rot.m_23) / s + quat.y = -(rot.m_13 - rot.m_31) / s + quat.z = -(rot.m_21 - rot.m_12) / s quat.w = 0.25 * s elif rot.m_11 > max((rot.m_22, rot.m_33)): - s = (( 1.0 + rot.m_11 - rot.m_22 - rot.m_33 ) ** 0.5) * 2 + s = ((1.0 + rot.m_11 - rot.m_22 - rot.m_33) ** 0.5) * 2 quat.x = 0.25 * s - quat.y = (rot.m_21 + rot.m_12 ) / s - quat.z = (rot.m_13 + rot.m_31 ) / s - quat.w = -(rot.m_32 - rot.m_23 ) / s + quat.y = (rot.m_21 + rot.m_12) / s + quat.z = (rot.m_13 + rot.m_31) / s + quat.w = -(rot.m_32 - rot.m_23) / s elif rot.m_22 > rot.m_33: - s = (( 1.0 + rot.m_22 - rot.m_11 - rot.m_33 ) ** 0.5) * 2 - quat.x = (rot.m_21 + rot.m_12 ) / s + s = ((1.0 + rot.m_22 - rot.m_11 - rot.m_33) ** 0.5) * 2 + quat.x = (rot.m_21 + rot.m_12) / s quat.y = 0.25 * s - quat.z = (rot.m_32 + rot.m_23 ) / s - quat.w = -(rot.m_13 - rot.m_31 ) / s + quat.z = (rot.m_32 + rot.m_23) / s + quat.w = -(rot.m_13 - rot.m_31) / s else: - s = (( 1.0 + rot.m_33 - rot.m_11 - rot.m_22 ) ** 0.5) * 2 - quat.x = (rot.m_13 + rot.m_31 ) / s - quat.y = (rot.m_32 + rot.m_23 ) / s + s = ((1.0 + rot.m_33 - rot.m_11 - rot.m_22) ** 0.5) * 2 + quat.x = (rot.m_13 + rot.m_31) / s + quat.y = (rot.m_32 + rot.m_23) / s quat.z = 0.25 * s - quat.w = -(rot.m_21 - rot.m_12 ) / s + quat.w = -(rot.m_21 - rot.m_12) / s return scale, quat - def get_inverse(self): """Get inverse (assuming is_scale_rotation is true!).""" # transpose inverts rotation but keeps the scale # dividing by scale^2 inverts the scale as well - return self.get_transpose() / (self.m_11**2 + self.m_12**2 + self.m_13**2) + return self.get_transpose() / (self.m_11 ** 2 + self.m_12 ** 2 + self.m_13 ** 2) def __mul__(self, rhs): if isinstance(rhs, (float, int)): @@ -1889,7 +1863,7 @@ def __mul__(self, rhs): return mat else: raise TypeError( - "do not know how to multiply Matrix33 with %s"%rhs.__class__) + "do not know how to multiply Matrix33 with %s" % rhs.__class__) def __div__(self, rhs): if isinstance(rhs, (float, int)): @@ -1906,31 +1880,31 @@ def __div__(self, rhs): return mat else: raise TypeError( - "do not know how to divide Matrix33 by %s"%rhs.__class__) + "do not know how to divide Matrix33 by %s" % rhs.__class__) # py3k __truediv__ = __div__ def __rmul__(self, lhs): if isinstance(lhs, (float, int)): - return self * lhs # commutes + return self * lhs # commutes else: raise TypeError( - "do not know how to multiply %s with Matrix33"%lhs.__class__) + "do not know how to multiply %s with Matrix33" % lhs.__class__) def __eq__(self, mat): if not isinstance(mat, NifFormat.Matrix33): raise TypeError( - "do not know how to compare Matrix33 and %s"%mat.__class__) + "do not know how to compare Matrix33 and %s" % mat.__class__) if (abs(self.m_11 - mat.m_11) > NifFormat.EPSILON - or abs(self.m_12 - mat.m_12) > NifFormat.EPSILON - or abs(self.m_13 - mat.m_13) > NifFormat.EPSILON - or abs(self.m_21 - mat.m_21) > NifFormat.EPSILON - or abs(self.m_22 - mat.m_22) > NifFormat.EPSILON - or abs(self.m_23 - mat.m_23) > NifFormat.EPSILON - or abs(self.m_31 - mat.m_31) > NifFormat.EPSILON - or abs(self.m_32 - mat.m_32) > NifFormat.EPSILON - or abs(self.m_33 - mat.m_33) > NifFormat.EPSILON): + or abs(self.m_12 - mat.m_12) > NifFormat.EPSILON + or abs(self.m_13 - mat.m_13) > NifFormat.EPSILON + or abs(self.m_21 - mat.m_21) > NifFormat.EPSILON + or abs(self.m_22 - mat.m_22) > NifFormat.EPSILON + or abs(self.m_23 - mat.m_23) > NifFormat.EPSILON + or abs(self.m_31 - mat.m_31) > NifFormat.EPSILON + or abs(self.m_32 - mat.m_32) > NifFormat.EPSILON + or abs(self.m_33 - mat.m_33) > NifFormat.EPSILON): return False return True @@ -1973,7 +1947,7 @@ def sup_norm(self): for row in self.as_list()) class Vector3: - + def assign(self, vec): """ Set this vector to values from another object that supports iteration or x,y,z properties """ # see if it is an iterable @@ -1988,13 +1962,13 @@ def assign(self, vec): self.y = vec.y if hasattr(vec, "z"): self.z = vec.z - + def __iter__(self): # just a convenience so we can do: x,y,z = Vector3() yield self.x yield self.y yield self.z - + def as_list(self): return [self.x, self.y, self.z] @@ -2002,12 +1976,12 @@ def as_tuple(self): return (self.x, self.y, self.z) def norm(self, sqrt=math.sqrt): - return sqrt(self.x*self.x + self.y*self.y + self.z*self.z) + return sqrt(self.x * self.x + self.y * self.y + self.z * self.z) def normalize(self, ignore_error=False, sqrt=math.sqrt): # inlining norm() to reduce overhead try: - factor = 1.0 / sqrt(self.x*self.x + self.y*self.y + self.z*self.z) + factor = 1.0 / sqrt(self.x * self.x + self.y * self.y + self.z * self.z) except ZeroDivisionError: if not ignore_error: raise @@ -2031,7 +2005,7 @@ def get_copy(self): return v def __str__(self): - return "[ %6.3f %6.3f %6.3f ]"%(self.x, self.y, self.z) + return "[ %6.3f %6.3f %6.3f ]" % (self.x, self.y, self.z) def __mul__(self, x): if isinstance(x, (float, int)): @@ -2051,7 +2025,7 @@ def __mul__(self, x): elif isinstance(x, NifFormat.Matrix44): return self * x.get_matrix_33() + x.get_translation() else: - raise TypeError("do not know how to multiply Vector3 with %s"%x.__class__) + raise TypeError("do not know how to multiply Vector3 with %s" % x.__class__) def __rmul__(self, x): if isinstance(x, (float, int)): @@ -2061,7 +2035,7 @@ def __rmul__(self, x): v.z = x * self.z return v else: - raise TypeError("do not know how to multiply %s and Vector3"%x.__class__) + raise TypeError("do not know how to multiply %s and Vector3" % x.__class__) def __div__(self, x): if isinstance(x, (float, int)): @@ -2071,7 +2045,7 @@ def __div__(self, x): v.z = self.z / x return v else: - raise TypeError("do not know how to divide Vector3 and %s"%x.__class__) + raise TypeError("do not know how to divide Vector3 and %s" % x.__class__) # py3k __truediv__ = __div__ @@ -2090,7 +2064,7 @@ def __add__(self, x): v.z = self.z + x.z return v else: - raise TypeError("do not know how to add Vector3 and %s"%x.__class__) + raise TypeError("do not know how to add Vector3 and %s" % x.__class__) def __radd__(self, x): if isinstance(x, (float, int)): @@ -2100,7 +2074,7 @@ def __radd__(self, x): v.z = x + self.z return v else: - raise TypeError("do not know how to add %s and Vector3"%x.__class__) + raise TypeError("do not know how to add %s and Vector3" % x.__class__) def __sub__(self, x): if isinstance(x, (float, int)): @@ -2116,7 +2090,7 @@ def __sub__(self, x): v.z = self.z - x.z return v else: - raise TypeError("do not know how to substract Vector3 and %s"%x.__class__) + raise TypeError("do not know how to substract Vector3 and %s" % x.__class__) def __rsub__(self, x): if isinstance(x, (float, int)): @@ -2126,7 +2100,7 @@ def __rsub__(self, x): v.z = x - self.z return v else: - raise TypeError("do not know how to substract %s and Vector3"%x.__class__) + raise TypeError("do not know how to substract %s and Vector3" % x.__class__) def __neg__(self): v = NifFormat.Vector3() @@ -2139,12 +2113,12 @@ def __neg__(self): def crossproduct(self, x): if isinstance(x, NifFormat.Vector3): v = NifFormat.Vector3() - v.x = self.y*x.z - self.z*x.y - v.y = self.z*x.x - self.x*x.z - v.z = self.x*x.y - self.y*x.x + v.x = self.y * x.z - self.z * x.y + v.y = self.z * x.x - self.x * x.z + v.z = self.x * x.y - self.y * x.x return v else: - raise TypeError("do not know how to calculate crossproduct of Vector3 and %s"%x.__class__) + raise TypeError("do not know how to calculate crossproduct of Vector3 and %s" % x.__class__) def __eq__(self, x): if isinstance(x, type(None)): @@ -2208,7 +2182,7 @@ def get_vector_3(self): return v def __str__(self): - return "[ %6.3f %6.3f %6.3f %6.3f ]"%(self.x, self.y, self.z, self.w) + return "[ %6.3f %6.3f %6.3f %6.3f ]" % (self.x, self.y, self.z, self.w) def __eq__(self, rhs): if isinstance(rhs, type(None)): @@ -2252,16 +2226,15 @@ def apply_scale(self, scale): self.dimensions.x *= scale self.dimensions.y *= scale self.dimensions.z *= scale - self.minimum_size *= scale + self.minimum_size *= scale - def get_mass_center_inertia(self, density = 1, solid = True): + def get_mass_center_inertia(self, density=1, solid=True): """Return mass, center, and inertia tensor.""" # the dimensions describe half the size of the box in each dimension # so the length of a single edge is dimension.dir * 2 mass, inertia = pyffi.utils.inertia.getMassInertiaBox( - (self.dimensions.x * 2, self.dimensions.y * 2, self.dimensions.z * 2), - density = density, solid = solid) - return mass, (0,0,0), inertia + (self.dimensions.x * 2, self.dimensions.y * 2, self.dimensions.z * 2), density=density, solid=solid) + return mass, (0, 0, 0), inertia class bhkCapsuleShape: def apply_scale(self, scale): @@ -2277,30 +2250,28 @@ def apply_scale(self, scale): self.second_point.y *= scale self.second_point.z *= scale - def get_mass_center_inertia(self, density = 1, solid = True): + def get_mass_center_inertia(self, density=1, solid=True): """Return mass, center, and inertia tensor.""" # (assumes self.radius == self.radius_1 == self.radius_2) length = (self.first_point - self.second_point).norm() - mass, inertia = pyffi.utils.inertia.getMassInertiaCapsule( - radius = self.radius, length = length, - density = density, solid = solid) + mass, inertia = pyffi.utils.inertia.getMassInertiaCapsule(radius=self.radius, length=length, + density=density, solid=solid) # now fix inertia so it is expressed in the right coordinates # need a transform that maps (0,0,length/2) on (second - first) / 2 # and (0,0,-length/2) on (first - second)/2 vec1 = ((self.second_point - self.first_point) / length).as_tuple() # find an orthogonal vector to vec1 index = min(enumerate(vec1), key=lambda val: abs(val[1]))[0] - vec2 = vecCrossProduct(vec1, tuple((1 if i == index else 0) - for i in range(3))) - vec2 = vecscalarMul(vec2, 1/vecNorm(vec2)) + vec2 = vecCrossProduct(vec1, tuple((1 if i == index else 0) for i in range(3))) + vec2 = vecscalarMul(vec2, 1 / vecNorm(vec2)) # find an orthogonal vector to vec1 and vec2 vec3 = vecCrossProduct(vec1, vec2) # get transform matrix - transform_transposed = (vec2, vec3, vec1) # this is effectively the transposed of our transform + transform_transposed = (vec2, vec3, vec1) # this is effectively the transposed of our transform transform = matTransposed(transform_transposed) # check the result (debug) - assert(vecDistance(matvecMul(transform, (0,0,1)), vec1) < 0.0001) - assert(abs(matDeterminant(transform) - 1) < 0.0001) + assert (vecDistance(matvecMul(transform, (0, 0, 1)), vec1) < 0.0001) + assert (abs(matDeterminant(transform) - 1) < 0.0001) # transform the inertia tensor inertia = matMul(matMul(transform_transposed, inertia), transform) return (mass, @@ -2314,24 +2285,22 @@ def get_transform_a_b(self, parent): blocks.""" # check entities if self.num_entities != 2: - raise ValueError( - "cannot get tranform for constraint " - "that hasn't exactly 2 entities") + raise ValueError("Cannot get tranform for constraint that hasn't exactly 2 entities") # find transform of entity A relative to entity B # find chains from parent to A and B entities chainA = parent.find_chain(self.entities[0]) chainB = parent.find_chain(self.entities[1]) # validate the chains - assert(isinstance(chainA[-1], NifFormat.bhkRigidBody)) - assert(isinstance(chainA[-2], NifFormat.NiCollisionObject)) - assert(isinstance(chainA[-3], NifFormat.NiNode)) - assert(isinstance(chainB[-1], NifFormat.bhkRigidBody)) - assert(isinstance(chainB[-2], NifFormat.NiCollisionObject)) - assert(isinstance(chainB[-3], NifFormat.NiNode)) + assert (isinstance(chainA[-1], NifFormat.bhkRigidBody)) + assert (isinstance(chainA[-2], NifFormat.NiCollisionObject)) + assert (isinstance(chainA[-3], NifFormat.NiNode)) + assert (isinstance(chainB[-1], NifFormat.bhkRigidBody)) + assert (isinstance(chainB[-2], NifFormat.NiCollisionObject)) + assert (isinstance(chainB[-3], NifFormat.NiNode)) # return the relative transform - return (chainA[-3].get_transform(relative_to = parent) - * chainB[-3].get_transform(relative_to = parent).get_inverse()) + return (chainA[-3].get_transform(relative_to=parent) * chainB[-3].get_transform( + relative_to=parent).get_inverse()) class bhkConvexVerticesShape: def apply_scale(self, scale): @@ -2344,14 +2313,14 @@ def apply_scale(self, scale): for n in self.normals: n.w *= scale - def get_mass_center_inertia(self, density = 1, solid = True): + def get_mass_center_inertia(self, density=1, solid=True): """Return mass, center, and inertia tensor.""" # first find an enumeration of all triangles making up the convex shape vertices, triangles = pyffi.utils.quickhull.qhull3d( [vert.as_tuple() for vert in self.vertices]) # now calculate mass, center, and inertia return pyffi.utils.inertia.get_mass_center_inertia_polyhedron( - vertices, triangles, density = density, solid = solid) + vertices, triangles, density=density, solid=solid) class bhkLimitedHingeConstraint: def apply_scale(self, scale): @@ -2370,11 +2339,10 @@ def update_a_b(self, parent): self.limited_hinge.update_a_b(self.get_transform_a_b(parent)) class bhkListShape: - def get_mass_center_inertia(self, density = 1, solid = True): + def get_mass_center_inertia(self, density=1, solid=True): """Return center of gravity and area.""" - subshapes_mci = [ subshape.get_mass_center_inertia(density = density, - solid = solid) - for subshape in self.sub_shapes ] + subshapes_mci = [subshape.get_mass_center_inertia(density=density, solid=solid) for subshape in + self.sub_shapes] total_mass = 0 total_center = (0, 0, 0) total_inertia = ((0, 0, 0), (0, 0, 0), (0, 0, 0)) @@ -2387,12 +2355,11 @@ def get_mass_center_inertia(self, density = 1, solid = True): # get average center and inertia for mass, center, inertia in subshapes_mci: - total_center = vecAdd(total_center, - vecscalarMul(center, mass / total_mass)) + total_center = vecAdd(total_center, vecscalarMul(center, mass / total_mass)) total_inertia = matAdd(total_inertia, inertia) return total_mass, total_center, total_inertia - def add_shape(self, shape, front = False): + def add_shape(self, shape, front=False): """Add shape to list.""" # check if it's already there if shape in self.sub_shapes: return @@ -2405,7 +2372,7 @@ def add_shape(self, shape, front = False): self.sub_shapes[num_shapes] = shape else: for i in range(num_shapes, 0, -1): - self.sub_shapes[i] = self.sub_shapes[i-1] + self.sub_shapes[i] = self.sub_shapes[i - 1] self.sub_shapes[0] = shape # expand list of unknown ints as well self.num_unknown_ints = num_shapes + 1 @@ -2450,8 +2417,7 @@ def update_a_b(self, parent): class bhkMoppBvTreeShape: def get_mass_center_inertia(self, density=1, solid=True): """Return mass, center of gravity, and inertia tensor.""" - return self.get_shape_mass_center_inertia( - density=density, solid=solid) + return self.get_shape_mass_center_inertia(density=density, solid=solid) def update_origin_scale(self): """Update scale and origin.""" @@ -2464,7 +2430,7 @@ def update_origin_scale(self): self.origin.x = minx - 0.1 self.origin.y = miny - 0.1 self.origin.z = minz - 0.1 - self.scale = (256*256*254) / (0.2+max([maxx-minx,maxy-miny,maxz-minz])) + self.scale = (256 * 256 * 254) / (0.2 + max([maxx - minx, maxy - miny, maxz - minz])) def update_mopp(self): """Update the MOPP data, scale, and origin, and welding info. @@ -2478,9 +2444,8 @@ def update_mopp_welding(self): logger = logging.getLogger("pyffi.mopp") # check type of shape if not isinstance(self.shape, NifFormat.bhkPackedNiTriStripsShape): - raise ValueError( - "expected bhkPackedNiTriStripsShape on mopp" - " but got %s instead" % self.shape.__class__.__name__) + raise ValueError("expected bhkPackedNiTriStripsShape on mopp but got {0} instead".format( + self.shape.__class__.__name__)) # first try with pyffi.utils.mopp failed = False try: @@ -2491,20 +2456,15 @@ def update_mopp_welding(self): # find material indices per triangle material_per_vertex = [] for subshape in self.shape.get_sub_shapes(): - material_per_vertex += ( - [subshape.material] * subshape.num_vertices) - material_per_triangle = [ - material_per_vertex[hktri.triangle.v_1] - for hktri in self.shape.data.triangles] + material_per_vertex += ([subshape.material] * subshape.num_vertices) + material_per_triangle = [material_per_vertex[hktri.triangle.v_1] for hktri in self.shape.data.triangles] # compute havok info try: - origin, scale, mopp, welding_infos \ - = pyffi.utils.mopp.getMopperOriginScaleCodeWelding( + origin, scale, mopp, welding_infos = pyffi.utils.mopp.getMopperOriginScaleCodeWelding( [vert.as_tuple() for vert in self.shape.data.vertices], [(hktri.triangle.v_1, hktri.triangle.v_2, - hktri.triangle.v_3) - for hktri in self.shape.data.triangles], + hktri.triangle.v_3) for hktri in self.shape.data.triangles], material_per_triangle) except (OSError, RuntimeError): failed = True @@ -2542,8 +2502,8 @@ def update_mopp_welding(self): def _makeSimpleMopp(self): """Make a simple mopp.""" - mopp = [] # the mopp 'assembly' script - self._q = 256*256 / self.scale # quantization factor + mopp = [] # the mopp 'assembly' script + self._q = 256 * 256 / self.scale # quantization factor # opcodes BOUNDX = 0x26 @@ -2554,14 +2514,14 @@ def _makeSimpleMopp(self): TESTZ = 0x12 # add first crude bounding box checks - self._vertsceil = [ self._moppCeil(v) for v in self.shape.data.vertices ] - self._vertsfloor = [ self._moppFloor(v) for v in self.shape.data.vertices ] - minx = min([ v[0] for v in self._vertsfloor ]) - miny = min([ v[1] for v in self._vertsfloor ]) - minz = min([ v[2] for v in self._vertsfloor ]) - maxx = max([ v[0] for v in self._vertsceil ]) - maxy = max([ v[1] for v in self._vertsceil ]) - maxz = max([ v[2] for v in self._vertsceil ]) + self._vertsceil = [self._moppCeil(v) for v in self.shape.data.vertices] + self._vertsfloor = [self._moppFloor(v) for v in self.shape.data.vertices] + minx = min([v[0] for v in self._vertsfloor]) + miny = min([v[1] for v in self._vertsfloor]) + minz = min([v[2] for v in self._vertsfloor]) + maxx = max([v[0] for v in self._vertsceil]) + maxy = max([v[1] for v in self._vertsceil]) + maxz = max([v[2] for v in self._vertsceil]) if minx < 0 or miny < 0 or minz < 0: raise ValueError("cannot update mopp tree with invalid origin") if maxx > 255 or maxy > 255 or maxz > 255: raise ValueError("cannot update mopp tree with invalid scale") mopp.extend([BOUNDZ, minz, maxz]) @@ -2570,21 +2530,21 @@ def _makeSimpleMopp(self): # add tree using subsequent X-Y-Z splits # (slow and no noticable difference from other simple tree so deactivated) - #tris = range(len(self.shape.data.triangles)) - #tree = self.split_triangles(tris, [[minx,maxx],[miny,maxy],[minz,maxz]]) - #mopp += self.mopp_from_tree(tree) + # tris = range(len(self.shape.data.triangles)) + # tree = self.split_triangles(tris, [[minx,maxx],[miny,maxy],[minz,maxz]]) + # mopp += self.mopp_from_tree(tree) # add a trivial tree # this prevents the player of walking through the model # but arrows may still fly through numtriangles = len(self.shape.data.triangles) i = 0x30 - for t in range(numtriangles-1): - mopp.extend([TESTZ, maxz, 0, 1, i]) - i += 1 - if i == 0x50: - mopp.extend([0x09, 0x20]) # increment triangle offset - i = 0x30 + for t in range(numtriangles - 1): + mopp.extend([TESTZ, maxz, 0, 1, i]) + i += 1 + if i == 0x50: + mopp.extend([0x09, 0x20]) # increment triangle offset + i = 0x30 mopp.extend([i]) return mopp @@ -2603,11 +2563,11 @@ def _moppFloor(self, v): def split_triangles(self, ts, bbox, dir=0): """Direction 0=X, 1=Y, 2=Z""" - btest = [] # for bounding box tests - test = [] # for branch command + btest = [] # for bounding box tests + test = [] # for branch command # check bounding box - tris = [ t.triangle for t in self.shape.data.triangles ] - tsverts = [ tris[t].v_1 for t in ts] + [ tris[t].v_2 for t in ts] + [ tris[t].v_3 for t in ts] + tris = [t.triangle for t in self.shape.data.triangles] + tsverts = [tris[t].v_1 for t in ts] + [tris[t].v_2 for t in ts] + [tris[t].v_3 for t in ts] minx = min([self._vertsfloor[v][0] for v in tsverts]) miny = min([self._vertsfloor[v][1] for v in tsverts]) minz = min([self._vertsfloor[v][2] for v in tsverts]) @@ -2615,48 +2575,52 @@ def split_triangles(self, ts, bbox, dir=0): maxy = max([self._vertsceil[v][1] for v in tsverts]) maxz = max([self._vertsceil[v][2] for v in tsverts]) # add bounding box checks if it's reduced in a direction - if (maxx - minx < bbox[0][1] - bbox[0][0]): - btest += [ 0x26, minx, maxx ] + if maxx - minx < bbox[0][1] - bbox[0][0]: + btest += [0x26, minx, maxx] bbox[0][0] = minx bbox[0][1] = maxx - if (maxy - miny < bbox[1][1] - bbox[1][0]): - btest += [ 0x27, miny, maxy ] + + if maxy - miny < bbox[1][1] - bbox[1][0]: + btest += [0x27, miny, maxy] bbox[1][0] = miny bbox[1][1] = maxy - if (maxz - minz < bbox[2][1] - bbox[2][0]): - btest += [ 0x28, minz, maxz ] + + if maxz - minz < bbox[2][1] - bbox[2][0]: + btest += [0x28, minz, maxz] bbox[2][0] = minz bbox[2][1] = maxz + # if only one triangle, no further split needed if len(ts) == 1: if ts[0] < 32: - return [ btest, [ 0x30 + ts[0] ], [], [] ] + return [btest, [0x30 + ts[0]], [], []] elif ts[0] < 256: - return [ btest, [ 0x50, ts[0] ], [], [] ] + return [btest, [0x50, ts[0]], [], []] else: - return [ btest, [ 0x51, ts[0] >> 8, ts[0] & 255 ], [], [] ] + return [btest, [0x51, ts[0] >> 8, ts[0] & 255], [], []] # sort triangles in required direction - ts.sort(key = lambda t: max(self._vertsceil[tris[t].v_1][dir], self._vertsceil[tris[t].v_2][dir], self._vertsceil[tris[t].v_3][dir])) + ts.sort(key=lambda t: max(self._vertsceil[tris[t].v_1][dir], self._vertsceil[tris[t].v_2][dir], + self._vertsceil[tris[t].v_3][dir])) # split into two - ts1 = ts[:len(ts)/2] - ts2 = ts[len(ts)/2:] + ts1 = ts[:len(ts) / 2] + ts2 = ts[len(ts) / 2:] # get maximum coordinate of small group - ts1verts = [ tris[t].v_1 for t in ts1] + [ tris[t].v_2 for t in ts1] + [ tris[t].v_3 for t in ts1] - ts2verts = [ tris[t].v_1 for t in ts2] + [ tris[t].v_2 for t in ts2] + [ tris[t].v_3 for t in ts2] + ts1verts = [tris[t].v_1 for t in ts1] + [tris[t].v_2 for t in ts1] + [tris[t].v_3 for t in ts1] + ts2verts = [tris[t].v_1 for t in ts2] + [tris[t].v_2 for t in ts2] + [tris[t].v_3 for t in ts2] ts1max = max([self._vertsceil[v][dir] for v in ts1verts]) # get minimum coordinate of large group ts2min = min([self._vertsfloor[v][dir] for v in ts2verts]) # set up test - test += [0x10+dir, ts1max, ts2min] + test += [0x10 + dir, ts1max, ts2min] # set up new bounding boxes for each subtree # make copy - bbox1 = [[bbox[0][0],bbox[0][1]],[bbox[1][0],bbox[1][1]],[bbox[2][0],bbox[2][1]]] - bbox2 = [[bbox[0][0],bbox[0][1]],[bbox[1][0],bbox[1][1]],[bbox[2][0],bbox[2][1]]] + bbox1 = [[bbox[0][0], bbox[0][1]], [bbox[1][0], bbox[1][1]], [bbox[2][0], bbox[2][1]]] + bbox2 = [[bbox[0][0], bbox[0][1]], [bbox[1][0], bbox[1][1]], [bbox[2][0], bbox[2][1]]] # update bound in test direction bbox1[dir][1] = ts1max bbox2[dir][0] = ts2min # return result - nextdir = dir+1 + nextdir = dir + 1 if nextdir == 3: nextdir = 0 return [btest, test, self.split_triangles(ts1, bbox1, nextdir), self.split_triangles(ts2, bbox2, nextdir)] @@ -2667,7 +2631,7 @@ def mopp_from_tree(self, tree): submopp1 = self.mopp_from_tree(tree[2]) submopp2 = self.mopp_from_tree(tree[3]) if len(submopp1) < 256: - mopp += [ len(submopp1) ] + mopp += [len(submopp1)] mopp += submopp1 mopp += submopp2 else: @@ -2681,13 +2645,14 @@ def mopp_from_tree(self, tree): return mopp # ported and extended from NifVis/bhkMoppBvTreeShape.py - def parse_mopp(self, start = 0, depth = 0, toffset = 0, verbose = False): + def parse_mopp(self, start=0, depth=0, toffset=0, verbose=False): """The mopp data is printed to the debug channel while parsed. Returns list of indices into mopp data of the bytes processed and a list of triangle indices encountered. The verbose argument is ignored (and is deprecated). """ + class Message: def __init__(self): self.logger = logging.getLogger("pyffi.mopp") @@ -2706,96 +2671,96 @@ def error(self): self.logger.error(self.msg) self.msg = "" - mopp = self.mopp_data # shortcut notation - ids = [] # indices of bytes processed - tris = [] # triangle indices - i = start # current index - ret = False # set to True if an opcode signals a triangle index + mopp = self.mopp_data # shortcut notation + ids = [] # indices of bytes processed + tris = [] # triangle indices + i = start # current index + ret = False # set to True if an opcode signals a triangle index while i < self.mopp_data_size and not ret: # get opcode and print it code = mopp[i] msg = Message() - msg.append("%4i:"%i + " "*depth + '0x%02X ' % code) + msg.append("%4i:" % i + " " * depth + '0x%02X ' % code) if code == 0x09: # increment triangle offset - toffset += mopp[i+1] - msg.append(mopp[i+1]) + toffset += mopp[i + 1] + msg.append(mopp[i + 1]) msg.append('%i [ triangle offset += %i, offset is now %i ]' - % (mopp[i+1], mopp[i+1], toffset)) - ids.extend([i,i+1]) + % (mopp[i + 1], mopp[i + 1], toffset)) + ids.extend([i, i + 1]) i += 2 - elif code in [ 0x0A ]: + elif code in [0x0A]: # increment triangle offset - toffset += mopp[i+1]*256 + mopp[i+2] - msg.append(mopp[i+1],mopp[i+2]) + toffset += mopp[i + 1] * 256 + mopp[i + 2] + msg.append(mopp[i + 1], mopp[i + 2]) msg.append('[ triangle offset += %i, offset is now %i ]' - % (mopp[i+1]*256 + mopp[i+2], toffset)) - ids.extend([i,i+1,i+2]) + % (mopp[i + 1] * 256 + mopp[i + 2], toffset)) + ids.extend([i, i + 1, i + 2]) i += 3 - elif code in [ 0x0B ]: + elif code in [0x0B]: # unsure about first two arguments, but the 3rd and 4th set triangle offset - toffset = 256*mopp[i+3] + mopp[i+4] - msg.append(mopp[i+1],mopp[i+2],mopp[i+3],mopp[i+4]) + toffset = 256 * mopp[i + 3] + mopp[i + 4] + msg.append(mopp[i + 1], mopp[i + 2], mopp[i + 3], mopp[i + 4]) msg.append('[ triangle offset = %i ]' % toffset) - ids.extend([i,i+1,i+2,i+3,i+4]) + ids.extend([i, i + 1, i + 2, i + 3, i + 4]) i += 5 - elif code in range(0x30,0x50): + elif code in range(0x30, 0x50): # triangle compact - msg.append('[ triangle %i ]'%(code-0x30+toffset)) + msg.append('[ triangle %i ]' % (code - 0x30 + toffset)) ids.append(i) - tris.append(code-0x30+toffset) + tris.append(code - 0x30 + toffset) i += 1 ret = True elif code == 0x50: # triangle byte - msg.append(mopp[i+1]) - msg.append('[ triangle %i ]'%(mopp[i+1]+toffset)) - ids.extend([i,i+1]) - tris.append(mopp[i+1]+toffset) + msg.append(mopp[i + 1]) + msg.append('[ triangle %i ]' % (mopp[i + 1] + toffset)) + ids.extend([i, i + 1]) + tris.append(mopp[i + 1] + toffset) i += 2 ret = True - elif code in [ 0x51 ]: + elif code in [0x51]: # triangle short - t = mopp[i+1]*256 + mopp[i+2] + toffset - msg.append(mopp[i+1],mopp[i+2]) + t = mopp[i + 1] * 256 + mopp[i + 2] + toffset + msg.append(mopp[i + 1], mopp[i + 2]) msg.append('[ triangle %i ]' % t) - ids.extend([i,i+1,i+2]) + ids.extend([i, i + 1, i + 2]) tris.append(t) i += 3 ret = True - elif code in [ 0x53 ]: + elif code in [0x53]: # triangle short? - t = mopp[i+3]*256 + mopp[i+4] + toffset - msg.append(mopp[i+1],mopp[i+2],mopp[i+3],mopp[i+4]) + t = mopp[i + 3] * 256 + mopp[i + 4] + toffset + msg.append(mopp[i + 1], mopp[i + 2], mopp[i + 3], mopp[i + 4]) msg.append('[ triangle %i ]' % t) - ids.extend([i,i+1,i+2,i+3,i+4]) + ids.extend([i, i + 1, i + 2, i + 3, i + 4]) tris.append(t) i += 5 ret = True - elif code in [ 0x05 ]: + elif code in [0x05]: # byte jump - msg.append('[ jump -> %i: ]'%(i+2+mopp[i+1])) - ids.extend([i,i+1]) - i += 2+mopp[i+1] + msg.append('[ jump -> %i: ]' % (i + 2 + mopp[i + 1])) + ids.extend([i, i + 1]) + i += 2 + mopp[i + 1] - elif code in [ 0x06 ]: + elif code in [0x06]: # short jump - jump = mopp[i+1]*256 + mopp[i+2] - msg.append('[ jump -> %i: ]'%(i+3+jump)) - ids.extend([i,i+1,i+2]) - i += 3+jump + jump = mopp[i + 1] * 256 + mopp[i + 2] + msg.append('[ jump -> %i: ]' % (i + 3 + jump)) + ids.extend([i, i + 1, i + 2]) + i += 3 + jump - elif code in [0x10,0x11,0x12, 0x13,0x14,0x15, 0x16,0x17,0x18, 0x19, 0x1A, 0x1B, 0x1C]: + elif code in [0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C]: # compact if-then-else with two arguments - msg.append(mopp[i+1], mopp[i+2]) + msg.append(mopp[i + 1], mopp[i + 2]) if code == 0x10: msg.append('[ branch X') elif code == 0x11: @@ -2804,88 +2769,95 @@ def error(self): msg.append('[ branch Z') else: msg.append('[ branch ?') - msg.append('-> %i: %i: ]'%(i+4,i+4+mopp[i+3])) + msg.append('-> %i: %i: ]' % (i + 4, i + 4 + mopp[i + 3])) msg.debug() - msg.append(" " + " "*depth + 'if:') + msg.append(" " + " " * depth + 'if:') msg.debug() - idssub1, trissub1 = self.parse_mopp(start = i+4, depth = depth+1, toffset = toffset, verbose = verbose) - msg.append(" " + " "*depth + 'else:') + idssub1, trissub1 = self.parse_mopp(start=i + 4, depth=depth + 1, toffset=toffset, verbose=verbose) + msg.append(" " + " " * depth + 'else:') msg.debug() - idssub2, trissub2 = self.parse_mopp(start = i+4+mopp[i+3], depth = depth+1, toffset = toffset, verbose = verbose) - ids.extend([i,i+1,i+2,i+3]) + idssub2, trissub2 = self.parse_mopp(start=i + 4 + mopp[i + 3], depth=depth + 1, toffset=toffset, + verbose=verbose) + ids.extend([i, i + 1, i + 2, i + 3]) ids.extend(idssub1) ids.extend(idssub2) tris.extend(trissub1) tris.extend(trissub2) ret = True - elif code in [0x20,0x21,0x22]: + elif code in [0x20, 0x21, 0x22]: # compact if-then-else with one argument - msg.append(mopp[i+1], '[ branch ? -> %i: %i: ]'%(i+3,i+3+mopp[i+2])).debug() - msg.append(" " + " "*depth + 'if:').debug() - idssub1, trissub1 = self.parse_mopp(start = i+3, depth = depth+1, toffset = toffset, verbose = verbose) - msg.append(" " + " "*depth + 'else:').debug() - idssub2, trissub2 = self.parse_mopp(start = i+3+mopp[i+2], depth = depth+1, toffset = toffset, verbose = verbose) - ids.extend([i,i+1,i+2]) + msg.append(mopp[i + 1], '[ branch ? -> %i: %i: ]' % (i + 3, i + 3 + mopp[i + 2])).debug() + msg.append(" " + " " * depth + 'if:').debug() + idssub1, trissub1 = self.parse_mopp(start=i + 3, depth=depth + 1, toffset=toffset, verbose=verbose) + msg.append(" " + " " * depth + 'else:').debug() + idssub2, trissub2 = self.parse_mopp(start=i + 3 + mopp[i + 2], depth=depth + 1, toffset=toffset, + verbose=verbose) + ids.extend([i, i + 1, i + 2]) ids.extend(idssub1) ids.extend(idssub2) tris.extend(trissub1) tris.extend(trissub2) ret = True - elif code in [0x23,0x24,0x25]: # short if x <= a then 1; if x > b then 2; - jump1 = mopp[i+3] * 256 + mopp[i+4] - jump2 = mopp[i+5] * 256 + mopp[i+6] - msg.append(mopp[i+1], mopp[i+2], '[ branch ? -> %i: %i: ]'%(i+7+jump1,i+7+jump2)).debug() - msg.append(" " + " "*depth + 'if:').debug() - idssub1, trissub1 = self.parse_mopp(start = i+7+jump1, depth = depth+1, toffset = toffset, verbose = verbose) - msg.append(" " + " "*depth + 'else:').debug() - idssub2, trissub2 = self.parse_mopp(start = i+7+jump2, depth = depth+1, toffset = toffset, verbose = verbose) - ids.extend([i,i+1,i+2,i+3,i+4,i+5,i+6]) + elif code in [0x23, 0x24, 0x25]: # short if x <= a then 1; if x > b then 2; + jump1 = mopp[i + 3] * 256 + mopp[i + 4] + jump2 = mopp[i + 5] * 256 + mopp[i + 6] + msg.append(mopp[i + 1], mopp[i + 2], + '[ branch ? -> %i: %i: ]' % (i + 7 + jump1, i + 7 + jump2)).debug() + msg.append(" " + " " * depth + 'if:').debug() + idssub1, trissub1 = self.parse_mopp(start=i + 7 + jump1, depth=depth + 1, toffset=toffset, + verbose=verbose) + msg.append(" " + " " * depth + 'else:').debug() + idssub2, trissub2 = self.parse_mopp(start=i + 7 + jump2, depth=depth + 1, toffset=toffset, + verbose=verbose) + ids.extend([i, i + 1, i + 2, i + 3, i + 4, i + 5, i + 6]) ids.extend(idssub1) ids.extend(idssub2) tris.extend(trissub1) tris.extend(trissub2) ret = True - elif code in [0x26,0x27,0x28]: - msg.append(mopp[i+1], mopp[i+2]) + elif code in [0x26, 0x27, 0x28]: + msg.append(mopp[i + 1], mopp[i + 2]) if code == 0x26: msg.append('[ bound X ]') elif code == 0x27: msg.append('[ bound Y ]') elif code == 0x28: msg.append('[ bound Z ]') - ids.extend([i,i+1,i+2]) + ids.extend([i, i + 1, i + 2]) i += 3 elif code in [0x01, 0x02, 0x03, 0x04]: - msg.append(mopp[i+1], mopp[i+2], mopp[i+3], '[ bound XYZ? ]') - ids.extend([i,i+1,i+2,i+3]) + msg.append(mopp[i + 1], mopp[i + 2], mopp[i + 3], '[ bound XYZ? ]') + ids.extend([i, i + 1, i + 2, i + 3]) i += 4 else: - msg.append("unknown mopp code 0x%02X"%code).error() + msg.append("unknown mopp code 0x%02X" % code).error() msg.append("following bytes are").debug() - extrabytes = [mopp[j] for j in range(i+1,min(self.mopp_data_size,i+10))] - extraindex = [j for j in range(i+1,min(self.mopp_data_size,i+10))] + extrabytes = [mopp[j] for j in range(i + 1, min(self.mopp_data_size, i + 10))] + extraindex = [j for j in range(i + 1, min(self.mopp_data_size, i + 10))] msg.append(extrabytes).debug() for b, j in zip(extrabytes, extraindex): - if j+b+1 < self.mopp_data_size: - msg.append("opcode after jump %i is 0x%02X"%(b,mopp[j+b+1]), [mopp[k] for k in range(j+b+2,min(self.mopp_data_size,j+b+11))]).debug() - raise ValueError("unknown mopp opcode 0x%02X"%code) + if j + b + 1 < self.mopp_data_size: + msg.append("opcode after jump %i is 0x%02X" % (b, mopp[j + b + 1]), [mopp[k] for k in + range(j + b + 2, min( + self.mopp_data_size, + j + b + 11))]).debug() + raise ValueError("unknown mopp opcode 0x%02X" % code) msg.debug() return ids, tris class bhkMultiSphereShape: - def get_mass_center_inertia(self, density = 1, solid = True): + def get_mass_center_inertia(self, density=1, solid=True): """Return center of gravity and area.""" subshapes_mci = [ (mass, center, inertia) for (mass, inertia), center in - zip( ( pyffi.utils.inertia.getMassInertiaSphere(radius = sphere.radius, - density = density, solid = solid) - for sphere in self.spheres ), - ( sphere.center.as_tuple() for sphere in self.spheres ) ) ] + zip((pyffi.utils.inertia.getMassInertiaSphere(radius=sphere.radius, density=density, solid=solid) + for sphere in self.spheres), + (sphere.center.as_tuple() for sphere in self.spheres))] total_mass = 0 total_center = (0, 0, 0) total_inertia = ((0, 0, 0), (0, 0, 0), (0, 0, 0)) @@ -2917,8 +2889,8 @@ def get_interchangeable_packed_shape(self): normals.extend( (strip.vertices[tri2] - strip.vertices[tri1]).crossproduct( strip.vertices[tri3] - strip.vertices[tri1]) - .normalized(ignore_error=True) - .as_tuple() + .normalized(ignore_error=True) + .as_tuple() for tri1, tri2, tri3 in strip.get_triangles()) # create packed shape and add geometry packed = NifFormat.bhkPackedNiTriStripsShape() @@ -2928,7 +2900,7 @@ def get_interchangeable_packed_shape(self): vertices=vertices, # default layer 1 (static collision) layer=self.data_layers[0].layer if self.data_layers else 1, - material=self.material.material) + material=self.material) # set scale packed.scale_copy.x = 1.0 packed.scale_copy.y = 1.0 @@ -2939,16 +2911,16 @@ def get_interchangeable_packed_shape(self): # return result return packed - def get_mass_center_inertia(self, density = 1, solid = True): + def get_mass_center_inertia(self, density=1, solid=True): """Return mass, center, and inertia tensor.""" # first find mass, center, and inertia of all shapes subshapes_mci = [] for data in self.strips_data: subshapes_mci.append( pyffi.utils.inertia.get_mass_center_inertia_polyhedron( - [ vert.as_tuple() for vert in data.vertices ], - [ triangle for triangle in data.get_triangles() ], - density = density, solid = solid)) + [vert.as_tuple() for vert in data.vertices], + [triangle for triangle in data.get_triangles()], + density=density, solid=solid)) # now calculate mass, center, and inertia total_mass = 0 @@ -2962,15 +2934,15 @@ def get_mass_center_inertia(self, density = 1, solid = True): return total_mass, total_center, total_inertia class bhkPackedNiTriStripsShape: - def get_mass_center_inertia(self, density = 1, solid = True): + def get_mass_center_inertia(self, density=1, solid=True): """Return mass, center, and inertia tensor.""" return pyffi.utils.inertia.get_mass_center_inertia_polyhedron( - [ vert.as_tuple() for vert in self.data.vertices ], - [ ( hktriangle.triangle.v_1, - hktriangle.triangle.v_2, - hktriangle.triangle.v_3 ) - for hktriangle in self.data.triangles ], - density = density, solid = solid) + [vert.as_tuple() for vert in self.data.vertices], + [(hktriangle.triangle.v_1, + hktriangle.triangle.v_2, + hktriangle.triangle.v_3) + for hktriangle in self.data.triangles], + density=density, solid=solid) def get_sub_shapes(self): """Return sub shapes (works for both Oblivion and Fallout 3).""" @@ -2994,10 +2966,10 @@ def add_shape(self, triangles, normals, vertices, layer=0, material=0): # add the shape self.sub_shapes[num_shapes].layer = layer self.sub_shapes[num_shapes].num_vertices = len(vertices) - self.sub_shapes[num_shapes].material.material = material + self.sub_shapes[num_shapes].material = material data.sub_shapes[num_shapes].layer = layer data.sub_shapes[num_shapes].num_vertices = len(vertices) - data.sub_shapes[num_shapes].material.material = material + data.sub_shapes[num_shapes].material = material firsttriangle = data.num_triangles firstvertex = data.num_vertices data.num_triangles += len(triangles) @@ -3015,10 +2987,8 @@ def add_shape(self, triangles, normals, vertices, layer=0, material=0): vdata.x = v[0] / 7.0 vdata.y = v[1] / 7.0 vdata.z = v[2] / 7.0 - - def get_vertex_hash_generator( - self, - vertexprecision=3, subshape_index=None): + + def get_vertex_hash_generator(self, vertexprecision=3, subshape_index=None): """Generator which produces a tuple of integers for each vertex to ease detection of duplicate/close enough to remove vertices. The precision parameter denote number of @@ -3059,9 +3029,9 @@ def get_vertex_hash_generator( vertexfactor = 10 ** vertexprecision if subshape_index is None: for matid, vert in zip(chain(*[repeat(i, sub_shape.num_vertices) - for i, sub_shape - in enumerate(self.get_sub_shapes())]), - self.data.vertices): + for i, sub_shape + in enumerate(self.get_sub_shapes())]), + self.data.vertices): yield (matid, tuple(float_to_int(value * vertexfactor) for value in vert.as_list())) else: @@ -3070,9 +3040,9 @@ def get_vertex_hash_generator( self.get_sub_shapes()): first_vertex += subshape.num_vertices for vert_index in range( - first_vertex, - first_vertex - + self.get_sub_shapes()[subshape_index].num_vertices): + first_vertex, + first_vertex + + self.get_sub_shapes()[subshape_index].num_vertices): yield tuple(float_to_int(value * vertexfactor) for value in self.data.vertices[vert_index].as_list()) @@ -3152,8 +3122,7 @@ def get_shape_mass_center_inertia(self, density=1, solid=True): center = (0, 0, 0) inertia = ((0, 0, 0), (0, 0, 0), (0, 0, 0)) else: - mass, center, inertia = self.shape.get_mass_center_inertia( - density=density, solid=solid) + mass, center, inertia = self.shape.get_mass_center_inertia(density=density, solid=solid) return mass, center, inertia class bhkRigidBody: @@ -3190,8 +3159,7 @@ def update_mass_center_inertia(self, density=1, solid=True, mass=None): if not mass is None: density = 1 - calc_mass, center, inertia = self.get_shape_mass_center_inertia( - density=density, solid=solid) + calc_mass, center, inertia = self.get_shape_mass_center_inertia(density=density, solid=solid) self.mass = calc_mass self.center.x, self.center.y, self.center.z = center @@ -3230,13 +3198,12 @@ def apply_scale(self, scale): # apply scale on dimensions self.radius *= scale - def get_mass_center_inertia(self, density = 1, solid = True): + def get_mass_center_inertia(self, density=1, solid=True): """Return mass, center, and inertia tensor.""" # the dimensions describe half the size of the box in each dimension # so the length of a single edge is dimension.dir * 2 - mass, inertia = pyffi.utils.inertia.getMassInertiaSphere( - self.radius, density = density, solid = solid) - return mass, (0,0,0), inertia + mass, inertia = pyffi.utils.inertia.getMassInertiaSphere(self.radius, density=density, solid=solid) + return mass, (0, 0, 0), inertia class bhkTransformShape: def apply_scale(self, scale): @@ -3249,12 +3216,11 @@ def apply_scale(self, scale): def get_mass_center_inertia(self, density=1, solid=True): """Return mass, center, and inertia tensor.""" # get shape mass, center, and inertia - mass, center, inertia = self.get_shape_mass_center_inertia( - density=density, solid=solid) + mass, center, inertia = self.get_shape_mass_center_inertia(density=density, solid=solid) # get transform matrix and translation vector transform = self.transform.get_matrix_33().as_tuple() transform_transposed = matTransposed(transform) - translation = ( self.transform.m_14, self.transform.m_24, self.transform.m_34 ) + translation = (self.transform.m_14, self.transform.m_24, self.transform.m_34) # transform center and inertia center = matvecMul(transform, center) center = vecAdd(center, translation) @@ -3277,8 +3243,7 @@ def get_dismember_partitions(self): """Return triangles and body part indices.""" triangles = [] trianglepartmap = [] - for bodypart, skinpartblock in zip( - self.partitions, self.skin_partition.skin_partition_blocks): + for bodypart, skinpartblock in zip(self.partitions, self.skin_partition.skin_partition_blocks): part_triangles = list(skinpartblock.get_mapped_triangles()) triangles += part_triangles trianglepartmap += [bodypart.body_part] * len(part_triangles) @@ -3305,6 +3270,7 @@ class ControllerLink: >>> link.node_name b'Bip01 Tail' """ + def _get_string(self, offset): """A wrapper around string_palette.palette.get_string. Used by get_node_name etc. Returns the string at given offset.""" @@ -3416,7 +3382,7 @@ def as_list(self): [self.m_11, self.m_12, self.m_13], [self.m_21, self.m_22, self.m_23], [self.m_31, self.m_32, self.m_33] - ] + ] def as_tuple(self): """Return matrix as 3x3 tuple.""" @@ -3424,16 +3390,15 @@ def as_tuple(self): (self.m_11, self.m_12, self.m_13), (self.m_21, self.m_22, self.m_23), (self.m_31, self.m_32, self.m_33) - ) + ) def __str__(self): - return( - "[ %6.3f %6.3f %6.3f ]\n" - "[ %6.3f %6.3f %6.3f ]\n" - "[ %6.3f %6.3f %6.3f ]\n" - % (self.m_11, self.m_12, self.m_13, - self.m_21, self.m_22, self.m_23, - self.m_31, self.m_32, self.m_33)) + return ("[ %6.3f %6.3f %6.3f ]\n" + "[ %6.3f %6.3f %6.3f ]\n" + "[ %6.3f %6.3f %6.3f ]\n" + % (self.m_11, self.m_12, self.m_13, + self.m_21, self.m_22, self.m_23, + self.m_31, self.m_32, self.m_33)) def set_identity(self): """Set to identity matrix.""" @@ -3452,15 +3417,15 @@ def set_identity(self): def is_identity(self): """Return ``True`` if the matrix is close to identity.""" - if (abs(self.m_11 - 1.0) > NifFormat.EPSILON - or abs(self.m_12) > NifFormat.EPSILON - or abs(self.m_13) > NifFormat.EPSILON - or abs(self.m_21) > NifFormat.EPSILON - or abs(self.m_22 - 1.0) > NifFormat.EPSILON - or abs(self.m_23) > NifFormat.EPSILON - or abs(self.m_31) > NifFormat.EPSILON - or abs(self.m_32) > NifFormat.EPSILON - or abs(self.m_33 - 1.0) > NifFormat.EPSILON): + if (abs(self.m_11 - 1.0) > NifFormat.EPSILON + or abs(self.m_12) > NifFormat.EPSILON + or abs(self.m_13) > NifFormat.EPSILON + or abs(self.m_21) > NifFormat.EPSILON + or abs(self.m_22 - 1.0) > NifFormat.EPSILON + or abs(self.m_23) > NifFormat.EPSILON + or abs(self.m_31) > NifFormat.EPSILON + or abs(self.m_32) > NifFormat.EPSILON + or abs(self.m_33 - 1.0) > NifFormat.EPSILON): return False else: return True @@ -3484,17 +3449,16 @@ def get_copy(self): def __eq__(self, mat): if not isinstance(mat, NifFormat.InertiaMatrix): - raise TypeError( - "do not know how to compare InertiaMatrix and %s"%mat.__class__) + raise TypeError("do not know how to compare InertiaMatrix and {0}".format(mat.__class__)) if (abs(self.m_11 - mat.m_11) > NifFormat.EPSILON - or abs(self.m_12 - mat.m_12) > NifFormat.EPSILON - or abs(self.m_13 - mat.m_13) > NifFormat.EPSILON - or abs(self.m_21 - mat.m_21) > NifFormat.EPSILON - or abs(self.m_22 - mat.m_22) > NifFormat.EPSILON - or abs(self.m_23 - mat.m_23) > NifFormat.EPSILON - or abs(self.m_31 - mat.m_31) > NifFormat.EPSILON - or abs(self.m_32 - mat.m_32) > NifFormat.EPSILON - or abs(self.m_33 - mat.m_33) > NifFormat.EPSILON): + or abs(self.m_12 - mat.m_12) > NifFormat.EPSILON + or abs(self.m_13 - mat.m_13) > NifFormat.EPSILON + or abs(self.m_21 - mat.m_21) > NifFormat.EPSILON + or abs(self.m_22 - mat.m_22) > NifFormat.EPSILON + or abs(self.m_23 - mat.m_23) > NifFormat.EPSILON + or abs(self.m_31 - mat.m_31) > NifFormat.EPSILON + or abs(self.m_32 - mat.m_32) > NifFormat.EPSILON + or abs(self.m_33 - mat.m_33) > NifFormat.EPSILON): return False return True @@ -3511,7 +3475,7 @@ def update_a_b(self, transform): self.pivot_b.z = pivot_b.z # axes (rotation only) transform = transform.get_matrix_33() - axle_b = self.axle_a.get_vector_3() * transform + axle_b = self.axle_a.get_vector_3() * transform perp_2_axle_in_b_2 = self.perp_2_axle_in_a_2.get_vector_3() * transform self.axle_b.x = axle_b.x self.axle_b.y = axle_b.y @@ -3528,7 +3492,7 @@ def as_list(self): [self.m_21, self.m_22, self.m_23, self.m_24], [self.m_31, self.m_32, self.m_33, self.m_34], [self.m_41, self.m_42, self.m_43, self.m_44] - ] + ] def as_tuple(self): """Return matrix as 4x4 tuple.""" @@ -3537,7 +3501,7 @@ def as_tuple(self): (self.m_21, self.m_22, self.m_23, self.m_24), (self.m_31, self.m_32, self.m_33, self.m_34), (self.m_41, self.m_42, self.m_43, self.m_44) - ) + ) def set_rows(self, row0, row1, row2, row3): """Set matrix from rows.""" @@ -3547,15 +3511,14 @@ def set_rows(self, row0, row1, row2, row3): self.m_41, self.m_42, self.m_43, self.m_44 = row3 def __str__(self): - return( - "[ %6.3f %6.3f %6.3f %6.3f ]\n" - "[ %6.3f %6.3f %6.3f %6.3f ]\n" - "[ %6.3f %6.3f %6.3f %6.3f ]\n" - "[ %6.3f %6.3f %6.3f %6.3f ]\n" - % (self.m_11, self.m_12, self.m_13, self.m_14, - self.m_21, self.m_22, self.m_23, self.m_24, - self.m_31, self.m_32, self.m_33, self.m_34, - self.m_41, self.m_42, self.m_43, self.m_44)) + return ("[ %6.3f %6.3f %6.3f %6.3f ]\n" + "[ %6.3f %6.3f %6.3f %6.3f ]\n" + "[ %6.3f %6.3f %6.3f %6.3f ]\n" + "[ %6.3f %6.3f %6.3f %6.3f ]\n" + % (self.m_11, self.m_12, self.m_13, self.m_14, + self.m_21, self.m_22, self.m_23, self.m_24, + self.m_31, self.m_32, self.m_33, self.m_34, + self.m_41, self.m_42, self.m_43, self.m_44)) def set_identity(self): """Set to identity matrix.""" @@ -3579,21 +3542,21 @@ def set_identity(self): def is_identity(self): """Return ``True`` if the matrix is close to identity.""" if (abs(self.m_11 - 1.0) > NifFormat.EPSILON - or abs(self.m_12) > NifFormat.EPSILON - or abs(self.m_13) > NifFormat.EPSILON - or abs(self.m_14) > NifFormat.EPSILON - or abs(self.m_21) > NifFormat.EPSILON - or abs(self.m_22 - 1.0) > NifFormat.EPSILON - or abs(self.m_23) > NifFormat.EPSILON - or abs(self.m_24) > NifFormat.EPSILON - or abs(self.m_31) > NifFormat.EPSILON - or abs(self.m_32) > NifFormat.EPSILON - or abs(self.m_33 - 1.0) > NifFormat.EPSILON - or abs(self.m_34) > NifFormat.EPSILON - or abs(self.m_41) > NifFormat.EPSILON - or abs(self.m_42) > NifFormat.EPSILON - or abs(self.m_43) > NifFormat.EPSILON - or abs(self.m_44 - 1.0) > NifFormat.EPSILON): + or abs(self.m_12) > NifFormat.EPSILON + or abs(self.m_13) > NifFormat.EPSILON + or abs(self.m_14) > NifFormat.EPSILON + or abs(self.m_21) > NifFormat.EPSILON + or abs(self.m_22 - 1.0) > NifFormat.EPSILON + or abs(self.m_23) > NifFormat.EPSILON + or abs(self.m_24) > NifFormat.EPSILON + or abs(self.m_31) > NifFormat.EPSILON + or abs(self.m_32) > NifFormat.EPSILON + or abs(self.m_33 - 1.0) > NifFormat.EPSILON + or abs(self.m_34) > NifFormat.EPSILON + or abs(self.m_41) > NifFormat.EPSILON + or abs(self.m_42) > NifFormat.EPSILON + or abs(self.m_43) > NifFormat.EPSILON + or abs(self.m_44 - 1.0) > NifFormat.EPSILON): return False else: return True @@ -3664,11 +3627,16 @@ def set_translation(self, translation): self.m_43 = translation.z def is_scale_rotation_translation(self): - if not self.get_matrix_33().is_scale_rotation(): return False - if abs(self.m_14) > NifFormat.EPSILON: return False - if abs(self.m_24) > NifFormat.EPSILON: return False - if abs(self.m_34) > NifFormat.EPSILON: return False - if abs(self.m_44 - 1.0) > NifFormat.EPSILON: return False + if not self.get_matrix_33().is_scale_rotation(): + return False + if abs(self.m_14) > NifFormat.EPSILON: + return False + if abs(self.m_24) > NifFormat.EPSILON: + return False + if abs(self.m_34) > NifFormat.EPSILON: + return False + if abs(self.m_44 - 1.0) > NifFormat.EPSILON: + return False return True def get_scale_rotation_translation(self): @@ -3717,18 +3685,22 @@ def set_scale_rotation_translation(self, scale, rotation, translation): def get_inverse(self, fast=True): """Calculates inverse (fast assumes is_scale_rotation_translation is True).""" + def adjoint(m, ii, jj): result = [] for i, row in enumerate(m): - if i == ii: continue + if i == ii: + continue result.append([]) for j, x in enumerate(row): - if j == jj: continue + if j == jj: + continue result[-1].append(x) return result + def determinant(m): if len(m) == 2: - return m[0][0]*m[1][1] - m[1][0]*m[0][1] + return m[0][0] * m[1][1] - m[1][0] * m[0][1] result = 0.0 for i in range(len(m)): det = determinant(adjoint(m, i, 0)) @@ -3755,10 +3727,10 @@ def determinant(m): nn = [[0.0 for i in range(4)] for j in range(4)] det = determinant(m) if abs(det) < NifFormat.EPSILON: - raise ZeroDivisionError('cannot invert matrix:\n%s'%self) + raise ZeroDivisionError('cannot invert matrix:\n{0}'.format(self)) for i in range(4): for j in range(4): - if (i+j) & 1: + if (i + j) & 1: nn[j][i] = -determinant(adjoint(m, i, j)) / det else: nn[j][i] = determinant(adjoint(m, i, j)) / det @@ -3792,25 +3764,25 @@ def __mul__(self, x): raise TypeError("matrix*vector not supported; please use left multiplication (vector*matrix)") elif isinstance(x, NifFormat.Matrix44): m = NifFormat.Matrix44() - m.m_11 = self.m_11 * x.m_11 + self.m_12 * x.m_21 + self.m_13 * x.m_31 + self.m_14 * x.m_41 - m.m_12 = self.m_11 * x.m_12 + self.m_12 * x.m_22 + self.m_13 * x.m_32 + self.m_14 * x.m_42 - m.m_13 = self.m_11 * x.m_13 + self.m_12 * x.m_23 + self.m_13 * x.m_33 + self.m_14 * x.m_43 - m.m_14 = self.m_11 * x.m_14 + self.m_12 * x.m_24 + self.m_13 * x.m_34 + self.m_14 * x.m_44 - m.m_21 = self.m_21 * x.m_11 + self.m_22 * x.m_21 + self.m_23 * x.m_31 + self.m_24 * x.m_41 - m.m_22 = self.m_21 * x.m_12 + self.m_22 * x.m_22 + self.m_23 * x.m_32 + self.m_24 * x.m_42 - m.m_23 = self.m_21 * x.m_13 + self.m_22 * x.m_23 + self.m_23 * x.m_33 + self.m_24 * x.m_43 - m.m_24 = self.m_21 * x.m_14 + self.m_22 * x.m_24 + self.m_23 * x.m_34 + self.m_24 * x.m_44 - m.m_31 = self.m_31 * x.m_11 + self.m_32 * x.m_21 + self.m_33 * x.m_31 + self.m_34 * x.m_41 - m.m_32 = self.m_31 * x.m_12 + self.m_32 * x.m_22 + self.m_33 * x.m_32 + self.m_34 * x.m_42 - m.m_33 = self.m_31 * x.m_13 + self.m_32 * x.m_23 + self.m_33 * x.m_33 + self.m_34 * x.m_43 - m.m_34 = self.m_31 * x.m_14 + self.m_32 * x.m_24 + self.m_33 * x.m_34 + self.m_34 * x.m_44 - m.m_41 = self.m_41 * x.m_11 + self.m_42 * x.m_21 + self.m_43 * x.m_31 + self.m_44 * x.m_41 - m.m_42 = self.m_41 * x.m_12 + self.m_42 * x.m_22 + self.m_43 * x.m_32 + self.m_44 * x.m_42 - m.m_43 = self.m_41 * x.m_13 + self.m_42 * x.m_23 + self.m_43 * x.m_33 + self.m_44 * x.m_43 - m.m_44 = self.m_41 * x.m_14 + self.m_42 * x.m_24 + self.m_43 * x.m_34 + self.m_44 * x.m_44 + m.m_11 = self.m_11 * x.m_11 + self.m_12 * x.m_21 + self.m_13 * x.m_31 + self.m_14 * x.m_41 + m.m_12 = self.m_11 * x.m_12 + self.m_12 * x.m_22 + self.m_13 * x.m_32 + self.m_14 * x.m_42 + m.m_13 = self.m_11 * x.m_13 + self.m_12 * x.m_23 + self.m_13 * x.m_33 + self.m_14 * x.m_43 + m.m_14 = self.m_11 * x.m_14 + self.m_12 * x.m_24 + self.m_13 * x.m_34 + self.m_14 * x.m_44 + m.m_21 = self.m_21 * x.m_11 + self.m_22 * x.m_21 + self.m_23 * x.m_31 + self.m_24 * x.m_41 + m.m_22 = self.m_21 * x.m_12 + self.m_22 * x.m_22 + self.m_23 * x.m_32 + self.m_24 * x.m_42 + m.m_23 = self.m_21 * x.m_13 + self.m_22 * x.m_23 + self.m_23 * x.m_33 + self.m_24 * x.m_43 + m.m_24 = self.m_21 * x.m_14 + self.m_22 * x.m_24 + self.m_23 * x.m_34 + self.m_24 * x.m_44 + m.m_31 = self.m_31 * x.m_11 + self.m_32 * x.m_21 + self.m_33 * x.m_31 + self.m_34 * x.m_41 + m.m_32 = self.m_31 * x.m_12 + self.m_32 * x.m_22 + self.m_33 * x.m_32 + self.m_34 * x.m_42 + m.m_33 = self.m_31 * x.m_13 + self.m_32 * x.m_23 + self.m_33 * x.m_33 + self.m_34 * x.m_43 + m.m_34 = self.m_31 * x.m_14 + self.m_32 * x.m_24 + self.m_33 * x.m_34 + self.m_34 * x.m_44 + m.m_41 = self.m_41 * x.m_11 + self.m_42 * x.m_21 + self.m_43 * x.m_31 + self.m_44 * x.m_41 + m.m_42 = self.m_41 * x.m_12 + self.m_42 * x.m_22 + self.m_43 * x.m_32 + self.m_44 * x.m_42 + m.m_43 = self.m_41 * x.m_13 + self.m_42 * x.m_23 + self.m_43 * x.m_33 + self.m_44 * x.m_43 + m.m_44 = self.m_41 * x.m_14 + self.m_42 * x.m_24 + self.m_43 * x.m_34 + self.m_44 * x.m_44 return m else: - raise TypeError("do not know how to multiply Matrix44 with %s"%x.__class__) + raise TypeError("do not know how to multiply Matrix44 with %s" % x.__class__) def __div__(self, x): if isinstance(x, (float, int)): @@ -3833,7 +3805,7 @@ def __div__(self, x): m.m_44 = self.m_44 / x return m else: - raise TypeError("do not know how to divide Matrix44 by %s"%x.__class__) + raise TypeError("do not know how to divide Matrix44 by {0}".format(x.__class__)) # py3k __truediv__ = __div__ @@ -3842,29 +3814,45 @@ def __rmul__(self, x): if isinstance(x, (float, int)): return self * x else: - raise TypeError("do not know how to multiply %s with Matrix44"%x.__class__) + raise TypeError("do not know how to multiply {0} with Matrix44".format(x.__class__)) def __eq__(self, m): if isinstance(m, type(None)): return False if not isinstance(m, NifFormat.Matrix44): - raise TypeError("do not know how to compare Matrix44 and %s"%m.__class__) - if abs(self.m_11 - m.m_11) > NifFormat.EPSILON: return False - if abs(self.m_12 - m.m_12) > NifFormat.EPSILON: return False - if abs(self.m_13 - m.m_13) > NifFormat.EPSILON: return False - if abs(self.m_14 - m.m_14) > NifFormat.EPSILON: return False - if abs(self.m_21 - m.m_21) > NifFormat.EPSILON: return False - if abs(self.m_22 - m.m_22) > NifFormat.EPSILON: return False - if abs(self.m_23 - m.m_23) > NifFormat.EPSILON: return False - if abs(self.m_24 - m.m_24) > NifFormat.EPSILON: return False - if abs(self.m_31 - m.m_31) > NifFormat.EPSILON: return False - if abs(self.m_32 - m.m_32) > NifFormat.EPSILON: return False - if abs(self.m_33 - m.m_33) > NifFormat.EPSILON: return False - if abs(self.m_34 - m.m_34) > NifFormat.EPSILON: return False - if abs(self.m_41 - m.m_41) > NifFormat.EPSILON: return False - if abs(self.m_42 - m.m_42) > NifFormat.EPSILON: return False - if abs(self.m_43 - m.m_43) > NifFormat.EPSILON: return False - if abs(self.m_44 - m.m_44) > NifFormat.EPSILON: return False + raise TypeError("do not know how to compare Matrix44 and {0}".format(m.__class__)) + if abs(self.m_11 - m.m_11) > NifFormat.EPSILON: + return False + if abs(self.m_12 - m.m_12) > NifFormat.EPSILON: + return False + if abs(self.m_13 - m.m_13) > NifFormat.EPSILON: + return False + if abs(self.m_14 - m.m_14) > NifFormat.EPSILON: + return False + if abs(self.m_21 - m.m_21) > NifFormat.EPSILON: + return False + if abs(self.m_22 - m.m_22) > NifFormat.EPSILON: + return False + if abs(self.m_23 - m.m_23) > NifFormat.EPSILON: + return False + if abs(self.m_24 - m.m_24) > NifFormat.EPSILON: + return False + if abs(self.m_31 - m.m_31) > NifFormat.EPSILON: + return False + if abs(self.m_32 - m.m_32) > NifFormat.EPSILON: + return False + if abs(self.m_33 - m.m_33) > NifFormat.EPSILON: + return False + if abs(self.m_34 - m.m_34) > NifFormat.EPSILON: + return False + if abs(self.m_41 - m.m_41) > NifFormat.EPSILON: + return False + if abs(self.m_42 - m.m_42) > NifFormat.EPSILON: + return False + if abs(self.m_43 - m.m_43) > NifFormat.EPSILON: + return False + if abs(self.m_44 - m.m_44) > NifFormat.EPSILON: + return False return True def __ne__(self, m): @@ -3910,7 +3898,7 @@ def __add__(self, x): m.m_44 = self.m_44 + x return m else: - raise TypeError("do not know how to add Matrix44 and %s"%x.__class__) + raise TypeError("do not know how to add Matrix44 and {0}".format(x.__class__)) def __sub__(self, x): if isinstance(x, (NifFormat.Matrix44)): @@ -3952,14 +3940,11 @@ def __sub__(self, x): m.m_44 = self.m_44 - x return m else: - raise TypeError("do not know how to substract Matrix44 and %s" - % x.__class__) + raise TypeError("do not know how to substract Matrix44 and {0}".format(x.__class__)) def sup_norm(self): - """Calculate supremum norm of matrix (maximum absolute value of all - entries).""" - return max(max(abs(elem) for elem in row) - for row in self.as_list()) + """Calculate supremum norm of matrix (maximum absolute value of all entries).""" + return max(max(abs(elem) for elem in row) for row in self.as_list()) class NiAVObject: """ @@ -3992,6 +3977,7 @@ class NiAVObject: >>> [prop.name for prop in node.properties] [b'hello', b'world'] """ + def add_property(self, prop): """Add the given property to the property list. @@ -4009,8 +3995,7 @@ def remove_property(self, prop): :param prop: The property block to remove. :type prop: L{NifFormat.NiProperty} """ - self.set_properties([otherprop for otherprop in self.get_properties() - if not(otherprop is prop)]) + self.set_properties([otherprop for otherprop in self.get_properties() if not (otherprop is prop)]) def get_properties(self): """Return a list of the properties of the block. @@ -4045,11 +4030,10 @@ def get_transform(self, relative_to=None): m.set_scale_rotation_translation(self.scale, self.rotation, self.translation) if not relative_to: return m # find chain from relative_to to self - chain = relative_to.find_chain(self, block_type = NifFormat.NiAVObject) + chain = relative_to.find_chain(self, block_type=NifFormat.NiAVObject) if not chain: raise ValueError( - 'cannot find a chain of NiAVObject blocks ' - 'between %s and %s.' % (self.name, relative_to.name)) + 'cannot find a chain of NiAVObject blocks between {0} and {1}.'.format(self.name, relative_to.name)) # and multiply with all transform matrices (not including relative_to) for block in reversed(chain[1:-1]): m *= block.get_transform() @@ -4096,18 +4080,15 @@ def apply_scale(self, scale): class NiBSplineCompTransformInterpolator: def get_translations(self): """Return an iterator over all translation keys.""" - return self._getCompKeys(self.translation_offset, 3, - self.translation_bias, self.translation_multiplier) + return self._getCompKeys(self.translation_offset, 3, self.translation_bias, self.translation_multiplier) def get_rotations(self): """Return an iterator over all rotation keys.""" - return self._getCompKeys(self.rotation_offset, 4, - self.rotation_bias, self.rotation_multiplier) + return self._getCompKeys(self.rotation_offset, 4, self.rotation_bias, self.rotation_multiplier) def get_scales(self): """Return an iterator over all scale keys.""" - for key in self._getCompKeys(self.scale_offset, 1, - self.scale_bias, self.scale_multiplier): + for key in self._getCompKeys(self.scale_offset, 1, self.scale_bias, self.scale_multiplier): yield key[0] def apply_scale(self, scale): @@ -4147,18 +4128,16 @@ class NiBSplineData: >>> list(block.get_comp_data(60, 2, 2, 2.5, 1.5)) # doctest: +ELLIPSIS [(1.0, 2.00...), (4.0, 2.99...)] """ + def _getData(self, offset, num_elements, element_size, controlpoints): """Helper function for get_float_data and get_short_data. For internal use only.""" # check arguments - if not (controlpoints is self.float_control_points - or controlpoints is self.short_control_points): + if not (controlpoints is self.float_control_points or controlpoints is self.short_control_points): raise ValueError("internal error while appending data") # parse the data for element in range(num_elements): - yield tuple( - controlpoints[offset + element * element_size + index] - for index in range(element_size)) + yield tuple(controlpoints[offset + element * element_size + index] for index in range(element_size)) def _appendData(self, data, controlpoints): """Helper function for append_float_data and append_short_data. For internal @@ -4196,8 +4175,7 @@ def get_short_data(self, offset, num_elements, element_size): :param element_size: Size of a single element. :return: A list of C{num_elements} tuples of size C{element_size}. """ - return self._getData( - offset, num_elements, element_size, self.short_control_points) + return self._getData(offset, num_elements, element_size, self.short_control_points) def get_comp_data(self, offset, num_elements, element_size, bias, multiplier): """Get an interator to the data, converted to float with extra bias and @@ -4246,8 +4224,7 @@ def append_comp_data(self, data): for datum in data: shortdata.append(tuple(int(32767 * (x - bias) / multiplier) for x in datum)) - return (self._appendData(shortdata, self.short_control_points), - bias, multiplier) + return (self._appendData(shortdata, self.short_control_points), bias, multiplier) def get_float_data(self, offset, num_elements, element_size): """Get an iterator to the data. @@ -4257,8 +4234,7 @@ def get_float_data(self, offset, num_elements, element_size): :param element_size: Size of a single element. :return: A list of C{num_elements} tuples of size C{element_size}. """ - return self._getData( - offset, num_elements, element_size, self.float_control_points) + return self._getData(offset, num_elements, element_size, self.float_control_points) def append_float_data(self, data): """Append data. @@ -4282,11 +4258,8 @@ def get_times(self): return # return all times for i in range(self.basis_data.num_control_points): - yield ( - self.start_time - + (i * (self.stop_time - self.start_time) - / (self.basis_data.num_control_points - 1)) - ) + yield (self.start_time + ( + i * (self.stop_time - self.start_time) / (self.basis_data.num_control_points - 1))) def _getFloatKeys(self, offset, element_size): """Helper function to get iterator to various keys. Internal use only.""" @@ -4297,9 +4270,7 @@ def _getFloatKeys(self, offset, element_size): if not self.basis_data or not self.spline_data: return # yield all keys - for key in self.spline_data.get_float_data(offset, - self.basis_data.num_control_points, - element_size): + for key in self.spline_data.get_float_data(offset, self.basis_data.num_control_points, element_size): yield key def _getCompKeys(self, offset, element_size, bias, multiplier): @@ -4311,10 +4282,8 @@ def _getCompKeys(self, offset, element_size, bias, multiplier): if not self.basis_data or not self.spline_data: return # yield all keys - for key in self.spline_data.get_comp_data(offset, - self.basis_data.num_control_points, - element_size, - bias, multiplier): + for key in self.spline_data.get_comp_data(offset, self.basis_data.num_control_points, element_size, bias, + multiplier): yield key class NiBSplineTransformInterpolator: @@ -4414,6 +4383,7 @@ class NiGeometryData: (4000, 5000, 6000, 0, 1000, 0, 0, 0, 0, 0, 310, 320, 330, 340) (1200, 3400, 5600, 1000, 0, 0, 97000, 96000, 0, 94000, 0, 0, 0, 0) """ + def update_center_radius(self): """Recalculate center and radius of the data.""" # in case there are no vertices, set center and radius to zero @@ -4446,7 +4416,7 @@ def update_center_radius(self): dx = cx - v.x dy = cy - v.y dz = cz - v.z - r2 = max(r2, dx*dx+dy*dy+dz*dz) + r2 = max(r2, dx * dx + dy * dy + dz * dz) self.radius = r2 ** 0.5 def apply_scale(self, scale): @@ -4461,10 +4431,7 @@ def apply_scale(self, scale): self.center.z *= scale self.radius *= scale - def get_vertex_hash_generator( - self, - vertexprecision=3, normalprecision=3, - uvprecision=5, vcolprecision=3): + def get_vertex_hash_generator(self, vertexprecision=3, normalprecision=3, uvprecision=5, vcolprecision=3): """Generator which produces a tuple of integers for each (vertex, normal, uv, vcol), to ease detection of duplicate vertices. The precision parameters denote number of @@ -4487,7 +4454,7 @@ def get_vertex_hash_generator( :type vcolprecision: float :return: A generator yielding a hash value for each vertex. """ - + verts = self.vertices if self.has_vertices else None norms = self.normals if self.has_normals else None uvsets = self.uv_sets if len(self.uv_sets) else None @@ -4500,7 +4467,7 @@ def get_vertex_hash_generator( h = [] if verts: h.extend([float_to_int(x * vertexfactor) - for x in [verts[i].x, verts[i].y, verts[i].z]]) + for x in [verts[i].x, verts[i].y, verts[i].z]]) if norms: h.extend([float_to_int(x * normalfactor) for x in [norms[i].x, norms[i].y, norms[i].z]]) @@ -4574,17 +4541,19 @@ class NiGeometry: >>> [child.name for child in skelroot.children] [b'geom', b'bone1', b'bone21', b'bone2', b'bone22', b'bone211'] """ + def is_skin(self): """Returns True if geometry is skinned.""" - return self.skin_instance != None + return self.skin_instance is not None def _validate_skin(self): """Check that skinning blocks are valid. Will raise NifError exception if not.""" - if self.skin_instance == None: return - if self.skin_instance.data == None: + if self.skin_instance is None: + return + if self.skin_instance.data is None: raise NifFormat.NifError('NiGeometry has NiSkinInstance without NiSkinData') - if self.skin_instance.skeleton_root == None: + if self.skin_instance.skeleton_root is None: raise NifFormat.NifError('NiGeometry has NiSkinInstance without skeleton root') if self.skin_instance.num_bones != self.skin_instance.data.num_bones: raise NifFormat.NifError('NiSkinInstance and NiSkinData have different number of bones') @@ -4602,10 +4571,10 @@ def add_bone(self, bone, vert_weights): skelroot = skininst.skeleton_root bone_index = skininst.num_bones - skininst.num_bones = bone_index+1 + skininst.num_bones = bone_index + 1 skininst.bones.update_size() skininst.bones[bone_index] = bone - skindata.num_bones = bone_index+1 + skindata.num_bones = bone_index + 1 skindata.bone_list.update_size() skinbonedata = skindata.bone_list[bone_index] # set vertex weights @@ -4615,11 +4584,8 @@ def add_bone(self, bone, vert_weights): skinbonedata.vertex_weights[i].index = vert_index skinbonedata.vertex_weights[i].weight = vert_weight - - def get_vertex_weights(self): - """Get vertex weights in a convenient format: list bone and weight per - vertex.""" + """Get vertex weights in a convenient format: list bone and weight per vertex.""" # shortcuts relevant blocks if not self.skin_instance: raise NifFormat.NifError('Cannot get vertex weights of geometry without skin.') @@ -4648,33 +4614,32 @@ def get_vertex_weights(self): boneweightlist.append([bonenum, skinweight.weight]) return weights - def flatten_skin(self): """Reposition all bone blocks and geometry block in the tree to be direct children of the skeleton root. Returns list of all used bones by the skin.""" - if not self.is_skin(): return [] # nothing to do + if not self.is_skin(): return [] # nothing to do - result = [] # list of repositioned bones - self._validate_skin() # validate the skin + result = [] # list of repositioned bones + self._validate_skin() # validate the skin skininst = self.skin_instance skindata = skininst.data skelroot = skininst.skeleton_root # reparent geometry self.set_transform(self.get_transform(skelroot)) - geometry_parent = skelroot.find_chain(self, block_type = NifFormat.NiAVObject)[-2] - geometry_parent.remove_child(self) # detatch geometry from tree - skelroot.add_child(self, front = True) # and attatch it to the skeleton root + geometry_parent = skelroot.find_chain(self, block_type=NifFormat.NiAVObject)[-2] + geometry_parent.remove_child(self) # detatch geometry from tree + skelroot.add_child(self, front=True) # and attatch it to the skeleton root # reparent all the bone blocks for bone_block in skininst.bones: # skeleton root, if it is used as bone, does not need to be processed if bone_block == skelroot: continue # get bone parent - bone_parent = skelroot.find_chain(bone_block, block_type = NifFormat.NiAVObject)[-2] + bone_parent = skelroot.find_chain(bone_block, block_type=NifFormat.NiAVObject)[-2] # set new child transforms for child in bone_block.children: child.set_transform(child.get_transform(bone_parent)) @@ -4682,7 +4647,7 @@ def flatten_skin(self): for child in bone_block.children: bone_parent.add_child(child) bone_block.num_children = 0 - bone_block.children.update_size() # = remove_child on each child + bone_block.children.update_size() # = remove_child on each child # set new bone transform bone_block.set_transform(bone_block.get_transform(skelroot)) # reparent bone block @@ -4692,8 +4657,6 @@ def flatten_skin(self): return result - - # The nif skinning algorithm works as follows (as of nifskope): # v' # vertex after skinning in geometry space # = sum over {b in skininst.bones} # sum over all bones b that influence the mesh @@ -4715,9 +4678,9 @@ def get_skin_deformation(self): skindata = skininst.data skelroot = skininst.skeleton_root - vertices = [ NifFormat.Vector3() for i in range(self.data.num_vertices) ] - normals = [ NifFormat.Vector3() for i in range(self.data.num_vertices) ] - sumweights = [ 0.0 for i in range(self.data.num_vertices) ] + vertices = [NifFormat.Vector3() for i in range(self.data.num_vertices)] + normals = [NifFormat.Vector3() for i in range(self.data.num_vertices)] + sumweights = [0.0 for i in range(self.data.num_vertices)] skin_offset = skindata.get_transform() # store one transform & rotation per bone bone_transforms = [] @@ -4727,8 +4690,8 @@ def get_skin_deformation(self): bone_matrix = bone_block.get_transform(skelroot) transform = bone_offset * bone_matrix * skin_offset scale, rotation, translation = transform.get_scale_rotation_translation() - bone_transforms.append( (transform, rotation) ) - + bone_transforms.append((transform, rotation)) + # the usual case if skindata.has_vertex_weights: for i, bone_block in enumerate(skininst.bones): @@ -4741,6 +4704,7 @@ def get_skin_deformation(self): if self.data.has_normals: normals[index] += weight * (self.data.normals[index] * rotation) sumweights[index] += weight + # we must get weights from the partition else: skinpartition = skininst.skin_partition @@ -4765,20 +4729,17 @@ def get_skin_deformation(self): sumweights[vert_index] += weight for i, s in enumerate(sumweights): - if abs(s - 1.0) > 0.01: + if abs(s - 1.0) > 0.01: logging.getLogger("pyffi.nif.nigeometry").warn( "vertex %i has weights not summing to one" % i) return vertices, normals - - # ported and extended from niflib::NiNode::GoToSkeletonBindPosition() (r2518) def send_bones_to_bind_position(self): """Send all bones to their bind position. - @deprecated: Use L{NifFormat.NiNode.send_bones_to_bind_position} instead of - this function. + @deprecated: Use L{NifFormat.NiNode.send_bones_to_bind_position} instead of this function. """ warnings.warn("use NifFormat.NiNode.send_bones_to_bind_position", DeprecationWarning) @@ -4806,8 +4767,6 @@ def send_bones_to_bind_position(self): child_matrix = child_offset.get_inverse() * parent_offset child_bone.set_transform(child_matrix) - - # ported from niflib::NiSkinData::ResetOffsets (r2561) def update_bind_position(self): """Make current position of the bones the bind position for this geometry. @@ -4830,7 +4789,7 @@ def update_bind_position(self): # calculate bone offsets for i, bone in enumerate(skininst.bones): - skindata.bone_list[i].set_transform(geomtransform * bone.get_transform(skelroot).get_inverse()) + skindata.bone_list[i].set_transform(geomtransform * bone.get_transform(skelroot).get_inverse()) def get_skin_partition(self): """Return the skin partition block.""" @@ -4866,12 +4825,12 @@ def apply_scale(self, scale): key.value.x *= scale key.value.y *= scale key.value.z *= scale - #key.forward.x *= scale - #key.forward.y *= scale - #key.forward.z *= scale - #key.backward.x *= scale - #key.backward.y *= scale - #key.backward.z *= scale + # key.forward.x *= scale + # key.forward.y *= scale + # key.forward.z *= scale + # key.backward.x *= scale + # key.backward.y *= scale + # key.backward.z *= scale # what to do with TBC? class NiMaterialColorController: @@ -4982,6 +4941,7 @@ class NiNode: >>> [effect.name for effect in node.effects] [b'hello', b'world'] """ + def add_child(self, child, front=False): """Add block to child list. @@ -5003,7 +4963,7 @@ def add_child(self, child, front=False): self.children[num_children] = child else: for i in range(num_children, 0, -1): - self.children[i] = self.children[i-1] + self.children[i] = self.children[i - 1] self.children[0] = child def remove_child(self, child): @@ -5013,7 +4973,7 @@ def remove_child(self, child): :type child: L{NifFormat.NiAVObject} """ self.set_children([otherchild for otherchild in self.get_children() - if not(otherchild is child)]) + if not (otherchild is child)]) def get_children(self): """Return a list of the children of the block. @@ -5052,7 +5012,7 @@ def remove_effect(self, effect): :type effect: L{NifFormat.NiDynamicEffect} """ self.set_effects([othereffect for othereffect in self.get_effects() - if not(othereffect is effect)]) + if not (othereffect is effect)]) def get_effects(self): """Return a list of the effects of the block. @@ -5089,8 +5049,7 @@ def merge_external_skeleton_root(self, skelroot): if isinstance(block, NifFormat.NiNode): if block.name: if block.name in bone_dict: - raise ValueError( - "multiple NiNodes with name %s" % block.name) + raise ValueError("multiple NiNodes with name {0}".format(block.name)) bone_dict[block.name] = block # add all non-bone children of the skeleton root to self @@ -5106,10 +5065,8 @@ def merge_external_skeleton_root(self, skelroot): # fix links to skeleton root and bones for externalblock in child.tree(): if isinstance(externalblock, NifFormat.NiSkinInstance): - if not(externalblock.skeleton_root is skelroot): - raise ValueError( - "expected skeleton root %s but got %s" - % (skelroot.name, externalblock.skeleton_root.name)) + if not (externalblock.skeleton_root is skelroot): + raise ValueError("expected skeleton root {0} but got {1}".format(skelroot.name, externalblock.skeleton_root.name)) externalblock.skeleton_root = self for i, externalbone in enumerate(externalblock.bones): externalblock.bones[i] = bone_dict[externalbone.name] @@ -5133,8 +5090,8 @@ def merge_skeleton_roots(self): """ logger = logging.getLogger("pyffi.nif.ninode") - result = [] # list of reparented blocks - failed = [] # list of blocks that could not be reparented + result = [] # list of reparented blocks + failed = [] # list of blocks that could not be reparented id44 = NifFormat.Matrix44() id44.set_identity() @@ -5155,25 +5112,24 @@ def merge_skeleton_roots(self): continue # check transforms if (geom.skin_instance.data.get_transform() - * geom.get_transform(geom.skin_instance.skeleton_root) != id44): + * geom.get_transform(geom.skin_instance.skeleton_root) != id44): logger.warn( "can't rebase %s: global skin data transform does not match " "geometry transform relative to skeleton root" % geom.name) failed.append(geom) - continue # skip this one + continue # skip this one # everything ok! # find geometry parent geomroot = geom.skin_instance.skeleton_root.find_chain(geom)[-2] # reparent - logger.debug("detaching %s from %s" % (geom.name, geomroot.name)) + logger.debug("detaching {0} from {1}".format(geom.name, geomroot.name)) geomroot.remove_child(geom) - logger.debug("attaching %s to %s" % (geom.name, self.name)) + logger.debug("attaching {0} to {1}".format(geom.name, self.name)) self.add_child(geom) # set its new skeleton root geom.skin_instance.skeleton_root = self # fix transform - geom.skin_instance.data.set_transform( - geom.get_transform(self).get_inverse(fast=False)) + geom.skin_instance.data.set_transform(geom.get_transform(self).get_inverse(fast=False)) # and signal that we reparented this block result.append(geom) @@ -5184,9 +5140,7 @@ def get_skinned_geometries(self): skeleton root. """ for geom in self.get_global_iterator(): - if (isinstance(geom, NifFormat.NiGeometry) - and geom.is_skin() - and geom.skin_instance.skeleton_root is self): + if isinstance(geom, NifFormat.NiGeometry) and geom.is_skin() and geom.skin_instance.skeleton_root is self: yield geom def send_geometries_to_bind_position(self): @@ -5213,7 +5167,7 @@ def send_geometries_to_bind_position(self): if not isinstance(bone, NifFormat.NiNode): continue for geom in geoms: - if not geom in sorted_geoms: + if geom not in sorted_geoms: if bone in geom.skin_instance.bones: sorted_geoms.append(geom) geoms = sorted_geoms @@ -5233,15 +5187,13 @@ def send_geometries_to_bind_position(self): if bonenode.name in bone_bind_transform: # calculate difference # (see explanation below) - diff = (bonedata.get_transform() - * bone_bind_transform[bonenode.name] - * geom.get_transform(self).get_inverse(fast=False)) + diff = (bonedata.get_transform() * bone_bind_transform[bonenode.name] * geom.get_transform(self).get_inverse(fast=False)) break if diff.is_identity(): - logger.debug("%s is already in bind position" % geom.name) + logger.debug("{0} is already in bind position".format(geom.name)) else: - logger.info("fixing %s bind position" % geom.name) + logger.info("fixing {0} bind position".format(geom.name)) # explanation: # we must set the bonedata transform T' such that its bone bind # position matrix @@ -5264,8 +5216,7 @@ def send_geometries_to_bind_position(self): logger.debug( "transforming bind position of bone %s" % bonenode.name if bonenode else "") - bonedata.set_transform(diff.get_inverse(fast=False) - * bonedata.get_transform()) + bonedata.set_transform(diff.get_inverse(fast=False) * bonedata.get_transform()) # transform geometry logger.debug("transforming vertices and normals") for vert in geom.data.vertices: @@ -5284,9 +5235,7 @@ def send_geometries_to_bind_position(self): # bonenode can be None; see pyffi issue #3114079 if not bonenode: continue - bone_bind_transform[bonenode.name] = ( - bonedata.get_transform().get_inverse(fast=False) - * geom.get_transform(self)) + bone_bind_transform[bonenode.name] = (bonedata.get_transform().get_inverse(fast=False) * geom.get_transform(self)) # validation: check that bones share bind position bone_bind_transform = {} @@ -5302,17 +5251,11 @@ def send_geometries_to_bind_position(self): continue if bonenode.name in bone_bind_transform: # calculate difference - diff = ((bonedata.get_transform().get_inverse(fast=False) - * geom.get_transform(self)) - - bone_bind_transform[bonenode.name]) + diff = ((bonedata.get_transform().get_inverse(fast=False) * geom.get_transform(self)) - bone_bind_transform[bonenode.name]) # calculate error (sup norm) - error = max(error, - max(max(abs(elem) for elem in row) - for row in diff.as_list())) + error = max(error, max(max(abs(elem) for elem in row) for row in diff.as_list())) else: - bone_bind_transform[bonenode.name] = ( - bonedata.get_transform().get_inverse(fast=False) - * geom.get_transform(self)) + bone_bind_transform[bonenode.name] = (bonedata.get_transform().get_inverse(fast=False) * geom.get_transform(self)) logger.debug("Geometry bind position error is %f" % error) if error > 1e-3: @@ -5338,8 +5281,7 @@ def send_detached_geometries_to_node_position(self): # * then check which geometries belong to which set # (note: bone can be None, see issue #3114079) bonesets = [ - list(set(bone for bone in geom.skin_instance.bones if bone)) - for geom in geoms] + list(set(bone for bone in geom.skin_instance.bones if bone)) for geom in geoms] # the merged flag signals that we are still merging bones merged = True while merged: @@ -5364,9 +5306,7 @@ def send_detached_geometries_to_node_position(self): logger.debug("bones per partition are") for boneset in bonesets: logger.debug(str([bone.name for bone in boneset])) - parts = [[geom for geom in geoms - if set(geom.skin_instance.bones) & set(boneset)] - for boneset in bonesets] + parts = [[geom for geom in geoms if set(geom.skin_instance.bones) & set(boneset)] for boneset in bonesets] logger.debug("geometries per partition are") for part in parts: logger.debug(str([geom.name for geom in part])) @@ -5390,8 +5330,7 @@ def send_detached_geometries_to_node_position(self): logger.debug("reference bone is %s" % lowest_bonenode.name) # find a geometry that has this bone for geom in part: - for bonenode, bonedata in zip(geom.skin_instance.bones, - geom.skin_instance.data.bone_list): + for bonenode, bonedata in zip(geom.skin_instance.bones, geom.skin_instance.data.bone_list): if bonenode is lowest_bonenode: lowest_geom = geom lowest_bonedata = bonedata @@ -5402,17 +5341,14 @@ def send_detached_geometries_to_node_position(self): else: raise RuntimeError("no reference geometry with this bone: bug?") # calculate matrix - diff = (lowest_bonedata.get_transform() - * lowest_bonenode.get_transform(self) - * lowest_geom.get_transform(self).get_inverse(fast=False)) + diff = (lowest_bonedata.get_transform() * lowest_bonenode.get_transform(self) * lowest_geom.get_transform(self).get_inverse(fast=False)) if diff.is_identity(): - logger.debug("%s is already in node position" - % lowest_bonenode.name) + logger.debug("{0} is already in node position".format(lowest_bonenode.name)) continue # now go over all geometries and synchronize their position to the # reference bone for geom in part: - logger.info("moving %s to node position" % geom.name) + logger.info("moving {0} to node position".format(geom.name)) # XXX we're using this trick a few times now # XXX move it to a separate NiGeometry function skininst = geom.skin_instance @@ -5435,10 +5371,9 @@ def send_detached_geometries_to_node_position(self): # v * T * ... = v * D * D^-1 * T * ... = v' * T' * ... # must be kept invariant for bonenode, bonedata in zip(skininst.bones, skindata.bone_list): - logger.debug("transforming bind position of bone %s" - % bonenode.name) - bonedata.set_transform(diff.get_inverse(fast=False) - * bonedata.get_transform()) + logger.debug("transforming bind position of bone {0}".format(bonenode.name)) + bonedata.set_transform(diff.get_inverse(fast=False) * bonedata.get_transform()) + # transform geometry logger.debug("transforming vertices and normals") for vert in geom.data.vertices: @@ -5477,15 +5412,12 @@ def send_bones_to_bind_position(self): # make sure all bone data of shared bones coincides for othergeom, otherbonenode, otherbonedata in bonelist: if bonenode is otherbonenode: - diff = ((otherbonedata.get_transform().get_inverse(fast=False) - * - othergeom.get_transform(self)) - - - (bonedata.get_transform().get_inverse(fast=False) - * - geom.get_transform(self))) + diff = ((otherbonedata.get_transform().get_inverse(fast=False) * othergeom.get_transform(self)) + - (bonedata.get_transform().get_inverse(fast=False) * geom.get_transform(self))) if diff.sup_norm() > 1e-3: - logger.warning("Geometries %s and %s do not share the same bind position: bone %s will be sent to a position matching only one of these" % (geom.name, othergeom.name, bonenode.name)) + logger.warning( + "Geometries %s and %s do not share the same bind position: bone %s will be sent to a position matching only one of these" % ( + geom.name, othergeom.name, bonenode.name)) # break the loop break else: @@ -5517,13 +5449,11 @@ def send_bones_to_bind_position(self): # calculate desired transform relative to skeleton root # transform is DIFF * PARENT - transform = (bonedata.get_transform().get_inverse(fast=False) - * geom.get_transform(self)) + transform = (bonedata.get_transform().get_inverse(fast=False) * geom.get_transform(self)) # calculate difference diff = transform * bonenode.get_transform(self).get_inverse(fast=False) if not diff.is_identity(): - logger.info("Sending %s to bind position" - % bonenode.name) + logger.info("Sending {0} to bind position".format(bonenode.name)) # fix transform of this node bonenode.set_transform(diff * bonenode.get_transform()) # fix transform of all its children @@ -5532,8 +5462,7 @@ def send_bones_to_bind_position(self): if childnode: childnode.set_transform(childnode.get_transform() * diff_inv) else: - logger.debug("%s is already in bind position" - % bonenode.name) + logger.debug("{0} is already in bind position".format(bonenode.name)) # validate error = 0.0 @@ -5548,15 +5477,11 @@ def send_bones_to_bind_position(self): # bone can be None; see pyffi issue #3114079 if bone is None: continue - diff = ((skindata.bone_list[i].get_transform().get_inverse(fast=False) - * geomtransform) - - bone.get_transform(self)) + diff = ((skindata.bone_list[i].get_transform().get_inverse(fast=False) * geomtransform) - bone.get_transform(self)) # calculate error (sup norm) - diff_error = max(max(abs(elem) for elem in row) - for row in diff.as_list()) + diff_error = max(max(abs(elem) for elem in row) for row in diff.as_list()) if diff_error > 1e-3: - logger.warning( - "Failed to set bind position of bone %s for geometry %s (error is %f)" + logger.warning("Failed to set bind position of bone %s for geometry %s (error is %f)" % (bone.name, geom.name, diff_error)) error = max(error, diff_error) @@ -5620,7 +5545,7 @@ def get_extra_datas(self): xtras = [xtra for xtra in self.extra_data_list] xtra = self.extra_data while xtra: - if not xtra in self.extra_data_list: + if xtra not in self.extra_data_list: xtras.append(xtra) xtra = xtra.next_extra_data return xtras @@ -5712,30 +5637,34 @@ def add_integer_extra_data(self, name, value): self.add_extra_data(extra) class NiObject: - def find(self, block_name = None, block_type = None): + def find(self, block_name=None, block_type=None): # does this block match the search criteria? if block_name and block_type: if isinstance(self, block_type): try: - if block_name == self.name: return self + if block_name == self.name: + return self except AttributeError: pass elif block_name: try: - if block_name == self.name: return self + if block_name == self.name: + return self except AttributeError: pass elif block_type: - if isinstance(self, block_type): return self + if isinstance(self, block_type): + return self # ok, this block is not a match, so check further down in tree for child in self.get_refs(): blk = child.find(block_name, block_type) - if blk: return blk + if blk: + return blk return None - def find_chain(self, block, block_type = None): + def find_chain(self, block, block_type=None): """Finds a chain of blocks going from C{self} to C{block}. If found, self is the first element and block is the last element. If no branch found, returns an empty list. Does not check whether there is more @@ -5760,7 +5689,7 @@ def apply_scale(self, scale): """ pass - def tree(self, block_type = None, follow_all = True, unique = False): + def tree(self, block_type=None, follow_all=True, unique=False): """A generator for parsing all blocks in the tree (starting from and including C{self}). @@ -5771,8 +5700,8 @@ def tree(self, block_type = None, follow_all = True, unique = False): # unique blocks: reduce this to the case of non-unique blocks if unique: block_list = [] - for block in self.tree(block_type = block_type, follow_all = follow_all, unique = False): - if not block in block_list: + for block in self.tree(block_type=block_type, follow_all=follow_all, unique=False): + if block not in block_list: yield block block_list.append(block) return @@ -5783,11 +5712,11 @@ def tree(self, block_type = None, follow_all = True, unique = False): elif isinstance(self, block_type): yield self elif not follow_all: - return # don't recurse further + return # don't recurse further # yield tree attached to each child for child in self.get_refs(): - for block in child.tree(block_type = block_type, follow_all = follow_all): + for block in child.tree(block_type=block_type, follow_all=follow_all): yield block def _validateTree(self): @@ -5810,8 +5739,7 @@ def is_interchangeable(self, other): """ if isinstance(self, (NifFormat.NiProperty, NifFormat.NiSourceTexture)): # use hash for properties and source textures - return ((self.__class__ is other.__class__) - and (self.get_hash() == other.get_hash())) + return self.__class__ is other.__class__ and self.get_hash() == other.get_hash() else: # for blocks with references: quick check only return self is other @@ -5823,8 +5751,7 @@ def is_interchangeable(self, other): b"dynalpha", b"hidesecret", b"lava") if self.__class__ is not other.__class__: return False - if (self.name.lower() in specialnames - or other.name.lower() in specialnames): + if self.name.lower() in specialnames or other.name.lower() in specialnames: # do not ignore name return self.get_hash() == other.get_hash() else: @@ -5840,8 +5767,7 @@ def save_as_dds(self, stream): pixeldata = data.pixeldata # create header, depending on the format - if self.pixel_format in (NifFormat.PixelFormat.PX_FMT_RGB8, - NifFormat.PixelFormat.PX_FMT_RGBA8): + if self.pixel_format in (NifFormat.PixelFormat.PX_FMT_RGB8, NifFormat.PixelFormat.PX_FMT_RGBA8): # uncompressed RGB(A) header.flags.caps = 1 header.flags.height = 1 @@ -5904,16 +5830,13 @@ def save_as_dds(self, stream): header.caps_1.complex = 1 header.caps_1.texture = 1 header.caps_1.mipmap = 1 - if isinstance(self, - NifFormat.NiPersistentSrcTextureRendererData): + if isinstance(self, NifFormat.NiPersistentSrcTextureRendererData): pixeldata.set_value( ''.join( - ''.join([chr(x) for x in tex]) - for tex in self.pixel_data)) + ''.join([chr(x) for x in tex]) for tex in self.pixel_data)) else: pixeldata.set_value(''.join(self.pixel_data_matrix)) - elif self.pixel_format in (NifFormat.PixelFormat.PX_FMT_DXT5, - NifFormat.PixelFormat.PX_FMT_DXT5_ALT): + elif self.pixel_format in (NifFormat.PixelFormat.PX_FMT_DXT5, NifFormat.PixelFormat.PX_FMT_DXT5_ALT): # format used in Megami Tensei: Imagine header.flags.caps = 1 header.flags.height = 1 @@ -5937,8 +5860,7 @@ def save_as_dds(self, stream): header.caps_1.mipmap = 1 pixeldata.set_value(''.join(self.pixel_data_matrix)) else: - raise ValueError( - "cannot save pixel format %i as DDS" % self.pixel_format) + raise ValueError("cannot save pixel format %i as DDS" % self.pixel_format) data.write(stream) @@ -5946,10 +5868,7 @@ class SkinTransform: def get_transform(self): """Return scale, rotation, and translation into a single 4x4 matrix.""" mat = NifFormat.Matrix44() - mat.set_scale_rotation_translation( - self.scale, - self.rotation, - self.translation) + mat.set_scale_rotation_translation(self.scale, self.rotation, self.translation) return mat def set_transform(self, mat): @@ -6072,15 +5991,14 @@ def is_interchangeable(self, other): return False # check class - if (not isinstance(self, other.__class__) - or not isinstance(other, self.__class__)): + if not isinstance(self, other.__class__) or not isinstance(other, self.__class__): return False # check some trivial things first for attribute in ( - "num_vertices", "keep_flags", "compress_flags", "has_vertices", - "num_uv_sets", "has_normals", "center", "radius", - "has_vertex_colors", "has_uv", "consistency_flags"): + "num_vertices", "keep_flags", "compress_flags", "has_vertices", + "num_uv_sets", "has_normals", "center", "radius", + "has_vertex_colors", "has_uv", "consistency_flags"): if getattr(self, attribute) != getattr(other, attribute): return False @@ -6088,10 +6006,10 @@ def is_interchangeable(self, other): verthashes1 = [hsh for hsh in self.get_vertex_hash_generator()] verthashes2 = [hsh for hsh in other.get_vertex_hash_generator()] for hash1 in verthashes1: - if not hash1 in verthashes2: + if hash1 not in verthashes2: return False for hash2 in verthashes2: - if not hash2 in verthashes1: + if hash2 not in verthashes1: return False # check triangle list @@ -6100,10 +6018,10 @@ def is_interchangeable(self, other): triangles2 = [tuple(verthashes2[i] for i in tri) for tri in other.get_triangles()] for tri1 in triangles1: - if not tri1 in triangles2: + if tri1 not in triangles2: return False for tri2 in triangles2: - if not tri2 in triangles1: + if tri2 not in triangles1: return False # looks pretty identical! @@ -6135,6 +6053,7 @@ def get_triangle_indices(self, triangles): :param triangles: An iterable of triangles to check. :type triangles: iterator or list of tuples of three ints """ + def triangleHash(triangle): """Calculate hash of a non-degenerate triangle. Returns ``None`` if the triangle is degenerate. @@ -6147,8 +6066,7 @@ def triangleHash(triangle): return hash((triangle[2], triangle[0], triangle[1])) # calculate hashes of all triangles in the geometry - self_triangles_hashes = [ - triangleHash(triangle) for triangle in self.get_triangles()] + self_triangles_hashes = [triangleHash(triangle) for triangle in self.get_triangles()] # calculate index of each triangle in the list of triangles for triangle in triangles: @@ -6169,19 +6087,18 @@ def bytes2vectors(data, pos, num): vec = NifFormat.Vector3() # XXX _byte_order! assuming little endian vec.x, vec.y, vec.z = struct.unpack(' maxbonespervertex: # delete bone influences with least weight - weight.sort(key=lambda x: x[1], reverse=True) # sort by weight + weight.sort(key=lambda x: x[1], reverse=True) # sort by weight # save lost weight to return to user lostweight = max( lostweight, max( [x[1] for x in weight[maxbonespervertex:]])) - del weight[maxbonespervertex:] # only keep first elements + del weight[maxbonespervertex:] # only keep first elements # normalize - totalweight = sum([x[1] for x in weight]) # sum of all weights + totalweight = sum([x[1] for x in weight]) # sum of all weights for x in weight: x[1] /= totalweight maxbones = maxbonespervertex # sort by again by bone (relied on later when matching vertices) @@ -6503,9 +6410,7 @@ def update_skin_partition(self, # reduce bone influences to meet maximum number of bones per partition # (i.e. maximum number of bones per triangle) - logger.info( - "Imposing maximum of %i bones per triangle (and hence, per partition)." - % maxbonesperpartition) + logger.info("Imposing maximum of %i bones per triangle (and hence, per partition)." % maxbonesperpartition) if triangles is None: triangles = geomdata.get_triangles() @@ -6526,7 +6431,7 @@ def update_skin_partition(self, # this triangle tribonesweights = {} for bonenum in tribones: tribonesweights[bonenum] = 0.0 - nono = set() # bones with weight 1 cannot be removed + nono = set() # bones with weight 1 cannot be removed for skinweights in [weights[t] for t in tri]: # skinweights[0] is the first skinweight influencing vertex t # and skinweights[0][0] is the bone number of that bone @@ -6538,12 +6443,9 @@ def update_skin_partition(self, # first find bones we can remove # restrict to bones not in the nono set - tribonesweights = [ - x for x in list(tribonesweights.items()) if x[0] not in nono] + tribonesweights = [x for x in list(tribonesweights.items()) if x[0] not in nono] if not tribonesweights: - raise ValueError( - "cannot remove anymore bones in this skin; " - "increase maxbonesperpartition and try again") + raise ValueError("cannot remove anymore bones in this skin; increase maxbonesperpartition and try again") # sort by vertex weight sum the last element of this list is now a # candidate for removal tribonesweights.sort(key=lambda x: x[1], reverse=True) @@ -6552,7 +6454,7 @@ def update_skin_partition(self, # remove minbone from all vertices of this triangle and from all # matching vertices for t in tri: - for tt in [t]: #match[t]: + for tt in [t]: # match[t]: # remove bone weight = weights[tt] for i, (bonenum, boneweight) in enumerate(weight): @@ -6574,7 +6476,7 @@ def update_skin_partition(self, # keep creating partitions as long as there are triangles left while triangles: # create a partition - part = [set(), [], None] # bones, triangles, partition index + part = [set(), [], None] # bones, triangles, partition index usedverts = set() addtriangles = True # keep adding triangles to it as long as the flag is set @@ -6594,7 +6496,7 @@ def update_skin_partition(self, # or if part has all bones of tribones and index coincides # then add this triangle to this part if ((not part[0]) - or ((part[0] >= tribones) and (part[2] == partindex))): + or ((part[0] >= tribones) and (part[2] == partindex))): part[0] |= tribones part[1].append(tri) usedverts |= set(tri) @@ -6647,7 +6549,7 @@ def update_skin_partition(self, # merge all partitions logger.info("Merging partitions.") - merged = True # signals success, in which case do another run + merged = True # signals success, in which case do another run while merged: merged = False # newparts is to contain the updated merged partitions as we go @@ -6668,12 +6570,11 @@ def update_skin_partition(self, continue # if partition indices are the same, and bone limit is not # exceeded, merge them - if ((parta[2] == partb[2]) - and (len(parta[0] | partb[0]) <= maxbonesperpartition)): + if (parta[2] == partb[2] and len(parta[0] | partb[0]) <= maxbonesperpartition): parta[0] |= partb[0] parta[1] += partb[1] addedparts.add(b) - merged = True # signal another try in merging partitions + merged = True # signal another try in merging partitions # update partitions to the merged partitions parts = newparts @@ -6681,14 +6582,14 @@ def update_skin_partition(self, logger.info("Skin has %i partitions." % len(parts)) # if skin partition already exists, use it - if skindata.skin_partition != None: + if skindata.skin_partition is not None: skinpart = skindata.skin_partition skininst.skin_partition = skinpart - elif skininst.skin_partition != None: + elif skininst.skin_partition is not None: skinpart = skininst.skin_partition skindata.skin_partition = skinpart else: - # otherwise, create a new block and link it + # otherwise, create a new block and link it skinpart = NifFormat.NiSkinPartition() skindata.skin_partition = skinpart skininst.skin_partition = skinpart @@ -6747,8 +6648,7 @@ def update_skin_partition(self, # do not start new bone set bodypart.part_flag.start_new_boneset = 0 # caps are invisible - bodypart.part_flag.editor_visible = (part[2] < 100 - or part[2] >= 1000) + bodypart.part_flag.editor_visible = (part[2] < 100 or part[2] >= 1000) # store part for next iteration lastpart = part @@ -6756,21 +6656,16 @@ def update_skin_partition(self, # get sorted list of bones bones = sorted(list(part[0])) triangles = part[1] - logger.info("Optimizing triangle ordering in partition %i" - % parts.index(part)) + logger.info("Optimizing triangle ordering in partition %i" % parts.index(part)) # optimize triangles for vertex cache and calculate strips - triangles = pyffi.utils.vertex_cache.get_cache_optimized_triangles( - triangles) - strips = pyffi.utils.vertex_cache.stable_stripify( - triangles, stitchstrips=stitchstrips) + triangles = pyffi.utils.vertex_cache.get_cache_optimized_triangles(triangles) + strips = pyffi.utils.vertex_cache.stable_stripify(triangles, stitchstrips=stitchstrips) triangles_size = 3 * len(triangles) strips_size = len(strips) + sum(len(strip) for strip in strips) vertices = [] # decide whether to use strip or triangles as primitive if stripify is None: - stripifyblock = ( - strips_size < triangles_size - and all(len(strip) < 65536 for strip in strips)) + stripifyblock = (strips_size < triangles_size and all(len(strip) < 65536 for strip in strips)) else: stripifyblock = stripify if stripifyblock: @@ -6802,11 +6697,8 @@ def update_skin_partition(self, skinpartblock.num_bones = len(bones) else: if maxbonesperpartition != maxbonespervertex: - raise ValueError( - "when padding bones maxbonesperpartition must be " - "equal to maxbonespervertex") - # freedom force vs. the 3rd reich needs exactly 4 bones per - # partition on every partition block + raise ValueError("when padding bones maxbonesperpartition must be equal to maxbonespervertex") + # freedom force vs. the 3rd reich needs exactly 4 bones per partition on every partition block skinpartblock.num_bones = maxbonesperpartition if stripifyblock: skinpartblock.num_strips = len(strips) @@ -6820,7 +6712,7 @@ def update_skin_partition(self, for i, bonenum in enumerate(bones): skinpartblock.bones[i] = bonenum for i in range(len(bones), skinpartblock.num_bones): - skinpartblock.bones[i] = 0 # dummy bone slots refer to first bone + skinpartblock.bones[i] = 0 # dummy bone slots refer to first bone skinpartblock.has_vertex_map = True skinpartblock.vertex_map.update_size() for i, v in enumerate(vertices): @@ -6849,20 +6741,19 @@ def update_skin_partition(self, # clear strips array skinpartblock.strips.update_size() skinpartblock.triangles.update_size() - for i, (v_1,v_2,v_3) in enumerate(triangles): + for i, (v_1, v_2, v_3) in enumerate(triangles): skinpartblock.triangles[i].v_1 = vertices.index(v_1) skinpartblock.triangles[i].v_2 = vertices.index(v_2) skinpartblock.triangles[i].v_3 = vertices.index(v_3) skinpartblock.has_bone_indices = True skinpartblock.bone_indices.update_size() for i, v in enumerate(vertices): - # the boneindices set keeps track of indices that have not been - # used yet + # the boneindices set keeps track of indices that have not been used yet boneindices = set(range(skinpartblock.num_bones)) for j in range(len(weights[v])): skinpartblock.bone_indices[i][j] = bones.index(weights[v][j][0]) boneindices.remove(skinpartblock.bone_indices[i][j]) - for j in range(len(weights[v]),skinpartblock.num_weights_per_vertex): + for j in range(len(weights[v]), skinpartblock.num_weights_per_vertex): if padbones: # if padbones is True then we have enforced # num_bones == num_weights_per_vertex so this will not trigger @@ -6895,7 +6786,7 @@ def update_skin_center_radius(self): """Update centers and radii of all skin data fields.""" # shortcuts relevant blocks if not self.skin_instance: - return # no skin, nothing to do + return # no skin, nothing to do self._validate_skin() geomdata = self.data skininst = self.skin_instance @@ -6905,8 +6796,7 @@ def update_skin_center_radius(self): for skindatablock in skindata.bone_list: # find all vertices influenced by this bone - boneverts = [verts[skinweight.index] - for skinweight in skindatablock.vertex_weights] + boneverts = [verts[skinweight.index] for skinweight in skindatablock.vertex_weights] # find bounding box of these vertices low = NifFormat.Vector3() @@ -6926,7 +6816,7 @@ def update_skin_center_radius(self): r2 = 0.0 for v in boneverts: d = center - v - r2 = max(r2, d.x*d.x+d.y*d.y+d.z*d.z) + r2 = max(r2, d.x * d.x + d.y * d.y + d.z * d.z) radius = r2 ** 0.5 # transform center in proper coordinates (radius remains unaffected) @@ -6944,11 +6834,9 @@ def get_interchangeable_tri_shape(self, triangles=None): from the original shape, use the triangles argument. """ # copy the shape (first to NiTriBasedGeom and then to NiTriShape) - shape = NifFormat.NiTriShape().deepcopy( - NifFormat.NiTriBasedGeom().deepcopy(self)) + shape = NifFormat.NiTriShape().deepcopy(NifFormat.NiTriBasedGeom().deepcopy(self)) # copy the geometry without strips - shapedata = NifFormat.NiTriShapeData().deepcopy( - NifFormat.NiTriBasedGeomData().deepcopy(self.data)) + shapedata = NifFormat.NiTriShapeData().deepcopy(NifFormat.NiTriBasedGeomData().deepcopy(self.data)) # update the shape data if triangles is None: shapedata.set_triangles(self.data.get_triangles()) @@ -6965,11 +6853,9 @@ def get_interchangeable_tri_strips(self, strips=None): from the original shape, use the strips argument. """ # copy the shape (first to NiTriBasedGeom and then to NiTriStrips) - strips_ = NifFormat.NiTriStrips().deepcopy( - NifFormat.NiTriBasedGeom().deepcopy(self)) + strips_ = NifFormat.NiTriStrips().deepcopy(NifFormat.NiTriBasedGeom().deepcopy(self)) # copy the geometry without triangles - stripsdata = NifFormat.NiTriStripsData().deepcopy( - NifFormat.NiTriBasedGeomData().deepcopy(self.data)) + stripsdata = NifFormat.NiTriStripsData().deepcopy(NifFormat.NiTriBasedGeomData().deepcopy(self.data)) # update the shape data if strips is None: stripsdata.set_strips(self.data.get_strips()) @@ -6997,17 +6883,18 @@ class NiTriShapeData: >>> block.get_triangles() [(0, 2, 1), (1, 2, 3), (2, 4, 3)] """ + def get_triangles(self): return [(t.v_1, t.v_2, t.v_3) for t in self.triangles] - def set_triangles(self, triangles, stitchstrips = False): + def set_triangles(self, triangles, stitchstrips=False): # note: the stitchstrips argument is ignored - only present to ensure # uniform interface between NiTriShapeData and NiTriStripsData # initialize triangle array n = len(triangles) self.num_triangles = n - self.num_triangle_points = 3*n + self.num_triangle_points = 3 * n self.has_triangles = (n > 0) self.triangles.update_size() @@ -7027,7 +6914,7 @@ class NiTriStripsData: >>> from pyffi.formats.nif import NifFormat >>> block = NifFormat.NiTriStripsData() - >>> block.set_triangles([(0,1,2),(2,1,3),(2,3,4)]) + >>> block.set_triangles([(0,1,2), (2,1,3), (2,3,4)]) >>> block.get_strips() [[0, 1, 2, 3, 4]] >>> block.get_triangles() @@ -7038,12 +6925,12 @@ class NiTriStripsData: >>> block.get_triangles() [(0, 2, 1), (1, 2, 3), (2, 4, 3)] """ + def get_triangles(self): return pyffi.utils.tristrip.triangulate(self.points) - def set_triangles(self, triangles, stitchstrips = False): - self.set_strips(pyffi.utils.vertex_cache.stripify( - triangles, stitchstrips=stitchstrips)) + def set_triangles(self, triangles, stitchstrips=False): + self.set_strips(pyffi.utils.vertex_cache.stripify(triangles, stitchstrips=stitchstrips)) def get_strips(self): return [[i for i in strip] for strip in self.points] @@ -7075,8 +6962,8 @@ def update_a_b(self, transform): self.pivot_b.z = pivot_b.z # axes (rotation only) transform = transform.get_matrix_33() - plane_b = self.plane_a.get_vector_3() * transform - twist_b = self.twist_a.get_vector_3() * transform + plane_b = self.plane_a.get_vector_3() * transform + twist_b = self.twist_a.get_vector_3() * transform self.plane_b.x = plane_b.x self.plane_b.y = plane_b.y self.plane_b.z = plane_b.z @@ -7115,23 +7002,15 @@ def get_string(self, offset): ... ValueError: ... """ - _b00 = pyffi.object_models.common._b00 # shortcut + _b00 = pyffi.object_models.common._b00 # shortcut # check that offset isn't too large if offset >= len(self.palette): - raise ValueError( - "StringPalette: getting string at %i " - "but palette is only %i long" - % (offset, len(self.palette))) + raise ValueError("StringPalette: getting string at %i but palette is only %i long" % (offset, len(self.palette))) # check that a string starts at this offset - if offset > 0 and self.palette[offset-1:offset] != _b00: + if offset > 0 and self.palette[offset - 1:offset] != _b00: logger = logging.getLogger("pyffi.nif.stringpalette") - logger.warning( - "StringPalette: no string starts at offset %i " - "(string is %s, preceeding character is %s)" % ( - offset, - self.palette[offset:self.palette.find(_b00, offset)], - self.palette[offset-1:offset], - )) + logger.warning("StringPalette: no string starts at offset %i (string is %s, preceeding character is %s)" % + (offset, self.palette[offset:self.palette.find(_b00, offset)], self.palette[offset - 1:offset], )) # return the string return self.palette[offset:self.palette.find(_b00, offset)] @@ -7152,7 +7031,7 @@ def get_all_strings(self): >>> print(repr(pal.palette.decode("ascii")).lstrip("u")) 'abc\\x00def\\x00' """ - _b00 = pyffi.object_models.common._b00 # shortcut + _b00 = pyffi.object_models.common._b00 # shortcut return self.palette[:-1].split(_b00) def add_string(self, text): @@ -7175,7 +7054,7 @@ def add_string(self, text): # empty text if not text: return -1 - _b00 = pyffi.object_models.common._b00 # shortcut + _b00 = pyffi.object_models.common._b00 # shortcut # convert text to bytes if necessary text = pyffi.object_models.common._as_bytes(text) # check if string is already in the palette @@ -7211,7 +7090,7 @@ def clear(self): >>> print(repr(pal.palette.decode("ascii")).lstrip("u")) '' """ - self.palette = pyffi.object_models.common._b # empty bytes object + self.palette = pyffi.object_models.common._b # empty bytes object self.length = 0 class TexCoord: @@ -7219,14 +7098,14 @@ def as_list(self): return [self.u, self.v] def normalize(self): - r = (self.u*self.u + self.v*self.v) ** 0.5 + r = (self.u * self.u + self.v * self.v) ** 0.5 if r < NifFormat.EPSILON: - raise ZeroDivisionError('cannot normalize vector %s'%self) + raise ZeroDivisionError('cannot normalize vector {0}'.format(self)) self.u /= r self.v /= r def __str__(self): - return "[ %6.3f %6.3f ]"%(self.u, self.v) + return "[ %6.3f %6.3f ]" % (self.u, self.v) def __mul__(self, x): if isinstance(x, (float, int)): @@ -7237,7 +7116,7 @@ def __mul__(self, x): elif isinstance(x, NifFormat.TexCoord): return self.u * x.u + self.v * x.v else: - raise TypeError("do not know how to multiply TexCoord with %s"%x.__class__) + raise TypeError("do not know how to multiply TexCoord with {0}".format(x.__class__)) def __rmul__(self, x): if isinstance(x, (float, int)): @@ -7246,7 +7125,7 @@ def __rmul__(self, x): v.v = x * self.v return v else: - raise TypeError("do not know how to multiply %s and TexCoord"%x.__class__) + raise TypeError("do not know how to multiply {0} and TexCoord".format(x.__class__)) def __add__(self, x): if isinstance(x, (float, int)): @@ -7260,7 +7139,7 @@ def __add__(self, x): v.v = self.v + x.v return v else: - raise TypeError("do not know how to add TexCoord and %s"%x.__class__) + raise TypeError("do not know how to add TexCoord and {0}".format(x.__class__)) def __radd__(self, x): if isinstance(x, (float, int)): @@ -7269,7 +7148,7 @@ def __radd__(self, x): v.v = x + self.v return v else: - raise TypeError("do not know how to add %s and TexCoord"%x.__class__) + raise TypeError("do not know how to add {0} and TexCoord".format(x.__class__)) def __sub__(self, x): if isinstance(x, (float, int)): @@ -7283,7 +7162,7 @@ def __sub__(self, x): v.v = self.v - x.v return v else: - raise TypeError("do not know how to substract TexCoord and %s"%x.__class__) + raise TypeError("do not know how to substract TexCoord and {0}".format(x.__class__)) def __rsub__(self, x): if isinstance(x, (float, int)): @@ -7292,7 +7171,7 @@ def __rsub__(self, x): v.v = x - self.v return v else: - raise TypeError("do not know how to substract %s and TexCoord"%x.__class__) + raise TypeError("do not know how to substract {0} and TexCoord".format(x.__class__)) def __neg__(self): v = NifFormat.TexCoord() @@ -7300,6 +7179,8 @@ def __neg__(self): v.v = -self.v return v -if __name__=='__main__': + +if __name__ == '__main__': import doctest + doctest.testmod() diff --git a/pyffi/object_models/xml/basic.py b/pyffi/object_models/xml/basic.py index 54ad2d22..3a9eae2e 100644 --- a/pyffi/object_models/xml/basic.py +++ b/pyffi/object_models/xml/basic.py @@ -41,6 +41,7 @@ from pyffi.utils.graph import DetailNode + class BasicBase(DetailNode): """Base class from which all basic types are derived. @@ -91,7 +92,7 @@ def __init__(self, template = None, argument = None, parent = None): :param parent: The parent of this instance, that is, the instance this instance is an attribute of.""" # parent disabled for performance - #self._parent = weakref.ref(parent) if parent else None + # self._parent = weakref.ref(parent) if parent else None pass # string representation @@ -168,4 +169,3 @@ def get_editor_value(self): def set_editor_value(self, editorvalue): """Set value from editor value.""" return self.set_value(editorvalue) - diff --git a/pyffi/utils/graph.py b/pyffi/utils/graph.py index e2008855..884a54e2 100644 --- a/pyffi/utils/graph.py +++ b/pyffi/utils/graph.py @@ -104,6 +104,7 @@ def accept(self, edge_type): if edge_type.acyclic != self.acyclic_filter: return False + class DetailNode(object): """A node of the detail tree which can have children.