From a9dd90be788f5ce894fd5a8c9bc8245980ec143e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 28 May 2011 09:34:45 +0000 Subject: [PATCH] move load_image into image_utils and add some docstrings to bpy_extras module. --- doc/python_api/sphinx_doc_gen.py | 5 +- .../scripts/modules/bpy_extras/image_utils.py | 91 ++++++++++++++++--- .../scripts/modules/bpy_extras/io_utils.py | 19 ---- .../scripts/modules/bpy_extras/mesh_utils.py | 40 ++++++-- .../modules/bpy_extras/object_utils.py | 22 +++++ 5 files changed, 134 insertions(+), 43 deletions(-) diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 9575955e13a..e96b4d363b4 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -65,8 +65,9 @@ else: "bpy.props", "bpy.utils", "bpy.context", - # "bpy.types", # supports filtering + "bpy.types", # supports filtering "bpy.ops", # supports filtering + #"bpy_extras", "bge", "aud", "bgl", @@ -363,10 +364,8 @@ def pymodule2sphinx(BASEPATH, module_name, module, title): for submod_name in module_all: ns = {} exec_str = "from %s import %s as submod" % (module.__name__, submod_name) - print(exec_str) exec(exec_str, ns, ns) submod = ns["submod"] - print(submod) if type(submod) == types.ModuleType: submod_ls.append((submod_name, submod)) diff --git a/release/scripts/modules/bpy_extras/image_utils.py b/release/scripts/modules/bpy_extras/image_utils.py index 4789fbc606a..a7d0226fa23 100644 --- a/release/scripts/modules/bpy_extras/image_utils.py +++ b/release/scripts/modules/bpy_extras/image_utils.py @@ -19,19 +19,88 @@ # __all__ = ( - "image_load", + "load_image", ) +# limited replacement for BPyImage.comprehensiveImageLoad +def load_image(imagepath, + dirname="", + place_holder=False, + recursive=False, + ncase_cmp=True, + convert_callback=None, + verbose=False, + ): + """ + Return an image from the file path with options to search multiple paths and + return a placeholder if its not found. -def image_load(filepath, dirpath, place_holder=False, recursive=False, convert_callback=None): - import bpy + :arg filepath: The image filename + If a path precedes it, this will be searched as well. + :type filepath: string + :arg dirname: is the directory where the image may be located - any file at + the end will be ignored. + :type dirname: string + :arg place_holder: if True a new place holder image will be created. + this is usefull so later you can relink the image to its original data. + :type place_holder: bool + :arg recursive: If True, directories will be recursivly searched. + Be carefull with this if you have files in your root directory because + it may take a long time. + :type recursive: bool + :arg ncase_cmp: on non windows systems, find the correct case for the file. + :type ncase_cmp: bool + :arg convert_callback: a function that takes an existing path and returns a new one. + Use this when loading image formats blender may not support, the CONVERT_CALLBACK + can take the path for a GIF (for example), convert it to a PNG and return the PNG's path. + For formats blender can read, simply return the path that is given. + :type convert_callback: function + :return: an image or None + :rtype: :class:`Image` + """ import os - try: - return bpy.data.images.load(filepath) - except RuntimeError: - if place_holder: - image = bpy.data.images.new(os.path.basename(filepath), 128, 128) - # allow the path to be resolved later - image.filepath = filepath - return image + # TODO: recursive + + def _image_load(path): + import bpy + + if convert_callback: + path = convert_callback(path) + + image = bpy.data.images.load(path) + + if verbose: + print(" image loaded '%s'" % path) + + return image + + if verbose: + print("load_image('%s', '%s', ...)" % (imagepath, dirname)) + + if os.path.exists(imagepath): + return _image_load(imagepath) + + variants = [imagepath] + + if dirname: + variants += [os.path.join(dirname, imagepath), os.path.join(dirname, os.path.basename(imagepath))] + + for filepath_test in variants: + if ncase_cmp: + ncase_variants = filepath_test, bpy.path.resolve_ncase(filepath) + else: + ncase_variants = (filepath_test, ) + + for nfilepath in ncase_variants: + if os.path.exists(nfilepath): + return _image_load(nfilepath) + + if place_holder: + image = bpy.data.images.new(os.path.basename(filepath), 128, 128) + # allow the path to be resolved later + image.filepath = imagepath + return image + + # TODO comprehensiveImageLoad also searched in bpy.config.textureDir + return None diff --git a/release/scripts/modules/bpy_extras/io_utils.py b/release/scripts/modules/bpy_extras/io_utils.py index b3bfe3d2737..c444fd618a8 100644 --- a/release/scripts/modules/bpy_extras/io_utils.py +++ b/release/scripts/modules/bpy_extras/io_utils.py @@ -22,7 +22,6 @@ __all__ = ( "ExportHelper", "ImportHelper", "axis_conversion", - "load_image", "create_derived_objects", "free_derived_objects", "unpack_list", @@ -162,24 +161,6 @@ def axis_conversion(from_forward='Y', from_up='Z', to_forward='Y', to_up='Z'): assert("internal error") -# limited replacement for BPyImage.comprehensiveImageLoad -def load_image(imagepath, dirname): - import os - - if os.path.exists(imagepath): - return bpy.data.images.load(imagepath) - - variants = [imagepath, os.path.join(dirname, imagepath), os.path.join(dirname, os.path.basename(imagepath))] - - for filepath in variants: - for nfilepath in (filepath, bpy.path.resolve_ncase(filepath)): - if os.path.exists(nfilepath): - return bpy.data.images.load(nfilepath) - - # TODO comprehensiveImageLoad also searched in bpy.config.textureDir - return None - - # return a tuple (free, object list), free is True if memory should be freed later with free_derived_objects() def create_derived_objects(scene, ob): if ob.parent and ob.parent.dupli_type != 'NONE': diff --git a/release/scripts/modules/bpy_extras/mesh_utils.py b/release/scripts/modules/bpy_extras/mesh_utils.py index 1f1c26a5405..b6d8a1fcf16 100644 --- a/release/scripts/modules/bpy_extras/mesh_utils.py +++ b/release/scripts/modules/bpy_extras/mesh_utils.py @@ -28,11 +28,15 @@ __all__ = ( ) def mesh_linked_faces(mesh): - ''' - Splits the mesh into connected parts, - these parts are returned as lists of faces. - used for seperating cubes from other mesh elements in the 1 mesh - ''' + """ + Splits the mesh into connected faces, use this for seperating cubes from + other mesh elements within 1 mesh datablock. + + :arg mesh: the mesh used to group with. + :type mesh: :class:`Mesh` + :return: lists of lists containing faces. + :rtype: list + """ # Build vert face connectivity vert_faces = [[] for i in range(len(mesh.vertices))] @@ -78,6 +82,11 @@ def mesh_linked_faces(mesh): def edge_face_count_dict(mesh): + """ + :return: dict of edge keys with their value set to the number of + faces using each edge. + :rtype: dict + """ face_edge_keys = [face.edge_keys for face in mesh.faces] face_edge_count = {} for face_keys in face_edge_keys: @@ -91,8 +100,13 @@ def edge_face_count_dict(mesh): def edge_face_count(mesh): + """ + :return: list face users for each item in mesh.edges. + :rtype: list + """ edge_face_count_dict = edge_face_count_dict(mesh) - return [edge_face_count_dict.get(ed.key, 0) for ed in mesh.edges] + get = dict.get + return [get(edge_face_count_dict, ed.key, 0) for ed in mesh.edges] def edge_loops_from_faces(mesh, faces=None, seams=()): @@ -101,12 +115,18 @@ def edge_loops_from_faces(mesh, faces=None, seams=()): Takes me.faces or a list of faces and returns the edge loops These edge loops are the edges that sit between quads, so they dont touch - 1 quad, note: not connected will make 2 edge loops, both only containing 2 edges. + 1 quad, note: not connected will make 2 edge loops, + both only containing 2 edges. return a list of edge key lists - [ [(0,1), (4, 8), (3,8)], ...] + [[(0, 1), (4, 8), (3, 8)], ...] - return a list of edge vertex index lists + :arg mesh: the mesh used to get edge loops from. + :type mesh: :class:`Mesh` + :arg faces: optional face list to only use some of the meshes faces. + :type faces: :class:`MeshFaces`, sequence or or NoneType + :return: return a list of edge vertex index lists. + :rtype: list """ OTHER_INDEX = 2, 3, 0, 1 # opposite face index @@ -117,7 +137,7 @@ def edge_loops_from_faces(mesh, faces=None, seams=()): edges = {} for f in faces: -# if len(f) == 4: +# if len(f) == 4: if f.vertices_raw[3] != 0: edge_keys = f.edge_keys for i, edkey in enumerate(f.edge_keys): diff --git a/release/scripts/modules/bpy_extras/object_utils.py b/release/scripts/modules/bpy_extras/object_utils.py index 606f8b9f89f..51a8d4b5e23 100644 --- a/release/scripts/modules/bpy_extras/object_utils.py +++ b/release/scripts/modules/bpy_extras/object_utils.py @@ -29,6 +29,16 @@ import mathutils def add_object_align_init(context, operator): + """ + Return a matrix using the operator settings and view context. + + :arg context: The context to use. + :type context: :class:`Context` + :arg operator: The operator, checked for location and rotation properties. + :type operator: :class:`Operator` + :return: the matrix from the context and settings. + :rtype: :class:`Matrix` + """ space_data = context.space_data if space_data.type != 'VIEW_3D': space_data = None @@ -70,7 +80,19 @@ def add_object_align_init(context, operator): def object_data_add(context, obdata, operator=None): + """ + Add an object using the view context and preference to to initialize the + location, rotation and layer. + :arg context: The context to use. + :type context: :class:`Context` + :arg obdata: the data used for the new object. + :type obdata: valid object data type or None. + :arg operator: The operator, checked for location and rotation properties. + :type operator: :class:`Operator` + :return: the newly created object in the scene. + :rtype: :class:`ObjectBase` + """ scene = context.scene # ugh, could be made nicer