Merge branch 'blender-v4.3-release'

This commit is contained in:
Campbell Barton
2024-11-03 22:04:42 +11:00
5 changed files with 118 additions and 131 deletions

View File

@@ -72,14 +72,12 @@ class ShaderScriptImport(bpy.types.Operator, ImportHelper):
y -= 20.0
return {'FINISHED'}
"""
Use ImportHelper's invoke_popup() to handle the invocation so that this operator's properties
are shown in a popup. This allows the user to configure additional settings on the operator like
the `set_label` property. Consider having a draw() method on the operator in order to layout the
properties in the UI appropriately.
If filepath information is not provided the file select window will be invoked instead.
"""
# Use ImportHelper's invoke_popup() to handle the invocation so that this operator's properties
# are shown in a popup. This allows the user to configure additional settings on the operator like
# the `set_label` property. Consider having a draw() method on the operator in order to layout the
# properties in the UI appropriately.
#
# If filepath information is not provided the file select window will be invoked instead.
def invoke(self, context, event):
return self.invoke_popup(context)

View File

@@ -1377,14 +1377,13 @@ _ext_manifest_filename_toml = "blender_manifest.toml"
def _extension_module_name_decompose(package):
"""
Returns the repository module name and the extensions ID from an extensions module name (``__package__``).
# Returns the repository module name and the extensions ID from an extensions module name (``__package__``).
#
# :arg module_name: The extensions module name.
# :type module_name: str
# :return: (repo_module_name, extension_id)
# :rtype: tuple[str, str]
:arg module_name: The extensions module name.
:type module_name: str
:return: (repo_module_name, extension_id)
:rtype: tuple[str, str]
"""
if not package.startswith(_ext_base_pkg_idname_with_dot):
raise ValueError("The \"package\" does not name an extension")

View File

@@ -308,33 +308,31 @@ class POSE_OT_selection_set_paste(_PoseModeOnlyMixin, Operator):
def _uniqify(name, other_names):
"""
:arg name: The name to make unique.
:type name: str
:arg other_names: The name to make unique.
:type other_names: str
:return: Return a unique name with ``.xxx`` suffix if necessary.
:rtype: str
Example usage:
>>> _uniqify('hey', ['there'])
'hey'
>>> _uniqify('hey', ['hey.001', 'hey.005'])
'hey'
>>> _uniqify('hey', ['hey', 'hey.001', 'hey.005'])
'hey.002'
>>> _uniqify('hey', ['hey', 'hey.005', 'hey.001'])
'hey.002'
>>> _uniqify('hey', ['hey', 'hey.005', 'hey.001', 'hey.left'])
'hey.002'
>>> _uniqify('hey', ['hey', 'hey.001', 'hey.002'])
'hey.003'
It also works with a dict_keys object:
>>> _uniqify('hey', {'hey': 1, 'hey.005': 1, 'hey.001': 1}.keys())
'hey.002'
"""
# :arg name: The name to make unique.
# :type name: str
# :arg other_names: The name to make unique.
# :type other_names: str
# :return: Return a unique name with ``.xxx`` suffix if necessary.
# :rtype: str
#
# Example usage:
#
# >>> _uniqify('hey', ['there'])
# 'hey'
# >>> _uniqify('hey', ['hey.001', 'hey.005'])
# 'hey'
# >>> _uniqify('hey', ['hey', 'hey.001', 'hey.005'])
# 'hey.002'
# >>> _uniqify('hey', ['hey', 'hey.005', 'hey.001'])
# 'hey.002'
# >>> _uniqify('hey', ['hey', 'hey.005', 'hey.001', 'hey.left'])
# 'hey.002'
# >>> _uniqify('hey', ['hey', 'hey.001', 'hey.002'])
# 'hey.003'
#
# It also works with a dict_keys object:
# >>> _uniqify('hey', {'hey': 1, 'hey.005': 1, 'hey.001': 1}.keys())
# 'hey.002'
if name not in other_names:
return name
@@ -356,14 +354,13 @@ def _uniqify(name, other_names):
def _to_json(context):
"""Convert the selected Selection Sets of the current rig to JSON.
Selected Sets are the active_selection_set determined by the UIList
plus any with the is_selected checkbox on.
:return: The selection as JSON data.
:rtype: str
"""
# Convert the selected Selection Sets of the current rig to JSON.
#
# Selected Sets are the active_selection_set determined by the UIList
# plus any with the is_selected checkbox on.
#
# :return: The selection as JSON data.
# :rtype: str
import json
arm = context.object
@@ -379,11 +376,10 @@ def _to_json(context):
def _from_json(context, as_json):
"""Add the selection sets (one or more) from JSON to the current rig.
:arg as_json: The JSON contents to load.
:type as_json: str
"""
# Add the selection sets (one or more) from JSON to the current rig.
#
# :arg as_json: The JSON contents to load.
# :type as_json: str
import json
json_obj = json.loads(as_json)

View File

@@ -53,18 +53,17 @@ ImageSpec = namedtuple(
def find_image_sequences(files):
"""From a group of files, detect image sequences.
# From a group of files, detect image sequences.
#
# This returns a generator of tuples, which contain the filename,
# start frame, and length of the detected sequence
#
# >>> list(find_image_sequences([
# ... "test2-001.jp2", "test2-002.jp2",
# ... "test3-003.jp2", "test3-004.jp2", "test3-005.jp2", "test3-006.jp2",
# ... "blah"]))
# [("blah", 1, 1), ("test2-001.jp2", 1, 2), ("test3-003.jp2", 3, 4)]
This returns a generator of tuples, which contain the filename,
start frame, and length of the detected sequence
>>> list(find_image_sequences([
... "test2-001.jp2", "test2-002.jp2",
... "test3-003.jp2", "test3-004.jp2", "test3-005.jp2", "test3-006.jp2",
... "blah"]))
[("blah", 1, 1), ("test2-001.jp2", 1, 2), ("test3-003.jp2", 3, 4)]
"""
from itertools import count
import re
num_regex = re.compile("[0-9]") # Find a single number.
@@ -123,11 +122,10 @@ def find_image_sequences(files):
def load_images(filenames, directory, force_reload=False, frame_start=1, find_sequences=False):
"""Wrapper for `bpy_extras.image_utils.load_image`.
# Wrapper for `bpy_extras.image_utils.load_image`.
Loads a set of images, movies, or even image sequences
Returns a generator of ImageSpec wrapper objects later used for texture setup
"""
# Loads a set of images, movies, or even image sequences
# Returns a generator of ImageSpec wrapper objects later used for texture setup
import os
from itertools import repeat
from bpy_extras.image_utils import load_image
@@ -163,14 +161,13 @@ def load_images(filenames, directory, force_reload=False, frame_start=1, find_se
# Position & Size Helpers
def offset_planes(planes, gap, axis):
"""Offset planes from each other by `gap` amount along a _local_ vector `axis`
For example, offset_planes([obj1, obj2], 0.5, Vector(0, 0, 1)) will place
obj2 0.5 blender units away from obj1 along the local positive Z axis.
This is in local space, not world space, so all planes should share
a common scale and rotation.
"""
# Offset planes from each other by `gap` amount along a _local_ vector `axis`
#
# For example, offset_planes([obj1, obj2], 0.5, Vector(0, 0, 1)) will place
# obj2 0.5 blender units away from obj1 along the local positive Z axis.
#
# This is in local space, not world space, so all planes should share
# a common scale and rotation.
prior = planes[0]
offset = Vector()
for current in planes[1:]:
@@ -183,7 +180,7 @@ def offset_planes(planes, gap, axis):
def compute_camera_size(context, center, fill_mode, aspect):
"""Determine how large an object needs to be to fit or fill the camera's field of view."""
# Determine how large an object needs to be to fit or fill the camera's field of view.
scene = context.scene
camera = scene.camera
view_frame = camera.data.view_frame(scene=scene)
@@ -218,7 +215,7 @@ def compute_camera_size(context, center, fill_mode, aspect):
def center_in_camera(camera, ob, axis=(1, 1)):
"""Center object along specified axis of the camera"""
# Center object along specified axis of the camera.
camera_matrix_col = camera.matrix_world.col
location = ob.location
@@ -483,7 +480,8 @@ def create_cycles_material(self, context, img_spec, name):
def get_input_nodes(node, links):
"""Get nodes that are a inputs to the given node"""
# Get nodes that are a inputs to the given node.
# Get all links going to node.
input_links = {lnk for lnk in links if lnk.to_node == node}
# Sort those links, get their input nodes (and avoid doubles!).
@@ -505,7 +503,8 @@ def get_input_nodes(node, links):
def auto_align_nodes(node_tree):
"""Given a shader node tree, arrange nodes neatly relative to the output node."""
# Given a shader node tree, arrange nodes neatly relative to the output node.
x_gap = 200
y_gap = 180
nodes = node_tree.nodes
@@ -532,10 +531,10 @@ def auto_align_nodes(node_tree):
def clean_node_tree(node_tree):
"""Clear all nodes in a shader node tree except the output.
# Clear all nodes in a shader node tree except the output.
#
# Returns the output node
Returns the output node
"""
nodes = node_tree.nodes
for node in list(nodes): # Copy to avoid altering the loop's data source.
if not node.type == 'OUTPUT_MATERIAL':
@@ -545,7 +544,7 @@ def clean_node_tree(node_tree):
def get_shadeless_node(dest_node_tree):
"""Return a "shadeless" cycles/EEVEE node, creating a node group if nonexistent"""
# Return a "shadeless" cycles/EEVEE node, creating a node group if nonexistent.
# WARNING: using a hard coded name isn't fool proof!
# Users could have this name already in a node-tree (albeit unlikely).
@@ -742,7 +741,8 @@ class IMAGE_OT_import_as_mesh_planes(AddObjectHelper, ImportHelper, MaterialProp
# -----------------
# Properties - Size
def update_size_mode(self, _context):
"""If sizing relative to the camera, always face the camera"""
# If sizing relative to the camera, always face the camera.
if self.size_mode == 'CAMERA':
self.prev_align_axis = self.align_axis
self.align_axis = 'CAM'
@@ -996,7 +996,8 @@ class IMAGE_OT_import_as_mesh_planes(AddObjectHelper, ImportHelper, MaterialProp
return plane
def compute_plane_size(self, context, img_spec):
"""Given the image size in pixels and location, determine size of plane"""
# Given the image size in pixels and location, determine size of plane.
px, py = img_spec.size
# Can't load data.
@@ -1026,7 +1027,8 @@ class IMAGE_OT_import_as_mesh_planes(AddObjectHelper, ImportHelper, MaterialProp
return x, y
def align_plane(self, context, plane):
"""Pick an axis and align the plane to it"""
# Pick an axis and align the plane to it.
from math import pi
if 'CAM' in self.align_axis:
# Camera-aligned.

View File

@@ -20,18 +20,16 @@ from bpy.props import (
# Local Utility Functions
def is_face_uv_selected(face, uv_layer, any_edge):
"""
Returns True if the face is UV selected.
:arg face: the face to query.
:type face: :class:`BMFace`
:arg uv_layer: the UV layer to source UVs from.
:type bmesh: :class:`BMLayerItem`
:arg any_edge: use edge selection instead of vertex selection.
:type any_edge: bool
:return: True if the face is UV selected.
:rtype: bool
"""
# Returns True if the face is UV selected.
#
# :arg face: the face to query.
# :type face: :class:`BMFace`
# :arg uv_layer: the UV layer to source UVs from.
# :type bmesh: :class:`BMLayerItem`
# :arg any_edge: use edge selection instead of vertex selection.
# :type any_edge: bool
# :return: True if the face is UV selected.
# :rtype: bool
if not face.select: # Geometry selection
return False
@@ -54,18 +52,16 @@ def is_face_uv_selected(face, uv_layer, any_edge):
def is_island_uv_selected(island, uv_layer, any_edge):
"""
Returns True if the island is UV selected.
:arg island: list of faces to query.
:type island: Sequence[:class:`BMFace`]
:arg uv_layer: the UV layer to source UVs from.
:type bmesh: :class:`BMLayerItem`
:arg any_edge: use edge selection instead of vertex selection.
:type any_edge: bool
:return: list of lists containing polygon indices.
:rtype: bool
"""
# Returns True if the island is UV selected.
#
# :arg island: list of faces to query.
# :type island: Sequence[:class:`BMFace`]
# :arg uv_layer: the UV layer to source UVs from.
# :type bmesh: :class:`BMLayerItem`
# :arg any_edge: use edge selection instead of vertex selection.
# :type any_edge: bool
# :return: list of lists containing polygon indices.
# :rtype: bool
for face in island:
if is_face_uv_selected(face, uv_layer, any_edge):
return True
@@ -73,15 +69,13 @@ def is_island_uv_selected(island, uv_layer, any_edge):
def island_uv_bounds(island, uv_layer):
"""
The UV bounds of UV island.
:arg island: list of faces to query.
:type island: Sequence[:class:`BMFace`]
:arg uv_layer: the UV layer to source UVs from.
:return: U-min, V-min, U-max, V-max.
:rtype: list[float]
"""
# The UV bounds of UV island.
#
# :arg island: list of faces to query.
# :type island: Sequence[:class:`BMFace`]
# :arg uv_layer: the UV layer to source UVs from.
# :return: U-min, V-min, U-max, V-max.
# :rtype: list[float]
minmax = [1e30, 1e30, -1e30, -1e30]
for face in island:
for loop in face.loops:
@@ -94,15 +88,13 @@ def island_uv_bounds(island, uv_layer):
def island_uv_bounds_center(island, uv_layer):
"""
The UV bounds center of UV island.
:arg island: list of faces to query.
:type island: Sequence[:class:`BMFace`]
:arg uv_layer: the UV layer to source UVs from.
:return: U, V center.
:rtype: tuple[float, float]
"""
# The UV bounds center of UV island.
#
# :arg island: list of faces to query.
# :type island: Sequence[:class:`BMFace`]
# :arg uv_layer: the UV layer to source UVs from.
# :return: U, V center.
# :rtype: tuple[float, float]
minmax = island_uv_bounds(island, uv_layer)
return (
(minmax[0] + minmax[2]) / 2.0,