diff --git a/mcblend/operator_func/__init__.py b/mcblend/operator_func/__init__.py index 3220776..b3fd412 100644 --- a/mcblend/operator_func/__init__.py +++ b/mcblend/operator_func/__init__.py @@ -298,7 +298,7 @@ def import_model(data: Dict, geometry_name: str, context: Context) -> List[str]: model_loader = ModelLoader(data, geometry_name) geometry = ImportGeometry(model_loader) armature = geometry.build_with_armature(context) - model_properties = armature.mcblend + model_properties = get_mcblend(armature) model_properties.texture_width = geometry.texture_width model_properties.texture_height = geometry.texture_height @@ -551,7 +551,7 @@ def import_model_form_project( armature = geometry.build_with_armature(context) # 2.1. Set proper textures resolution and model bounds - model_properties = armature.mcblend + model_properties = get_mcblend(armature) model_properties = cast( MCBLEND_ObjectProperties, model_properties) @@ -571,7 +571,7 @@ def import_model_form_project( # 2.2. Save render controller properties in the armature for rc_stack_item in rc_stack: - armature_rc = armature.mcblend.\ + armature_rc = get_mcblend(armature).\ render_controllers.add() if rc_stack_item.texture is not None: armature_rc.texture = rc_stack_item.texture.name @@ -752,6 +752,7 @@ def prepare_physics_simulation(context: Context) -> Dict: if not child.mctype == MCObjType.CUBE: continue new_obj = child.thisobj.copy() + new_obj = cast(Object, new_obj) new_obj.data = child.obj_data.copy() new_obj.animation_data_clear() get_objects(context.collection).link(new_obj) diff --git a/mcblend/operator_func/animation.py b/mcblend/operator_func/animation.py index c4040ee..89a21a1 100644 --- a/mcblend/operator_func/animation.py +++ b/mcblend/operator_func/animation.py @@ -4,9 +4,9 @@ from __future__ import annotations from typing import NamedTuple, Dict, Optional, List, Tuple, Set, cast -import math +import math # pyright: ignore[reportShadowedImports] from dataclasses import dataclass, field -from itertools import tee, islice +from itertools import tee, islice # pyright: ignore[reportShadowedImports] import bpy from bpy.types import Action, Context @@ -15,19 +15,19 @@ import numpy as np from .typed_bpy_access import ( - get_fcurves, get_keyframe_points, get_nla_tracks, get_selected_objects, + get_fcurves, get_keyframe_points, get_nla_tracks, get_strips, get_timeline_markers) from .json_tools import get_vect_json from .common import ( AnimationLoopType, MINECRAFT_SCALE_FACTOR, MCObjType, McblendObjectGroup, - ANIMATION_TIMESTAMP_PRECISION, + ANIMATION_TIMESTAMP_PRECISION, NumpyTable ) def _pick_closest_rotation( - base: np.ndarray, close_to: np.ndarray, - original_rotation: Optional[np.ndarray] = None - ) -> np.ndarray: + base: NumpyTable, close_to: NumpyTable, + original_rotation: Optional[NumpyTable] = None + ) -> NumpyTable: ''' Takes two arrays with euler rotations in degrees. Looks for rotations that result in same orientation ad the base rotation. Picks the vector @@ -37,7 +37,7 @@ def _pick_closest_rotation( bones rotated before the animation. Issue #25 on Github describes the problem in detail. - :base: np.ndarray: the base rotation. Function is looking for different + :base: NumpyTable: the base rotation. Function is looking for different representations of this orientation. :param close_to: target rotation. Function returns the result as close as possible to this vector. @@ -50,9 +50,9 @@ def _pick_closest_rotation( original_rotation = np.array([0.0, 0.0, 0.0]) def _pick_closet_location( - base: np.ndarray, close_to: np.ndarray - ) -> Tuple[float, np.ndarray]: - choice: np.ndarray = base + base: NumpyTable, close_to: NumpyTable + ) -> Tuple[float, NumpyTable]: + choice: NumpyTable = base distance = np.linalg.norm(choice - close_to) for i in range(3): # Adds removes 360 to all 3 axis (picks the best) @@ -175,9 +175,9 @@ def get_action_keyframes(action: Action) -> List[float]: class PoseBone(NamedTuple): '''Properties of a pose of single bone.''' name: str - location: np.ndarray - rotation: np.ndarray - scale: np.ndarray + location: NumpyTable + rotation: NumpyTable + scale: NumpyTable parent_name: Optional[str] = None def relative(self, original: PoseBone) -> PoseBone: diff --git a/mcblend/operator_func/common.py b/mcblend/operator_func/common.py index 13ac39d..992e84a 100644 --- a/mcblend/operator_func/common.py +++ b/mcblend/operator_func/common.py @@ -8,10 +8,11 @@ from enum import Enum from typing import ( Deque, Dict, Iterator, NamedTuple, List, Optional, Tuple, Any, Iterable, - Sequence, cast) + Sequence, cast, TypeAlias, Literal, ClassVar) from collections import deque import numpy as np +import numpy.typing as npt import bpy from bpy.types import MeshUVLoopLayer, Object, MeshPolygon, PoseBone, Context @@ -26,7 +27,9 @@ from .texture_generator import Mask, ColorMask, get_masks_from_side from .exception import ExporterException -MINECRAFT_SCALE_FACTOR = 16 +NumpyTable: TypeAlias = npt.NDArray[np.float64] + +MINECRAFT_SCALE_FACTOR = 16.0 '''The scale convertion from blender to minecraft (16 units == 1 meter).''' ANIMATION_TIMESTAMP_PRECISION = 2 @@ -89,15 +92,22 @@ class McblendObject: :param group: The :class:`McblendObjectGroup` that stores all of the :class:`McblendObject`s being processed with this object. ''' + thisobj_id: ObjectId + thisobj: Object + parentobj_id: ObjectId | None + children_ids: list[ObjectId] + mctype: MCObjType + group: McblendObjectGroup + def __init__( self, thisobj_id: ObjectId, thisobj: Object, - parentobj_id: Optional[ObjectId], children_ids: List[ObjectId], + parentobj_id: ObjectId | None, children_ids: list[ObjectId], mctype: MCObjType, group: McblendObjectGroup): self.thisobj_id = thisobj_id - self.thisobj: Object = thisobj - self.parentobj_id: Optional[ObjectId] = parentobj_id - self.children_ids: List[ObjectId] = children_ids - self.mctype: MCObjType = mctype + self.thisobj = thisobj + self.parentobj_id = parentobj_id + self.children_ids = children_ids + self.mctype = mctype self.group = group @property @@ -132,13 +142,13 @@ def inflate(self, inflate: float): get_mcblend(self.thisobj).inflate = inflate @property - def min_uv_size(self) -> np.ndarray: + def min_uv_size(self) -> NumpyTable: '''The lower UV size limit of this object.''' return np.array( get_mcblend(self.thisobj).min_uv_size) @min_uv_size.setter - def min_uv_size(self, min_uv_size: np.ndarray): + def min_uv_size(self, min_uv_size: NumpyTable): get_mcblend(self.thisobj).min_uv_size = cast( tuple[int, int, int],min_uv_size) @@ -226,7 +236,7 @@ def obj_matrix_world(self) -> Matrix: return this_obj_matrix_world @property - def mcube_size(self) -> np.ndarray: + def mcube_size(self) -> NumpyTable: ''' The cube size in Minecraft format based on the bounding box of the blender object wrapped inside this object. @@ -236,7 +246,7 @@ def mcube_size(self) -> np.ndarray: return (np.array(bound_box[6]) - np.array(bound_box[0]))[[0, 2, 1]] @property - def mccube_position(self) -> np.ndarray: + def mccube_position(self) -> NumpyTable: ''' The cube position in Minecraft format based on the bounding box of the blender object wrapped inside this object. @@ -244,7 +254,7 @@ def mccube_position(self) -> np.ndarray: return np.array(self.obj_bound_box[0])[[0, 2, 1]] @property - def mcpivot(self) -> np.ndarray: + def mcpivot(self) -> NumpyTable: ''' The pivot point of Minecraft object exported using this object. ''' @@ -298,7 +308,7 @@ def get_local_matrix( def get_mcrotation( self, other: Optional[McblendObject] = None - ) -> np.ndarray: + ) -> NumpyTable: ''' Returns the Minecraft rotation of this object optionally in relation to the other :class:`McblendObject`. @@ -324,7 +334,7 @@ def local_rotation( ) else: result_euler = to_euler(self.obj_matrix_world, 'XZY') - result: np.ndarray = np.array(result_euler)[[0, 2, 1]] + result: NumpyTable = np.array(result_euler)[[0, 2, 1]] result = result * np.array([1, -1, 1]) result = result * 180/math.pi # math.degrees() for array return result @@ -450,6 +460,11 @@ def find_lose_parts(self) -> Tuple[int, ...]: aptr.value = min(a, b) return tuple(i.value for i in groups) +FaceName: TypeAlias = Literal[ + 'north', 'east', 'south', 'west', 'up', 'down'] +FacePattern: TypeAlias = Literal[ + '---', '+--', '-+-', '++-', '--+', '+-+', '-++', '+++'] + # TODO - CubePolygonsSolver, CubePolygons and CubePolygon is a messy structure # maybe CubePolygonsSolver should be removed class CubePolygonsSolver: @@ -465,7 +480,7 @@ class CubePolygonsSolver: - solution - a list of assigned positions of vertices (list of names like '+++'). ''' - FACE_PATTERNS = [ + FACE_PATTERNS: ClassVar[list[list[FacePattern]]] = [ ['---', '+--', '+-+', '--+'], # Cube Front (north) ['--+', '-++', '-+-', '---'], # Cube Right (east) ['-++', '+++', '++-', '-+-'], # Cube Back (south) @@ -473,10 +488,13 @@ class CubePolygonsSolver: ['--+', '+-+', '+++', '-++'], # Cube Up (up) ['-+-', '++-', '+--', '---'], # Cube Down (down) ] - FACE_NAMES = ['north', 'east', 'south', 'west', 'up', 'down'] + FACE_NAMES: ClassVar[list[FaceName]] = [ + 'north', 'east', 'south', 'west', 'up', 'down'] # key (side, is_mirrored) : value (names of the vertices) - MC_MAPPING_UV_ORDERS = { + MC_MAPPING_UV_ORDERS: ClassVar[ + dict[tuple[FaceName, bool], tuple[str, str, str, str]] + ] = { ('east', False) :('-+-', '---', '--+', '-++'), ('north', False) :('---', '+--', '+-+', '--+'), ('west', False) :('+--', '++-', '+++', '+-+'), @@ -491,17 +509,22 @@ class CubePolygonsSolver: ('down', True) :('++-', '-+-', '---', '+--'), } + p_options: list[list[str]] + polygons: Iterable[MeshPolygon] + solved: bool + solution: list[str | None] + def __init__( self, p_options: List[List[str]], polygons: Iterable[MeshPolygon]): self.p_options = p_options self.polygons = polygons self.solved = False - self.solution: List[Optional[str]] = [None] * 8 + self.solution = [None] * 8 @staticmethod def _get_vertices_order( - name: str, mirror: bool, + name: FaceName, mirror: bool, bound_box_vertices: List[str | None] ) -> Tuple[int, int, int, int]: '''Gets the order of vertices for given cube polygon''' @@ -547,7 +570,7 @@ def is_valid(self): ''' used_face_patterns = [False]*6 for polygon in self.polygons: - complete_face = [None]*4 + complete_face: list[str | None] = [None]*4 for i, vertex_index in enumerate(polygon.vertices): complete_face[i] = self.solution[vertex_index] if None in complete_face: @@ -690,7 +713,7 @@ class CubePolygon(NamedTuple): order: Tuple[int, int, int, int] def uv_layer_coordinates( - self, uv_layer: MeshUVLoopLayer) -> np.ndarray: + self, uv_layer: MeshUVLoopLayer) -> NumpyTable: ''' Returns 4x2 numpy array with UV coordinates of this cube polygon loops from the uv_layer. The order of the coordinates in the array is @@ -702,7 +725,7 @@ def uv_layer_coordinates( return crds @staticmethod - def validate_rectangle_uv(crds: np.ndarray) -> Tuple[bool, bool, bool]: + def validate_rectangle_uv(crds: NumpyTable) -> Tuple[bool, bool, bool]: ''' Takes an 4x2 array with UV coordinates of 4 points (left bottom, right bottom, right top, left top) and checks if they're mapped to @@ -758,12 +781,13 @@ class McblendObjectGroup: transformation space of the animation. Animating that object is equivalent to animating everything else in opposite way. ''' - def __init__( - self, armature: Object, - world_origin: Optional[Object]): - self.data: Dict[ObjectId, McblendObject] = {} + data: dict[ObjectId, McblendObject] + world_origin: Object | None + + def __init__(self, armature: Object, world_origin: Optional[Object]): + self.data = {} '''the content of the group.''' - self.world_origin: Optional[Object] = world_origin + self.world_origin = world_origin self._load_objects(armature) def get_world_origin_matrix(self): @@ -853,7 +877,7 @@ def _load_objects(self, armature: Object): children_ids=[], mctype=MCObjType.LOCATOR, group=self) self.data[parentobj_id].children_ids.append(obj_id) -def cyclic_equiv(u: List, v: List) -> bool: +def cyclic_equiv(u: list[Any], v: list[Any]) -> bool: ''' Compare cyclic equivalency of two lists. @@ -950,7 +974,7 @@ def fix_cube_rotation(obj: Object): set_matrix_local(obj, matmul_chain( loc_mat, counter_rotation, rot_mat, scl_mat)) -def get_vect_json(arr: Iterable) -> List[float]: +def get_vect_json(arr: Iterable) -> list[float]: ''' Changes the iterable of numbers into basic python list of floats. Values from the original iterable are rounded to the 3rd deimal diff --git a/mcblend/operator_func/importer.py b/mcblend/operator_func/importer.py index 1190bb3..098cd97 100644 --- a/mcblend/operator_func/importer.py +++ b/mcblend/operator_func/importer.py @@ -3,7 +3,7 @@ ''' from __future__ import annotations -import math +import math # pyright: ignore[reportShadowedImports] from typing import ( Dict, List, Optional, Any, Tuple, Set, Union, TYPE_CHECKING, cast) from enum import Enum @@ -79,18 +79,25 @@ class ModelLoader: :param data: The JSON dict with models file. :param geometry_name: Optional - the name of the geometry to load. ''' - def __init__(self, data: Dict, geometry_name: str = ""): + data: dict[str, Any] + warnings: list[str] + format_version: str + parser_version: str + geometry: dict[str, Any] + geometry_path: list[str | int] + + def __init__(self, data: dict[str, Any], geometry_name: str = ""): self.data = data # List of warnings about problems related to loading the model - self.warnings: List[str] = [] + self.warnings = [] self.format_version, self.parser_version = self._load_format_version( data) geometry, geometry_path = self._load_geometry( geometry_name, self.data) - self.description: Dict = self._load_description( + self.description = self._load_description( geometry, geometry_path) - self.bones: List = self._load_bones( + self.bones = self._load_bones( geometry['bones'], geometry_path + ['bones']) def append_warning(self, warning: str, json_path: List[Union[str, int]]): @@ -359,7 +366,7 @@ def _load_description(self, geometry: Any, geometry_path: List) -> Dict: result = { "texture_width" : 64, "texture_height" : 64, - "visible_bounds_offset" : [0, 0, 0], + "visible_bounds_offset" : [0.0, 0.0, 0.0], "visible_bounds_width" : 1, "visible_bounds_height": 1 } @@ -1205,13 +1212,16 @@ class ImportLocator: :param name: Name of the locator. :param position: The position of the locator. ''' - def __init__( - self, name: str, position: Vector3d, rotation: Vector3d): + name: str + position: Vector3d + rotation: Vector3d + blend_empty: Optional[Object] + + def __init__(self, name: str, position: Vector3d, rotation: Vector3d): self.name = name self.position = position self.rotation = rotation - - self.blend_empty: Optional[Object] = None + self.blend_empty = None class ImportCube: @@ -1221,8 +1231,16 @@ class ImportCube: :param data: The part of the Minecraft model JSON dict that represents this cube. ''' - def __init__( - self, data: Dict): + blend_cube: Optional[Object] + uv: Dict + mirror: bool + inflate: bool + origin: Vector3d + pivot: Vector3d + size: Vector3d + rotation: Vector3d + + def __init__(self, data: Dict): ''' Creates ImportCube object created from a dictionary (part of the JSON) file in the model. @@ -1231,18 +1249,18 @@ def __init__( - `data: Dict` - the part of the Minecraft model JSON file that represents the cube. ''' - self.blend_cube: Optional[Object] = None + self.blend_cube = None - self.uv: Dict = data['uv'] - self.mirror: bool = data['mirror'] - self.inflate: bool = data['inflate'] - self.origin: Vector3d = tuple( # type: ignore + self.uv = data['uv'] + self.mirror = data['mirror'] + self.inflate = data['inflate'] + self.origin = tuple( # type: ignore data['origin']) - self.pivot: Vector3d = tuple( # type: ignore + self.pivot = tuple( # type: ignore data['pivot']) - self.size: Vector3d = tuple( # type: ignore + self.size = tuple( # type: ignore data['size']) - self.rotation: Vector3d = tuple( # type: ignore + self.rotation = tuple( # type: ignore data['rotation']) @@ -1253,6 +1271,13 @@ class ImportPolyMesh: :param data: The part of the Minecraft model JSON dict that represents this poly_mesh. ''' + blend_object: Optional[Object] + normalized_uvs: bool + positions: List[Vector3d] + normals: List[Vector3d] + uvs: List[Vector2d] + polys: List[List[Vector3di]] + def __init__( self, data: Dict): ''' @@ -1262,13 +1287,13 @@ def __init__( :param data: The part of the Minecraft model JSON file that represents the poly_mesh. ''' - self.blend_object: Optional[Object] = None + self.blend_object = None + self.normalized_uvs = data['normalized_uvs'] + self.positions = data['positions'] + self.normals = data['normals'] + self.uvs = data['uvs'] + self.polys = data['polys'] - self.normalized_uvs: bool = data['normalized_uvs'] - self.positions: List[Vector3d] = data['positions'] - self.normals: List[Vector3d] = data['normals'] - self.uvs: List[Vector2d] = data['uvs'] - self.polys: List[List[Vector3di]] = data['polys'] class ImportBone: ''' @@ -1277,6 +1302,17 @@ class ImportBone: :param data: The part of the Minecraft model JSON dict that represents the bone. ''' + blend_empty: Object | None + name: str + parent: str + binding: str + cubes: list[ImportCube] + poly_mesh: ImportPolyMesh | None + locators: list[ImportLocator] + pivot: Vector3d + rotation: Vector3d + mirror: bool + def __init__(self, data: Dict): self.blend_empty: Optional[Object] = None @@ -1290,17 +1326,17 @@ def __init__(self, data: Dict): for cube in data['cubes']: import_cubes.append(ImportCube(cube)) - self.name: str = data['name'] - self.parent: str = data['parent'] - self.binding: str = data['binding'] + self.name = data['name'] + self.parent = data['parent'] + self.binding = data['binding'] self.cubes = import_cubes - self.poly_mesh: Optional[ImportPolyMesh] = None + self.poly_mesh = None if data['poly_mesh'] is not None: self.poly_mesh = ImportPolyMesh(data['poly_mesh']) self.locators = locators - self.pivot: Vector3d = tuple( # type: ignore + self.pivot = tuple( # type: ignore data['pivot']) - self.rotation: Vector3d = tuple( # type: ignore + self.rotation = tuple( # type: ignore data['rotation']) self.mirror = data['mirror'] @@ -1311,6 +1347,15 @@ class ImportGeometry: :param loader: Loader object with all of the required model properties. ''' + indentifier: str + texture_width: int + texture_height: int + visible_bounds_offset: Vector3d + visible_bounds_width: float + visible_bounds_height: float + bones: dict[str, ImportBone] + uv_converter: CoordinatesConverter + def __init__(self, loader: ModelLoader): # Set the values self.identifier = loader.description['identifier'] @@ -1321,7 +1366,7 @@ def __init__(self, loader: ModelLoader): self.visible_bounds_width = loader.description['visible_bounds_width'] self.visible_bounds_height = loader.description[ 'visible_bounds_height'] - self.bones: Dict[str, ImportBone] = {} + self.bones = {} self.uv_converter = CoordinatesConverter( np.array([[0, self.texture_width], [0, self.texture_height]]), np.array([[0, 1], [1, 0]]) diff --git a/mcblend/operator_func/material.py b/mcblend/operator_func/material.py index 8f75e92..e350d6d 100644 --- a/mcblend/operator_func/material.py +++ b/mcblend/operator_func/material.py @@ -4,7 +4,6 @@ from typing import Deque, Optional, List, Tuple from collections import deque -import bpy from bpy.types import Image, Material, Node, NodeTree from .typed_bpy_access import ( diff --git a/mcblend/operator_func/model.py b/mcblend/operator_func/model.py index 304ca30..2652375 100644 --- a/mcblend/operator_func/model.py +++ b/mcblend/operator_func/model.py @@ -16,7 +16,7 @@ from .common import ( MINECRAFT_SCALE_FACTOR, McblendObject, McblendObjectGroup, MCObjType, - CubePolygons, CubePolygon, MeshType + CubePolygons, CubePolygon, MeshType, NumpyTable ) from .typed_bpy_access import get_data, get_loop_indices, get_mcblend from .extra_types import Vector2di, Vector3d, Vector3di @@ -109,8 +109,8 @@ class BoneExport: - `model: ModelExport` - a model that contains this bone. - `name: str` - the name of the bone. - `parent: Optional[str]` - the name of a parent of this bone - - `rotation: np.ndarray` - rotation of the bone. - - `pivot: np.ndarray` - pivot of the bone. + - `rotation: NumpyTable` - rotation of the bone. + - `pivot: NumpyTable` - pivot of the bone. - `cubes: List[CubeExport]` - list of cubes to export. - `locators: Dict[str, LocatorExport]` - list of locators to export. (if exists) or None @@ -131,8 +131,8 @@ def __init__(self, bone: McblendObject, model: ModelExport): self.name: str = bone.obj_name self.parent: Optional[str] = ( None if bone.parent is None else bone.parent.obj_name) - self.rotation: np.ndarray = bone.get_mcrotation(bone.parent) - self.pivot: np.ndarray = bone.mcpivot * MINECRAFT_SCALE_FACTOR + self.rotation: NumpyTable = bone.get_mcrotation(bone.parent) + self.pivot: NumpyTable = bone.mcpivot * MINECRAFT_SCALE_FACTOR self.cubes: List[CubeExport] = [] self.poly_mesh: PolyMesh = PolyMesh() self.locators: Dict[str, LocatorExport] = {} @@ -157,7 +157,7 @@ def load(self, thisobj: McblendObject): (self.model.texture_width, self.model.texture_height) ) - def _scale(objprop: McblendObject) -> np.ndarray: + def _scale(objprop: McblendObject) -> NumpyTable: '''Scale of a bone''' _, _, scale = objprop.obj_matrix_world.decompose() # type: ignore scale = cast(Vector, scale) # type: ignore @@ -286,8 +286,8 @@ def json(self) -> Dict: @dataclass class LocatorExport: '''Object that represents a Locator during model export.''' - offset: np.ndarray - rotation: np.ndarray + offset: NumpyTable + rotation: NumpyTable mcblend_obj: McblendObject def json(self): @@ -307,10 +307,10 @@ def json(self): @dataclass class CubeExport: '''Object that represents a cube during model export.''' - size: np.ndarray - pivot: np.ndarray - origin: np.ndarray - rotation: np.ndarray + size: NumpyTable + pivot: NumpyTable + origin: NumpyTable + rotation: NumpyTable inflate: float uv: Any uv_mirror: bool @@ -398,7 +398,7 @@ def __init__(self, texture_size: Vector2di) -> None: def get_uv_export( self, mcobj: McblendObject, - cube_size: np.ndarray) -> Tuple[Any, bool]: + cube_size: NumpyTable) -> Tuple[Any, bool]: ''' Creates uv properties for given McblendObject. @@ -434,7 +434,7 @@ def get_uv_export( def _get_uv( self, uv_layer: MeshUVLoopLayer, - cube_polygon: CubePolygon, name: str) -> np.ndarray: + cube_polygon: CubePolygon, name: str) -> NumpyTable: ''' Get certain UV coordinates identified by a name from a face. @@ -451,7 +451,7 @@ def _get_uv( def _get_standard_cube_uv_export( self, cube_polygons: CubePolygons, - uv_layer: MeshUVLoopLayer, cube_size: np.ndarray + uv_layer: MeshUVLoopLayer, cube_size: NumpyTable ) -> Tuple[Any, bool]: ''' Attempts to return UV and mirror for standard UV mapping. Raises @@ -459,13 +459,13 @@ def _get_standard_cube_uv_export( given input. ''' # Get min and max value of he loop coordinates - loop_crds_list: List[np.ndarray] = [] + loop_crds_list: List[NumpyTable] = [] for loop in get_data(uv_layer): loop_crds_list.append( self.blend_to_mc_converter.convert(np.array(loop.uv)) ) - loop_crds_arr: np.ndarray = np.vstack(loop_crds_list) - min_loop_crds: np.ndarray = loop_crds_arr.min(0) # type: ignore + loop_crds_arr: NumpyTable = np.vstack(loop_crds_list) + min_loop_crds: NumpyTable = loop_crds_arr.min(0) # type: ignore # max_loop_crds = loop_crds_arr.max(0) # Depth width height diff --git a/mcblend/operator_func/sqlite_bedrock_packs b/mcblend/operator_func/sqlite_bedrock_packs index b4ad262..4a13614 160000 --- a/mcblend/operator_func/sqlite_bedrock_packs +++ b/mcblend/operator_func/sqlite_bedrock_packs @@ -1 +1 @@ -Subproject commit b4ad2627531433da45daf42cb3f607ed0aa0a639 +Subproject commit 4a136144351f954c56066039ef2a4092c9165e6e diff --git a/mcblend/operator_func/texture_generator.py b/mcblend/operator_func/texture_generator.py index 7dc123a..7d002a6 100644 --- a/mcblend/operator_func/texture_generator.py +++ b/mcblend/operator_func/texture_generator.py @@ -6,14 +6,18 @@ from __future__ import annotations from itertools import cycle, accumulate -from typing import Tuple, Iterable, NamedTuple, List, Optional, Sequence +from typing import Tuple, Iterable, NamedTuple, List, Optional, Sequence, TYPE_CHECKING from abc import ABC, abstractmethod from enum import Enum + import numpy as np from .extra_types import Vector2d, Vector3d +if TYPE_CHECKING: + from .common import NumpyTable + class UvMaskTypes(Enum): ''' UvMaskTypes are used for selecting one of the avaliable masks types in @@ -56,7 +60,7 @@ def list_mix_mask_modes_as_blender_enum(self, context): class Mask(ABC): '''Abstract class, parent of all Filters.''' @abstractmethod - def apply(self, image: np.ndarray): + def apply(self, image: NumpyTable): ''' Applies the image to the image. @@ -97,7 +101,7 @@ def __init__( self.interpolate = interpolate self.normalize = normalize - def apply(self, image: np.ndarray): + def apply(self, image: NumpyTable): # xp and fp for np.interp if self.interpolate: fp_r = [c.r for c in self.colors] @@ -139,12 +143,12 @@ class MultiplicativeMask(Mask): A mask which can return a matrix which can be multiplied element-wise by the image matrix to get the result of applying the mask. ''' - def apply(self, image: np.ndarray): + def apply(self, image: NumpyTable): mask = self.get_mask(image) image[:,:,:] = image*mask @abstractmethod - def get_mask(self, image: np.ndarray) -> np.ndarray: + def get_mask(self, image: NumpyTable) -> NumpyTable: '''Returns 2D matrix with the filter array.''' @@ -179,7 +183,7 @@ def __init__( self.relative_boundaries = relative_boundaries def get_surface_properties( - self, image: np.ndarray, + self, image: NumpyTable, sort_points=False) -> Tuple[int, int, int, int, int, int]: ''' Uses points passed in the constructor and the image to return the @@ -233,7 +237,7 @@ def __init__( expotent: float=1.0): super().__init__(p1, p2, relative_boundaries=relative_boundaries) self.stripe_strength: List[float] = [] - stripe_width = [] + stripe_width: list[float] = [] for i in stripes: if i.width < 0: raise Exception( @@ -345,7 +349,7 @@ def __init__( self.expotent = expotent self.hard_edge = hard_edge - def get_mask(self, image: np.ndarray): + def get_mask(self, image: NumpyTable): w, h, u1, u2, v1, v2 = self.get_surface_properties(image) # Create basic mask array @@ -423,7 +427,7 @@ def __init__( self.horizontal = horizontal self.relative_boundaries = relative_boundaries - def get_mask(self, image: np.ndarray) -> np.ndarray: + def get_mask(self, image: NumpyTable) -> NumpyTable: w, h, _ = image.shape mask = np.ones((w, h)) @@ -493,7 +497,7 @@ class MixMask(MultiplicativeMask): min, max or median values. ''' def __init__( - self, masks: Iterable[MultiplicativeMask], *, + self, masks: list[MultiplicativeMask], *, strength: Vector2d=(0.0, 1.0), expotent: float=1.0, mode='mean'): self.strength = strength diff --git a/mcblend/operator_func/typed_bpy_access.py b/mcblend/operator_func/typed_bpy_access.py index bed7c52..4435d79 100644 --- a/mcblend/operator_func/typed_bpy_access.py +++ b/mcblend/operator_func/typed_bpy_access.py @@ -375,7 +375,7 @@ def new_collection(name): >>> bpy.data.collections.new(name) ''' - return bpy.data.collections.new(name) + return bpy.data.collections.new(name) # type: ignore def new_material(name: str): ''' @@ -383,7 +383,7 @@ def new_material(name: str): >>> bpy.data.materials.new(name) ''' - return bpy.data.materials.new(name) + return bpy.data.materials.new(name) # type: ignore def set_co(obj, co): ''' diff --git a/mcblend/operator_func/typed_bpy_access.pyi b/mcblend/operator_func/typed_bpy_access.pyi index 422801f..871b0f0 100644 --- a/mcblend/operator_func/typed_bpy_access.pyi +++ b/mcblend/operator_func/typed_bpy_access.pyi @@ -41,7 +41,7 @@ def get_co(obj: MeshVertex) -> Vector: ... def get_constraints(object: Object) -> ObjectConstraints: ... @overload -def get_constraints(pose_bone: Object) -> PoseBoneConstraints: ... +def get_constraints(pose_bone: PoseBone) -> PoseBoneConstraints: ... def get_context_object(context: Context) -> Object: ... diff --git a/mcblend/operator_func/uv.py b/mcblend/operator_func/uv.py index c0a3927..dd5588d 100644 --- a/mcblend/operator_func/uv.py +++ b/mcblend/operator_func/uv.py @@ -19,7 +19,7 @@ from .json_tools import get_vect_json from .common import ( MINECRAFT_SCALE_FACTOR, McblendObject, McblendObjectGroup, CubePolygon, - MeshType) + MeshType, NumpyTable) from .extra_types import Vector2di @@ -36,13 +36,13 @@ class CoordinatesConverter: :param space_b: The space to convert to. ''' - def __init__(self, space_a: np.ndarray, space_b: np.ndarray): + def __init__(self, space_a: NumpyTable, space_b: NumpyTable): self.space_a = np.copy(space_a.T) self.space_b = np.copy(space_b.T) self.scale_a = self.space_a[1] - self.space_a[0] self.scale_b = self.space_b[1] - self.space_b[0] - def convert(self, x: Collection[float]) -> np.ndarray: + def convert(self, x: Collection[float]) -> NumpyTable: ''' Performs a conversion on coordinates passed to the function with x argument (from space_a to space_b). @@ -224,7 +224,7 @@ def apply_suggestion(self, suggestion: Suggestion): suggestion.position[1] - size[1] ) - def paint_texture(self, arr: np.ndarray, resolution: int = 1): + def paint_texture(self, arr: NumpyTable, resolution: int = 1): ''' Paints the UvBox on the texture represented by the numpy array. @@ -315,7 +315,7 @@ def set_blender_uv(self, converter: CoordinatesConverter): (self.uv[0] + self.size[0], self.uv[1])) uv_data[left_up].uv = converter.convert(self.uv) - def paint_texture(self, arr: np.ndarray, resolution: int = 1): + def paint_texture(self, arr: NumpyTable, resolution: int = 1): ''' Paints the UvBox on the texture. @@ -474,7 +474,7 @@ def clear_uv_layers(self): self.thisobj.obj_data.uv_layers[0] ) - def paint_texture(self, arr: np.ndarray, resolution: int = 1): + def paint_texture(self, arr: NumpyTable, resolution: int = 1): self.side1.paint_texture(arr, resolution) self.side2.paint_texture(arr, resolution) self.side3.paint_texture(arr, resolution) @@ -556,7 +556,7 @@ def clear_uv_layers(self): for obj in self._objects: obj.clear_uv_layers() - def paint_texture(self, arr: np.ndarray, resolution: int = 1): + def paint_texture(self, arr: NumpyTable, resolution: int = 1): # They mapped to one place (paint only one) # for obj in self._objects: if len(self._objects) > 0: @@ -664,7 +664,7 @@ def append_for_uv_mapping(self, object_properties: McblendObjectGroup): continue dimensions = ( objprop.mcube_size * - np.array(objprop.obj_matrix_world.decompose()[2].xzy) * # scale + np.array(objprop.obj_matrix_world.to_scale().xzy) * # scale MINECRAFT_SCALE_FACTOR ) if objprop.inflate != 0: