diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..2c07333 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.11 diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..dc3f727 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.analysis.typeCheckingMode": "basic" +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..2919202 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,14 @@ +[project] +name = "solstice" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +authors = [{ name = "Yuki Kitagawa", email = "yuki.kitagawa@pm.me" }] +requires-python = ">=3.11,<3.12" +dependencies = [ + "fake-bpy-module-latest>=20250505", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/src/solstice/__init__.py b/src/solstice/__init__.py index df50e2f..a1e187d 100644 --- a/src/solstice/__init__.py +++ b/src/solstice/__init__.py @@ -1,3 +1,5 @@ +# ruff: noqa: E402 # Allow using imports at middle of the file + import sys bl_info = { @@ -15,7 +17,8 @@ if "solstice.align_island" in sys.modules: if module_name.startswith("solstice."): del sys.modules[module_name] -from .align_island import AlignIsland + +# from .align_island import AlignIsland from .export_constraint import ExportConstraint import bpy @@ -23,7 +26,7 @@ import bpy def uv_menu_func(self, context): self.layout.separator() self.layout.label(text="Solstice Toolbox", icon="PLUGIN") - self.layout.operator(AlignIsland.bl_idname) + # self.layout.operator(AlignIsland.bl_idname) def bone_menu_func(self, context): @@ -33,7 +36,7 @@ def bone_menu_func(self, context): def register(): - bpy.utils.register_class(AlignIsland) + # bpy.utils.register_class(AlignIsland) bpy.utils.register_class(ExportConstraint) bpy.types.IMAGE_MT_uvs.append(uv_menu_func) bpy.types.VIEW3D_MT_object.append(bone_menu_func) @@ -42,7 +45,7 @@ def register(): def unregister(): bpy.types.IMAGE_MT_uvs.remove(uv_menu_func) bpy.types.VIEW3D_MT_object.remove(bone_menu_func) - bpy.utils.unregister_class(AlignIsland) + # bpy.utils.unregister_class(AlignIsland) bpy.utils.unregister_class(ExportConstraint) diff --git a/src/solstice/align_island.py b/src/solstice/align_island.py index 62f7da1..7ea2acd 100644 --- a/src/solstice/align_island.py +++ b/src/solstice/align_island.py @@ -1,3 +1,5 @@ +# This is not working. Don't use now + import math import bpy import bpy.types @@ -12,8 +14,9 @@ def get_uv_editable_objects(context): objs = [] else: objs = [ - o for o in bpy.data.objects - if compat.get_object_select(o) and o.type == 'MESH' + o + for o in bpy.data.objects + if compat.get_object_select(o) and o.type == "MESH" ] ob = context.active_object @@ -38,7 +41,6 @@ class AlignIsland(bpy.types.Operator): objs = get_uv_editable_objects(context) for obj in objs: - bm = bmesh.from_edit_mesh(obj.data) if common.check_version(2, 73, 0) >= 0: bm.faces.ensure_lookup_table() @@ -54,7 +56,9 @@ class AlignIsland(bpy.types.Operator): self.report( {"ERROR"}, "Please select a single edge. Got: {} edge(s).".format( - len(selected_edges))) + len(selected_edges) + ), + ) return {"CANCELLED"} # Get the UV island that the edge belongs to @@ -77,9 +81,12 @@ class AlignIsland(bpy.types.Operator): selected_edge_uv.append(selected_island.uv[vert].uv) # Check the direction of the edge in UV space as degrees - edge_direction = (selected_edge_uv[1] - - selected_edge_uv[0]).to_track_quat( - "X", "Y").to_euler().z + edge_direction = ( + (selected_edge_uv[1] - selected_edge_uv[0]) + .to_track_quat("X", "Y") + .to_euler() + .z + ) # Choose the closest grid direction if edge_direction < 45 or edge_direction > 315: @@ -94,8 +101,7 @@ class AlignIsland(bpy.types.Operator): # Calculate the rotation needed to align the island to the grid direction rotation = grid_direction - edge_direction - self.report({"INFO"}, - f"Edge direction: {math.degrees(edge_direction)}") + self.report({"INFO"}, f"Edge direction: {math.degrees(edge_direction)}") self.report({"INFO"}, f"Grid direction: {grid_direction}") # Select the island diff --git a/src/solstice/export_constraint.py b/src/solstice/export_constraint.py index d19e637..951f983 100644 --- a/src/solstice/export_constraint.py +++ b/src/solstice/export_constraint.py @@ -4,22 +4,26 @@ import bpy def write_constraints_copy_rotation(f, constraint): f.write(" (constraint 'copy_rotation'\n") f.write(" influence: {}\n".format(constraint.influence)) - f.write(" target: {}\n".format(constraint.target.name)) - f.write(" subtarget: {}\n".format(constraint.subtarget)) - f.write(" owner_space: {}\n".format(constraint.owner_space)) - f.write(" target_space: {}\n".format(constraint.target_space)) - f.write(" use_xyz: [{} {} {}]\n".format( - constraint.use_x, - constraint.use_y, - constraint.use_z, - )) - f.write(" invert_xyz: [{} {} {}]\n".format( - constraint.invert_x, - constraint.invert_y, - constraint.invert_z, - )) - f.write(" mix_mode: {}\n".format(constraint.mix_mode)) - f.write(" euler_order: {}\n".format(constraint.euler_order)) + f.write(" target: '{}'\n".format(constraint.target.name)) + f.write(" subtarget: '{}'\n".format(constraint.subtarget)) + f.write(" owner_space: '{}'\n".format(constraint.owner_space)) + f.write(" target_space: '{}'\n".format(constraint.target_space)) + f.write( + " use_xyz: [{} {} {}]\n".format( + constraint.use_x, + constraint.use_y, + constraint.use_z, + ) + ) + f.write( + " invert_xyz: [{} {} {}]\n".format( + constraint.invert_x, + constraint.invert_y, + constraint.invert_z, + ) + ) + f.write(" mix_mode: '{}'\n".format(constraint.mix_mode)) + f.write(" euler_order: '{}'\n".format(constraint.euler_order)) f.write(" )") @@ -27,21 +31,27 @@ def write_constraint_limit_rotation(f, constraint): f.write(" (constraint 'limit_rotation'\n") f.write(" influence: {}\n".format(constraint.influence)) f.write(" owner_space: {}\n".format(constraint.owner_space)) - f.write(" use_limit_xyz: [{} {} {}]\n".format( - constraint.use_limit_x, - constraint.use_limit_y, - constraint.use_limit_z, - )) - f.write(" min_xyz: [{} {} {}]\n".format( - constraint.min_x, - constraint.min_y, - constraint.min_z, - )) - f.write(" max_xyz: [{} {} {}]\n".format( - constraint.max_x, - constraint.max_y, - constraint.max_z, - )) + f.write( + " use_limit_xyz: [{} {} {}]\n".format( + constraint.use_limit_x, + constraint.use_limit_y, + constraint.use_limit_z, + ) + ) + f.write( + " min_xyz: [{} {} {}]\n".format( + constraint.min_x, + constraint.min_y, + constraint.min_z, + ) + ) + f.write( + " max_xyz: [{} {} {}]\n".format( + constraint.max_x, + constraint.max_y, + constraint.max_z, + ) + ) def write_bone_constraints(context, filepath): @@ -49,7 +59,7 @@ def write_bone_constraints(context, filepath): obj = context.active_object # Open the file for writing - with open(filepath, 'w') as file: + with open(filepath, "w") as file: # Iterate over all bones for bone in obj.pose.bones: # Write bone name @@ -57,7 +67,7 @@ def write_bone_constraints(context, filepath): # Iterate over all bone constraints for constraint in bone.constraints: - if not constraint.enabled or constraint.mute or not constraint.active: + if not constraint.enabled: continue # Write constraint type and settings @@ -70,14 +80,19 @@ def write_bone_constraints(context, filepath): else: # Unknown constraint type # Print python class name - file.write("; unknown constraint.\n; class: {}\n".format( - constraint.__class__.__name__)) + file.write( + "; unknown constraint.\n; class: {}\n".format( + constraint.__class__.__name__ + ) + ) file.write("; (constraint '{}'\n".format(constraint.type)) for prop in dir(constraint): - if not prop.startswith('_') and not callable( - getattr(constraint, prop)): - file.write("; {}: {}\n".format( - prop, getattr(constraint, prop))) + if not prop.startswith("_") and not callable( + getattr(constraint, prop) + ): + file.write( + "; {}: {}\n".format(prop, getattr(constraint, prop)) + ) file.write("; )\n") file.write(")\n") @@ -87,13 +102,13 @@ class ExportConstraint(bpy.types.Operator): bl_idname = "solstice.export_bone_constraints" bl_label = "Export Bone Constraints" - filepath: bpy.props.StringProperty() + filepath: bpy.props.StringProperty() # type: ignore def execute(self, context): # Assert the selected file is a bone - if context.active_object.type != 'ARMATURE': - self.report({'ERROR'}, "Active object is not an armature.") - return {'CANCELLED'} + if context.active_object and context.active_object.type != "ARMATURE": + self.report({"ERROR"}, "Active object is not an armature.") + return {"CANCELLED"} # Do something with the selected file path print("Selected file:", self.filepath) @@ -101,9 +116,10 @@ class ExportConstraint(bpy.types.Operator): write_bone_constraints(context, self.filepath) - return {'FINISHED'} + return {"FINISHED"} def invoke(self, context, event): # Open the file browser dialog + assert context.window_manager is not None context.window_manager.fileselect_add(self) - return {'RUNNING_MODAL'} + return {"RUNNING_MODAL"} diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..238fe71 --- /dev/null +++ b/uv.lock @@ -0,0 +1,23 @@ +version = 1 +revision = 1 +requires-python = "==3.11.*" + +[[package]] +name = "fake-bpy-module-latest" +version = "20250505" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/c3/49e21dc7ddf91b4df4720fb6ef1c3a7317dae8fe60360fc7aeae6d6e540b/fake_bpy_module_latest-20250505.tar.gz", hash = "sha256:30e3e0b09cde1b9b94aed772dbc2f56729b4f05b066a5f5bfde7edea4a4df443", size = 961714 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/73/f6eca9916da6bb4cf19aea8af0b8041d184246560f6490d17364b9df175e/fake_bpy_module_latest-20250505-py3-none-any.whl", hash = "sha256:0b633e323c757c3392b7623fe8088fd6e64244254fb79aec3139a4261ad9b26b", size = 1086370 }, +] + +[[package]] +name = "solstice" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "fake-bpy-module-latest" }, +] + +[package.metadata] +requires-dist = [{ name = "fake-bpy-module-latest", specifier = ">=20250505" }]