Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'release/scripts/modules')
-rw-r--r--release/scripts/modules/addon_utils.py95
-rw-r--r--release/scripts/modules/bl_i18n_utils/bl_extract_messages.py19
-rw-r--r--release/scripts/modules/bl_i18n_utils/settings.py4
-rw-r--r--release/scripts/modules/bl_i18n_utils/utils.py17
-rw-r--r--release/scripts/modules/bl_i18n_utils/utils_spell_check.py3
-rw-r--r--release/scripts/modules/bpy/path.py9
-rw-r--r--release/scripts/modules/bpy/utils/__init__.py (renamed from release/scripts/modules/bpy/utils.py)5
-rw-r--r--release/scripts/modules/bpy/utils/previews.py153
-rw-r--r--release/scripts/modules/bpy_extras/anim_utils.py303
-rw-r--r--release/scripts/modules/bpy_extras/io_utils.py39
-rw-r--r--release/scripts/modules/bpy_extras/object_utils.py13
-rw-r--r--release/scripts/modules/bpy_extras/view3d_utils.py18
-rw-r--r--release/scripts/modules/bpy_types.py40
-rw-r--r--release/scripts/modules/nodeitems_utils.py10
-rw-r--r--release/scripts/modules/rna_info.py9
-rw-r--r--release/scripts/modules/rna_prop_ui.py13
-rw-r--r--release/scripts/modules/rna_xml.py2
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():