diff options
Diffstat (limited to 'release/scripts/modules')
17 files changed, 635 insertions, 117 deletions
diff --git a/release/scripts/modules/addon_utils.py b/release/scripts/modules/addon_utils.py index 8c86f31022c..63b01a4b884 100644 --- a/release/scripts/modules/addon_utils.py +++ b/release/scripts/modules/addon_utils.py @@ -74,40 +74,43 @@ def modules_refresh(module_cache=addons_fake_modules): print("fake_module", mod_path, mod_name) import ast ModuleType = type(ast) - file_mod = open(mod_path, "r", encoding='UTF-8') - if speedy: - lines = [] - line_iter = iter(file_mod) - l = "" - while not l.startswith("bl_info"): - try: - l = line_iter.readline() - except UnicodeDecodeError as e: - if not error_encoding: - error_encoding = True - print("Error reading file as UTF-8:", mod_path, e) - file_mod.close() - return None - - if len(l) == 0: - break - while l.rstrip(): - lines.append(l) - try: - l = line_iter.readline() - except UnicodeDecodeError as e: - if not error_encoding: - error_encoding = True - print("Error reading file as UTF-8:", mod_path, e) - file_mod.close() - return None - - data = "".join(lines) + try: + file_mod = open(mod_path, "r", encoding='UTF-8') + except OSError as e: + print("Error opening file %r: %s" % (mod_path, e)) + return None - else: - data = file_mod.read() + with file_mod: + if speedy: + lines = [] + line_iter = iter(file_mod) + l = "" + while not l.startswith("bl_info"): + try: + l = line_iter.readline() + except UnicodeDecodeError as e: + if not error_encoding: + error_encoding = True + print("Error reading file as UTF-8:", mod_path, e) + return None + + if len(l) == 0: + break + while l.rstrip(): + lines.append(l) + try: + l = line_iter.readline() + except UnicodeDecodeError as e: + if not error_encoding: + error_encoding = True + print("Error reading file as UTF-8:", mod_path, e) + return None + + data = "".join(lines) - file_mod.close() + else: + data = file_mod.read() + del file_mod try: ast_data = ast.parse(data, filename=mod_path) @@ -416,19 +419,21 @@ def reset_all(reload_scripts=False): disable(mod_name) -def module_bl_info(mod, info_basis={"name": "", - "author": "", - "version": (), - "blender": (), - "location": "", - "description": "", - "wiki_url": "", - "support": 'COMMUNITY', - "category": "", - "warning": "", - "show_expanded": False, - } - ): +def module_bl_info(mod, info_basis=None): + if info_basis is None: + info_basis = { + "name": "", + "author": "", + "version": (), + "blender": (), + "location": "", + "description": "", + "wiki_url": "", + "support": 'COMMUNITY', + "category": "", + "warning": "", + "show_expanded": False, + } addon_info = getattr(mod, "bl_info", {}) diff --git a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py index c3b2ae9908b..43a09a1acbd 100644 --- a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py +++ b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py @@ -64,7 +64,7 @@ def _gen_check_ctxt(settings): def _diff_check_ctxt(check_ctxt, minus_check_ctxt): - """Returns check_ctxt - minus_check_ctxt""" + """Removes minus_check_ctxt from check_ctxt""" for key in check_ctxt: if isinstance(check_ctxt[key], set): for warning in minus_check_ctxt[key]: @@ -576,8 +576,9 @@ def dump_py_messages_from_files(msgs, reports, files, settings): #print(func_translate_args) # Break recursive nodes look up on some kind of nodes. - # E.g. we don’t want to get strings inside subscripts (blah["foo"])! - stopper_nodes = {ast.Subscript} + # E.g. we don't want to get strings inside subscripts (blah["foo"])! + # we don't want to get strings from comparisons (foo.type == 'BAR'). + stopper_nodes = {ast.Subscript, ast.Compare} # Consider strings separate: ("a" if test else "b") separate_nodes = {ast.IfExp} @@ -897,7 +898,7 @@ def dump_addon_messages(module_name, do_checks, settings): del msgs[key] if check_ctxt: - check_ctxt = _diff_check_ctxt(check_ctxt, minus_check_ctxt) + _diff_check_ctxt(check_ctxt, minus_check_ctxt) # and we are done with those! del minus_pot @@ -924,18 +925,18 @@ def main(): return import sys - back_argv = sys.argv + import argparse + # Get rid of Blender args! - sys.argv = sys.argv[sys.argv.index("--") + 1:] + argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [] - import argparse parser = argparse.ArgumentParser(description="Process UI messages from inside Blender.") parser.add_argument('-c', '--no_checks', default=True, action="store_false", help="No checks over UI messages.") parser.add_argument('-m', '--no_messages', default=True, action="store_false", help="No export of UI messages.") parser.add_argument('-o', '--output', default=None, help="Output POT file path.") parser.add_argument('-s', '--settings', default=None, help="Override (some) default settings. Either a JSon file name, or a JSon string.") - args = parser.parse_args() + args = parser.parse_args(argv) settings = settings_i18n.I18nSettings() settings.from_json(args.settings) @@ -945,8 +946,6 @@ def main(): dump_messages(do_messages=args.no_messages, do_checks=args.no_checks, settings=settings) - sys.argv = back_argv - if __name__ == "__main__": print("\n\n *** Running {} *** \n".format(__file__)) diff --git a/release/scripts/modules/bl_i18n_utils/settings.py b/release/scripts/modules/bl_i18n_utils/settings.py index 920a56a628b..30ee3cdc7d8 100644 --- a/release/scripts/modules/bl_i18n_utils/settings.py +++ b/release/scripts/modules/bl_i18n_utils/settings.py @@ -88,6 +88,7 @@ LANGUAGES = ( (38, "Uzbek (Oʻzbek)", "uz_UZ"), (39, "Uzbek Cyrillic (Ўзбек)", "uz_UZ@cyrillic"), (40, "Hindi (मानक हिन्दी)", "hi_IN"), + (41, "Vietnamese (tiếng Việt)", "vi_VN"), ) # Default context, in py! @@ -324,6 +325,7 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "y", # Sub-strings. "available with", + "brown fox", "can't save image while rendering", "expected a timeline/animation area to be active", "expected a view3d region", @@ -332,6 +334,8 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "image file not found", "image path can't be written to", "in memory to enable editing!", + "jumps over", + "the lazy dog", "unable to load movie clip", "unable to load text", "unable to open the file", diff --git a/release/scripts/modules/bl_i18n_utils/utils.py b/release/scripts/modules/bl_i18n_utils/utils.py index 524fef909e8..d472621029e 100644 --- a/release/scripts/modules/bl_i18n_utils/utils.py +++ b/release/scripts/modules/bl_i18n_utils/utils.py @@ -162,7 +162,7 @@ def get_po_files_from_dir(root_dir, langs=set()): yield uid, po_file -def enable_addons(addons={}, support={}, disable=False, check_only=False): +def enable_addons(addons=None, support=None, disable=False, check_only=False): """ Enable (or disable) addons based either on a set of names, or a set of 'support' types. Returns the list of all affected addons (as fake modules)! @@ -170,6 +170,11 @@ def enable_addons(addons={}, support={}, disable=False, check_only=False): """ import addon_utils + if addons is None: + addons = {} + if support is None: + support = {} + userpref = bpy.context.user_preferences used_ext = {ext.module for ext in userpref.addons} @@ -212,13 +217,13 @@ class I18nMessage: __slots__ = ("msgctxt_lines", "msgid_lines", "msgstr_lines", "comment_lines", "is_fuzzy", "is_commented", "settings") - def __init__(self, msgctxt_lines=[], msgid_lines=[], msgstr_lines=[], comment_lines=[], + def __init__(self, msgctxt_lines=None, msgid_lines=None, msgstr_lines=None, comment_lines=None, is_commented=False, is_fuzzy=False, settings=settings): self.settings = settings - self.msgctxt_lines = msgctxt_lines - self.msgid_lines = msgid_lines - self.msgstr_lines = msgstr_lines - self.comment_lines = comment_lines + self.msgctxt_lines = msgctxt_lines or [] + self.msgid_lines = msgid_lines or [] + self.msgstr_lines = msgstr_lines or [] + self.comment_lines = comment_lines or [] self.is_fuzzy = is_fuzzy self.is_commented = is_commented diff --git a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py index 4126dde1225..669794799e6 100644 --- a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py +++ b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py @@ -125,6 +125,7 @@ class SpellChecker: "multisampling", "multitexture", "multiuser", + "multiview", "namespace", "keyconfig", "online", @@ -576,6 +577,8 @@ class SpellChecker: "btx", "cineon", "dpx", + "dwaa", + "dwab", "dxf", "eps", "exr", diff --git a/release/scripts/modules/bpy/path.py b/release/scripts/modules/bpy/path.py index 25a6c7242e0..be38fefbd2a 100644 --- a/release/scripts/modules/bpy/path.py +++ b/release/scripts/modules/bpy/path.py @@ -116,7 +116,11 @@ def is_subdir(path, directory): from os.path import normpath, normcase path = normpath(normcase(path)) directory = normpath(normcase(directory)) - return path.startswith(directory) + if len(path) > len(directory): + if path.startswith(directory): + sep = ord(_os.sep) if isinstance(directory, bytes) else _os.sep + return (path[len(directory)] == sep) + return False def clean_name(name, replace="_"): @@ -222,7 +226,8 @@ def resolve_ncase(path): if _os.path.isdir(dirpath): try: files = _os.listdir(dirpath) - except PermissionError: # We might not have the permission to list dirpath... + except PermissionError: + # We might not have the permission to list dirpath... return path, False else: return path, False diff --git a/release/scripts/modules/bpy/utils.py b/release/scripts/modules/bpy/utils/__init__.py index 5f235ae3958..348deb2c063 100644 --- a/release/scripts/modules/bpy/utils.py +++ b/release/scripts/modules/bpy/utils/__init__.py @@ -38,6 +38,7 @@ __all__ = ( "unregister_manual_map", "make_rna_paths", "manual_map", + "previews", "resource_path", "script_path_user", "script_path_pref", @@ -640,10 +641,10 @@ def unregister_module(module, verbose=False): # we start with the built-in default mapping def _blender_default_map(): import sys - import rna_wiki_reference as ref_mod + import rna_manual_reference as ref_mod ret = (ref_mod.url_manual_prefix, ref_mod.url_manual_mapping) # avoid storing in memory - del sys.modules["rna_wiki_reference"] + del sys.modules["rna_manual_reference"] return ret # hooks for doc lookups diff --git a/release/scripts/modules/bpy/utils/previews.py b/release/scripts/modules/bpy/utils/previews.py new file mode 100644 index 00000000000..965971139e4 --- /dev/null +++ b/release/scripts/modules/bpy/utils/previews.py @@ -0,0 +1,153 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +""" +This module contains utility functions to handle custom previews. + +It behaves as a high-level 'cached' previews manager. + +This allows scripts to generate their own previews, and use them as icons in UI widgets +('icon_value' for UILayout functions). + + +Custom Icon Example +------------------- + +.. literalinclude:: ../../../release/scripts/templates_py/ui_previews_custom_icon.py +""" + +__all__ = ( + "new", + "remove", + "ImagePreviewCollection", + ) + +import _bpy +_utils_previews = _bpy._utils_previews +del _bpy + + +_uuid_open = set() + + +# High-level previews manager. +# not accessed directly +class ImagePreviewCollection(dict): + """ + Dictionary-like class of previews. + + This is a subclass of Python's built-in dict type, + used to store multiple image previews. + + .. note:: + + - instance with :mod:`bpy.utils.previews.new` + - keys must be ``str`` type. + - values will be :class:`bpy.types.ImagePreview` + """ + + # Internal notes: + # - Blender's internal 'PreviewImage' struct uses 'self._uuid' prefix. + + def __init__(self): + super().__init__() + self._uuid = hex(id(self)) + _uuid_open.add(self._uuid) + + def __del__(self): + if self._uuid not in _uuid_open: + return + + raise ResourceWarning( + "<%s id=%s[%d]>: left open, remove with " + "'bpy.utils.previews.remove()'" % + (self.__class__.__name__, self._uuid, len(self))) + self.close() + + def _gen_key(self, name): + return ":".join((self._uuid, name)) + + def new(self, name): + if name in self: + raise KeyException("key %r already exists") + p = self[name] = _utils_previews.new( + self._gen_key(name)) + return p + new.__doc__ = _utils_previews.new.__doc__ + + def load(self, name, path, path_type, force_reload=False): + if name in self: + raise KeyException("key %r already exists") + p = self[name] = _utils_previews.load( + self._gen_key(name), path, path_type, force_reload) + return p + load.__doc__ = _utils_previews.load.__doc__ + + def clear(self): + """Clear all previews.""" + for name in self.keys(): + _utils_previews.release(self._gen_key(name)) + super().clear() + + def close(self): + """Close the collection and clear all previews.""" + self.clear() + _uuid_open.remove(self._uuid) + + def __delitem__(self, key): + _utils_previews.release(self._gen_key(key)) + super().__delitem__(key) + + def __repr__(self): + return "<%s id=%s[%d], %s>" % ( + self.__class__.__name__, + self._uuid, + len(self), + super().__repr__()) + + +def new(): + """ + :return: a new preview collection. + :rtype: :class:`ImagePreviewCollection` + """ + + return ImagePreviewCollection() + + +def remove(pcoll): + """ + Remove the specified previews collection. + + :arg pcoll: Preview collection to close. + :type pcoll: :class:`ImagePreviewCollection` + """ + pcoll.close() + + +# don't complain about resources on exit (only unregister) +import atexit + + +def exit_clear_warning(): + del ImagePreviewCollection.__del__ + +atexit.register(exit_clear_warning) +del atexit, exit_clear_warning diff --git a/release/scripts/modules/bpy_extras/anim_utils.py b/release/scripts/modules/bpy_extras/anim_utils.py index 4ee5e685668..435169c39b0 100644 --- a/release/scripts/modules/bpy_extras/anim_utils.py +++ b/release/scripts/modules/bpy_extras/anim_utils.py @@ -25,6 +25,304 @@ __all__ = ( import bpy +def frange(start, stop, step=1.0): + while start < stop: + yield start + start += step + + +def ref_curve_eval(curve, frame_start, frame_stop, frame_step, x): + fac = (x - frame_start) / frame_step + idx = int(fac) + fac = abs(fac - idx) + if idx < 0: + return curve[0] + elif idx + 1 >= len(curve): + return curve[-1] + return (1.0 - fac) * curve[idx] + fac * curve[idx + 1] + + +def bezt_optimize(points, threshold, res, steps, org_ref_curve, frame_start, frame_stop, frame_step): + """ + Try to optimize given pair of Bezier segments (triplet of contiguous control points). + """ + # Trying to remove the center point and adjusting relevant handles of each end points. + # If resulting curve gives error below threshold (i.e. average difference between y-values of original + # and simplified curve is small enough), we keep it (i.e. remove its center point). + + from mathutils.geometry import interpolate_bezier + from math import sqrt + + def correct_bezpart(points): + # Same as in C code... + h1 = points[0] - points[1] + h2 = points[3] - points[2] + d = points[3].x - points[0].x + d1 = abs(h1.x) + d2 = abs(h2.x) + + if d != 0.0 and d1 + d2 > d: + fac = d / (d1 + d2) + points[1] = points[0] - h1 * fac + points[2] = points[3] - h2 * fac + + def bez_diff(ref_curve, cur_curve, res): + # start and end values shall be the same! + start_diff = end_diff = 0 + for i, (ref_v, cur_pt) in enumerate(zip(ref_curve[1:-1], cur_curve[1:-1])): + # Note we give much higher importance (quadratic rate) to difference near matching end. + start_fac = (i + 1) / res + end_fac = 1.0 - start_fac + start_diff += (cur_pt.y - ref_v) / (start_fac * start_fac) + end_diff += (cur_pt.y - ref_v) / (end_fac * end_fac) + return start_diff / (res - 2), end_diff / (res - 2) + + correct_bezpart(points) + + start_vec = points[1] - points[0] + end_vec = points[2] - points[3] + + neg_slope = points[1].y < points[0].y if points[1].y != points[0].y else points[2].y < points[0].y if points[2].y != points[0].y else points[3].y < points[0].y + + cur_curve = interpolate_bezier(points[0], points[1], points[2], points[3], res) + ref_curve = [ref_curve_eval(org_ref_curve, frame_start, frame_stop, frame_step, pt.x) for pt in cur_curve] + + start_diff, end_diff = bez_diff(ref_curve, cur_curve, res) + prev_start_diff, prev_end_diff = start_diff, end_diff + do_start = 0 + #~ print(points) + #~ print(start_diff, end_diff) + + f = 1.0 + for i in range(steps): + error = max(abs(start_diff), abs(end_diff)) + if error < threshold: + return error + + prev_points = list(points) + prev_start_vec, prev_end_vec = start_vec.copy(), end_vec.copy() + + if do_start > 0 or (do_start == 0 and abs(start_diff) > abs(end_diff)): + do_start += 1 + if neg_slope: + if start_diff > 0.0: + start_vec /= 1 + start_diff * f + else: + start_vec *= 1 - start_diff * f + else: + if start_diff < 0.0: + start_vec /= 1 - start_diff * f + else: + start_vec *= 1 + start_diff * f + points[1] = points[0] + start_vec + else: + do_start -= 1 + if neg_slope: + if end_diff > 0.0: + end_vec *= 1 + end_diff * f + else: + end_vec /= 1 - end_diff * f + else: + if end_diff < 0.0: + end_vec *= 1 - end_diff * f + else: + end_vec /= 1 + end_diff * f + points[2] = points[3] + end_vec + + correct_bezpart(points) + cur_curve = interpolate_bezier(points[0], points[1], points[2], points[3], res) + ref_curve = [ref_curve_eval(org_ref_curve, frame_start, frame_stop, frame_step, pt.x) for pt in cur_curve] + + start_diff, end_diff = bez_diff(ref_curve, cur_curve, res) + #~ print(points) + #~ print(start_diff, end_diff, f, do_start, neg_slope) + + if ((do_start > 0 and abs(start_diff) > abs(prev_start_diff)) or + (do_start < 0 and abs(end_diff) > abs(prev_end_diff))): + #~ print("WRONG!!!", (start_diff, prev_start_diff) if do_start > 0 else (end_diff, prev_end_diff)) + points[:] = prev_points + start_diff, end_diff = prev_start_diff, prev_end_diff + start_vec, end_vec = prev_start_vec, prev_end_vec + do_start *= -1 + if not (do_start % 2): + f /= 2 + else: + do_start = 0 + prev_start_diff, prev_end_diff = start_diff, end_diff + + return max(abs(start_diff), abs(end_diff)) + + +def simplify_fcurve(fcurve, frame_start, frame_stop, threshold): + """ + This function simplifies given fcurve, removing some existing control points and adjusting the others' handles. + Note that it does not remove non-aligned (or auto) points, nor any using something else than Bezier interpolation. + + :arg frame_start: First frame to simplify. + :type frame_start: int + :arg frame_stop: Last frame to simplify (excluded). + :type frame_stop: int + :arg threshold: Precision of simplification + (the smaller the more precise, never zero, typically 0.1 gives best results). + :type threshold: float + + :return: The number of deleted keyframes. + :rtype: int + """ + + # * We make several passes on the curve, removing each time at most (n - 1) / 2 of its control points. + # * End points are never removed. + # * Points which do not have aligned handles are never removed, neither are points using non-Bezier interpolation. + # * Each set of contiguous, aligned/auto points define a single curve segment. + # * At each pass, for each segment, we check a set of triplets, and try to optimize it. + + SIMPLIFIED_TYPES_AUTO = {'AUTO', 'AUTO_CLAMPED'} + SIMPLIFIED_TYPES = {'ALIGNED'} | SIMPLIFIED_TYPES_AUTO + SIMPLIFIED_INTERPOLATION = {'BEZIER'} + + frame_step = max(0.001, threshold / 10.0) + res = min(1000, int(1 / threshold * 10)) + steps = min(100, int(1 / threshold * 5)) + + ref_curve = [fcurve.evaluate(x) for x in frange(frame_start, frame_stop, frame_step)] + + curves = [[[], False]] + for pt in fcurve.keyframe_points: + if pt.co.x < frame_start: + continue + if pt.co.x >= frame_stop: + break + if pt.interpolation not in SIMPLIFIED_INTERPOLATION: + # 'Break' point. + if len(curves[-1][0]) > 2: + curves.append([[], False]) + else: # Current curve segment is too short to be simplifiable, simply ignore it! + curves[-1][0][:] = [] + #~ print("breaking") + continue + if pt.handle_left_type not in SIMPLIFIED_TYPES or pt.handle_right_type not in SIMPLIFIED_TYPES: + # 'Break' point. + if len(curves[-1][0]) > 1: + curves[-1][0].append([[pt.handle_left, pt.co, pt.handle_right], False, pt]) + curves.append([[], False]) + else: # Current curve segment is too short to be simplifiable, simply ignore it! + curves[-1][0][:] = [] + #~ print("breaking") + curves[-1][0].append([[pt.handle_left, pt.co, pt.handle_right], False, pt]) + + if not curves[-1][0]: + del curves[-1] # Cleanup. + + if not curves: + return 0 + + del_keyframes = [] + step_simplified = True + while step_simplified: + step_simplified = False + for crv in curves: + if crv[1]: + continue # that whole segment of curve is considered impossible to simplify further. + + curve = crv[0] + curve_len = len(curve) + new_curve1 = curve[0:1] + del_keyframes1 = [] + simplified1 = 0 + tot_error1 = 0.0 + if curve_len <= 2: + continue + + for i in range(0, curve_len - 2, 2): + if curve[i + 1][1]: + # Center knot of this triplet is locked (marked as not removable), skip. + new_curve1 += curve[i + 1:i + 3] + points = [curve[i][0][1].copy(), curve[i][0][2].copy(), curve[i + 2][0][0].copy(), curve[i + 2][0][1].copy()] + error = bezt_optimize(points, threshold, res, steps, ref_curve, frame_start, frame_stop, frame_step) + #~ print(error) + if (error < threshold): + del_keyframes1.append(curve[i + 1][2]) + tot_error1 += error + # Center points of knots do not change - ever! + new_curve1[-1][0][2] = points[1] + new_curve1.append(curve[i + 2]) + new_curve1[-1][0][0] = points[2] + simplified1 += 1 + else: + new_curve1 += curve[i + 1:i + 3] # Mere copy of org curve... + #~ new_curve1[-2][1] = True # Lock that center knot from now on. + step_simplified = step_simplified or (simplified1 > 0) + + if curve_len > 3: + # If we have four or more control points, we also have to check the other possible set of triplets... + new_curve2 = curve[0:1] + del_keyframes2 = [] + simplified2 = 0 + tot_error2 = 0.0 + + for i in range(1, curve_len - 2, 2): + if curve[i + 1][1]: + # Center knot of this triplet is locked (marked as not removable), skip. + new_curve2 += curve[i + 1:i + 3] + points = [curve[i][0][1].copy(), curve[i][0][2].copy(), curve[i + 2][0][0].copy(), curve[i + 2][0][1].copy()] + error = bezt_optimize(points, threshold, res, steps, ref_curve, frame_start, frame_stop, frame_step) + #~ print(error) + if (error < threshold): + del_keyframes2.append(curve[i + 1][2]) + tot_error2 += error + # Center points of knots do not change - ever! + new_curve2[-1][0][2] = points[1] + new_curve2.append(curve[i + 2]) + new_curve2[-1][0][0] = points[2] + simplified2 += 1 + else: + new_curve2 += curve[i + 1:i + 3] # Mere copy of org curve... + #~ new_curve2[-2][1] = True # Lock that center knot from now on. + + if (simplified2 > simplified1) or (simplified2 and ((tot_error2 < tot_error1) or not simplified1)): + new_curve1 = new_curve2 + del_keyframes1 = del_keyframes2 + step_simplified = step_simplified or (simplified2 > 0) + + if (len(new_curve1) < curve_len): + curve[:] = new_curve1 + del_keyframes += del_keyframes1 + else: + crv[1] = True # That segment of curve cannot be simplified further. + + ret = len(del_keyframes) + if not del_keyframes: + return ret + + # Now! Update our fcurve. + + # 'Flatten' our curve segments into a single curve again. + curve = [] + for c, _ in curves: + if len(c) >= 2: + if curve and curve[-1][2] == c[0][2]: + curve[-1][0][2] = c[0][2] + curve += c[1:] + else: + curve += c + + # Update handles of kept, modified keyframes. + for bezt, _, pt in c: + # Tag 'auto' handles as 'aligned'. + if pt.handle_left_type in SIMPLIFIED_TYPES_AUTO: + pt.handle_left_type = 'ALIGNED' + if pt.handle_right_type in SIMPLIFIED_TYPES_AUTO: + pt.handle_right_type = 'ALIGNED' + pt.handle_left, pt.co, pt.handle_right = bezt + + # Remove deleted keyframes - WARNING must be the last thing done! Otherwise, other points become invalid... + for pt in sorted(del_keyframes, key=lambda pt: pt.co.x, reverse=True): + fcurve.keyframe_points.remove(pt, fast=True) + + fcurve.update() + return ret + + # XXX visual keying is actually always considered as True in this code... def bake_action(frame_start, frame_end, @@ -37,6 +335,7 @@ def bake_action(frame_start, do_parents_clear=False, do_clean=False, action=None, + clean_threshold=0.0, ): """ @@ -66,6 +365,8 @@ def bake_action(frame_start, :arg action: An action to bake the data into, or None for a new action to be created. :type action: :class:`bpy.types.Action` or None + :arg clean_threshold: How much approximation do we accept while simplifying fcurves. + :type clean_threshold: float :return: an action or None :rtype: :class:`bpy.types.Action` @@ -241,6 +542,8 @@ def bake_action(frame_start, keyframe_points.remove(keyframe_points[i]) else: i += 1 + if clean_threshold != 0.0: + simplify_fcurve(fcu, keyframe_points[0].co.x, keyframe_points[-1].co.x + 1, clean_threshold) scene.frame_set(frame_back) diff --git a/release/scripts/modules/bpy_extras/io_utils.py b/release/scripts/modules/bpy_extras/io_utils.py index 81de0d7c6f0..65ccc3f8dc3 100644 --- a/release/scripts/modules/bpy_extras/io_utils.py +++ b/release/scripts/modules/bpy_extras/io_utils.py @@ -21,7 +21,7 @@ __all__ = ( "ExportHelper", "ImportHelper", - "OrientationHelper", + "orientation_helper_factory", "axis_conversion", "axis_conversion_ensure", "create_derived_objects", @@ -35,7 +35,11 @@ __all__ = ( ) import bpy -from bpy.props import StringProperty, BoolProperty, EnumProperty +from bpy.props import ( + StringProperty, + BoolProperty, + EnumProperty, + ) def _check_axis_conversion(op): @@ -61,6 +65,12 @@ class ExportHelper: options={'HIDDEN'}, ) + # needed for mix-ins + order = [ + "filepath", + "check_existing", + ] + # subclasses can override with decorator # True == use ext, False == no ext, None == do nothing. check_extension = True @@ -109,6 +119,11 @@ class ImportHelper: subtype='FILE_PATH', ) + # needed for mix-ins + order = [ + "filepath", + ] + def invoke(self, context, event): context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'} @@ -117,13 +132,14 @@ class ImportHelper: return _check_axis_conversion(self) -class OrientationHelper: +def orientation_helper_factory(name, axis_forward='Y', axis_up='Z'): + members = {} def _update_axis_forward(self, context): if self.axis_forward[-1] == self.axis_up[-1]: self.axis_up = self.axis_up[0:-1] + 'XYZ'[('XYZ'.index(self.axis_up[-1]) + 1) % 3] - axis_forward = EnumProperty( + members['axis_forward'] = EnumProperty( name="Forward", items=(('X', "X Forward", ""), ('Y', "Y Forward", ""), @@ -132,7 +148,7 @@ class OrientationHelper: ('-Y', "-Y Forward", ""), ('-Z', "-Z Forward", ""), ), - default='-Z', + default=axis_forward, update=_update_axis_forward, ) @@ -140,7 +156,7 @@ class OrientationHelper: if self.axis_up[-1] == self.axis_forward[-1]: self.axis_forward = self.axis_forward[0:-1] + 'XYZ'[('XYZ'.index(self.axis_forward[-1]) + 1) % 3] - axis_up = EnumProperty( + members['axis_up'] = EnumProperty( name="Up", items=(('X', "X Up", ""), ('Y', "Y Up", ""), @@ -149,10 +165,17 @@ class OrientationHelper: ('-Y', "-Y Up", ""), ('-Z', "-Z Up", ""), ), - default='Y', + default=axis_up, update=_update_axis_up, ) + members["order"] = [ + "axis_forward", + "axis_up", + ] + + return type(name, (object,), members) + # Axis conversion function, not pretty LUT # use lookup table to convert between any axis @@ -349,7 +372,7 @@ def unpack_list(list_of_tuples): # same as above except that it adds 0 for triangle faces def unpack_face_list(list_of_tuples): - #allocate the entire list + # allocate the entire list flat_ls = [0] * (len(list_of_tuples) * 4) i = 0 diff --git a/release/scripts/modules/bpy_extras/object_utils.py b/release/scripts/modules/bpy_extras/object_utils.py index 13ef86b23c0..16224144ee4 100644 --- a/release/scripts/modules/bpy_extras/object_utils.py +++ b/release/scripts/modules/bpy_extras/object_utils.py @@ -31,7 +31,10 @@ __all__ = ( import bpy -from bpy.props import BoolProperty, FloatVectorProperty +from bpy.props import ( + BoolProperty, + FloatVectorProperty, + ) def add_object_align_init(context, operator): @@ -171,7 +174,7 @@ def object_data_add(context, obdata, operator=None, use_active_layer=True, name= obj_act.select = True scene.update() # apply location - #scene.objects.active = obj_new + # scene.objects.active = obj_new bpy.ops.object.join() # join into the active. if obdata: @@ -287,7 +290,8 @@ def world_to_camera_view(scene, obj, coord): Returns the camera space coords for a 3d point. (also known as: normalized device coordinates - NDC). - Where (0, 0) is the bottom left and (1, 1) is the top right of the camera frame. + Where (0, 0) is the bottom left and (1, 1) + is the top right of the camera frame. values outside 0-1 are also supported. A negative 'z' value means the point is behind the camera. @@ -300,7 +304,8 @@ def world_to_camera_view(scene, obj, coord): :type obj: :class:`bpy.types.Object` :arg coord: World space location. :type coord: :class:`mathutils.Vector` - :return: a vector where X and Y map to the view plane and Z is the depth on the view axis. + :return: a vector where X and Y map to the view plane and + Z is the depth on the view axis. :rtype: :class:`mathutils.Vector` """ from mathutils import Vector diff --git a/release/scripts/modules/bpy_extras/view3d_utils.py b/release/scripts/modules/bpy_extras/view3d_utils.py index ec4a395f1e1..4aa06262970 100644 --- a/release/scripts/modules/bpy_extras/view3d_utils.py +++ b/release/scripts/modules/bpy_extras/view3d_utils.py @@ -69,11 +69,13 @@ def region_2d_to_origin_3d(region, rv3d, coord, clamp=None): .. note:: - Orthographic views have a less obvious origin, the far clip is used to define the viewport near/far extents. - Since far clip can be a very large value, the result may give with numeric precision issues. + Orthographic views have a less obvious origin, + the far clip is used to define the viewport near/far extents. + Since far clip can be a very large value, + the result may give with numeric precision issues. - To avoid this problem, you can optionally clamp the far clip to a smaller value - based on the data you're operating on. + To avoid this problem, you can optionally clamp the far clip to a + smaller value based on the data you're operating on. :arg region: region of the 3D viewport, typically bpy.context.region. :type region: :class:`bpy.types.Region` @@ -160,7 +162,7 @@ def region_2d_to_location_3d(region, rv3d, coord, depth_location): )[0] -def location_3d_to_region_2d(region, rv3d, coord): +def location_3d_to_region_2d(region, rv3d, coord, default=None): """ Return the *region* relative 2d location of a 3d position. @@ -170,8 +172,10 @@ def location_3d_to_region_2d(region, rv3d, coord): :type rv3d: :class:`bpy.types.RegionView3D` :arg coord: 3d worldspace location. :type coord: 3d vector + :arg default: Return this value if ``coord`` + is behind the origin of a perspective view. :return: 2d location - :rtype: :class:`mathutils.Vector` + :rtype: :class:`mathutils.Vector` or ``default`` argument. """ from mathutils import Vector @@ -184,4 +188,4 @@ def location_3d_to_region_2d(region, rv3d, coord): height_half + height_half * (prj.y / prj.w), )) else: - return None + return default diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py index c7ec7e1e54a..92dbd2dbd0e 100644 --- a/release/scripts/modules/bpy_types.py +++ b/release/scripts/modules/bpy_types.py @@ -715,7 +715,7 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta): __slots__ = () def path_menu(self, searchpaths, operator, - props_default={}, filter_ext=None): + props_default=None, filter_ext=None): layout = self.layout # hard coded to set the operators 'filepath' to the filename. @@ -745,8 +745,9 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta): text=bpy.path.display_name(f), translate=False) - for attr, value in props_default.items(): - setattr(props, attr, value) + if props_default is not None: + for attr, value in props_default.items(): + setattr(props, attr, value) props.filepath = filepath if operator == "script.execute_preset": @@ -754,14 +755,21 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta): def draw_preset(self, context): """ - Define these on the subclass - - preset_operator - - preset_subdir + Define these on the subclass: + - preset_operator (string) + - preset_subdir (string) + + Optionally: + - preset_extensions (set of strings) + - preset_operator_defaults (dict of keyword args) """ import bpy + ext_valid = getattr(self, "preset_extensions", {".py", ".xml"}) + props_default = getattr(self, "preset_operator_defaults", None) self.path_menu(bpy.utils.preset_paths(self.preset_subdir), self.preset_operator, - filter_ext=lambda ext: ext.lower() in {".py", ".xml"}) + props_default=props_default, + filter_ext=lambda ext: ext.lower() in ext_valid) @classmethod def draw_collapsible(cls, context, layout): @@ -773,24 +781,6 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta): layout.menu(cls.__name__, icon='COLLAPSEMENU') -class Region(StructRNA): - __slots__ = () - - def callback_add(self, cb, args, draw_mode): - """ - Append a draw function to this region, - deprecated, instead use bpy.types.SpaceView3D.draw_handler_add - """ - for area in self.id_data.areas: - for region in area.regions: - if region == self: - spacetype = type(area.spaces[0]) - return spacetype.draw_handler_add(cb, args, self.type, - draw_mode) - - return None - - class NodeTree(bpy_types.ID, metaclass=RNAMetaPropGroup): __slots__ = () diff --git a/release/scripts/modules/nodeitems_utils.py b/release/scripts/modules/nodeitems_utils.py index 2882a08fbd4..2f69ea84386 100644 --- a/release/scripts/modules/nodeitems_utils.py +++ b/release/scripts/modules/nodeitems_utils.py @@ -37,13 +37,17 @@ class NodeCategory: else: def items_gen(context): for item in items: - if item.poll is None or item.poll(context): + if item.poll is None or context is None or item.poll(context): yield item self.items = items_gen class NodeItem: - def __init__(self, nodetype, label=None, settings={}, poll=None): + def __init__(self, nodetype, label=None, settings=None, poll=None): + + if settings is None: + settings = {} + self.nodetype = nodetype self._label = label self.settings = settings @@ -136,7 +140,7 @@ def register_node_categories(identifier, cat_list): def node_categories_iter(context): for cat_type in _node_categories.values(): for cat in cat_type[0]: - if cat.poll and cat.poll(context): + if cat.poll and ((context is None) or cat.poll(context)): yield cat diff --git a/release/scripts/modules/rna_info.py b/release/scripts/modules/rna_info.py index 353362ed168..dae262e93d7 100644 --- a/release/scripts/modules/rna_info.py +++ b/release/scripts/modules/rna_info.py @@ -268,7 +268,8 @@ class InfoPropertyRNA: self.default_str = "\"%s\"" % self.default elif self.type == "enum": if self.is_enum_flag: - self.default_str = "%r" % self.default # repr or set() + # self.default_str = "%r" % self.default # repr or set() + self.default_str = "{%s}" % repr(list(sorted(self.default)))[1:-1] else: self.default_str = "'%s'" % self.default elif self.array_length: @@ -695,7 +696,7 @@ def BuildRNAInfo(): return InfoStructRNA.global_lookup, InfoFunctionRNA.global_lookup, InfoOperatorRNA.global_lookup, InfoPropertyRNA.global_lookup -if __name__ == "__main__": +def main(): import rna_info struct = rna_info.BuildRNAInfo()[0] data = [] @@ -723,3 +724,7 @@ if __name__ == "__main__": else: text = bpy.data.texts.new(name="api.py") text.from_string(data) + + +if __name__ == "__main__": + main() diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py index f4649453b46..44722fa7162 100644 --- a/release/scripts/modules/rna_prop_ui.py +++ b/release/scripts/modules/rna_prop_ui.py @@ -32,6 +32,13 @@ def rna_idprop_ui_get(item, create=True): return None +def rna_idprop_ui_del(item): + try: + del item['_RNA_UI'] + except KeyError: + pass + + def rna_idprop_ui_prop_get(item, prop, create=True): rna_ui = rna_idprop_ui_get(item, create) @@ -46,7 +53,7 @@ def rna_idprop_ui_prop_get(item, prop, create=True): return rna_ui[prop] -def rna_idprop_ui_prop_clear(item, prop): +def rna_idprop_ui_prop_clear(item, prop, remove=True): rna_ui = rna_idprop_ui_get(item, False) if rna_ui is None: @@ -54,8 +61,10 @@ def rna_idprop_ui_prop_clear(item, prop): try: del rna_ui[prop] - except: + except KeyError: pass + if remove and len(item.keys()) == 1: + rna_idprop_ui_del(item) def rna_idprop_context_value(context, context_member, property_type): diff --git a/release/scripts/modules/rna_xml.py b/release/scripts/modules/rna_xml.py index 729d6238ac3..ad4809efbe1 100644 --- a/release/scripts/modules/rna_xml.py +++ b/release/scripts/modules/rna_xml.py @@ -32,7 +32,7 @@ def build_property_typemap(skip_classes, skip_typemap): if issubclass(cls, skip_classes): continue - ## to support skip-save we cant get all props + # # to support skip-save we cant get all props # properties = cls.bl_rna.properties.keys() properties = [] for prop_id, prop in cls.bl_rna.properties.items(): |