refactor: Modernize dev experience

This commit is contained in:
Yuki Kitagawa 2025-05-17 21:09:30 +08:00
parent 4a265bbfad
commit 4f9c5acde0
7 changed files with 124 additions and 58 deletions

1
.python-version Normal file
View File

@ -0,0 +1 @@
3.11

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"python.analysis.typeCheckingMode": "basic"
}

14
pyproject.toml Normal file
View File

@ -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"

View File

@ -1,3 +1,5 @@
# ruff: noqa: E402 # Allow using imports at middle of the file
import sys import sys
bl_info = { bl_info = {
@ -15,7 +17,8 @@ if "solstice.align_island" in sys.modules:
if module_name.startswith("solstice."): if module_name.startswith("solstice."):
del sys.modules[module_name] del sys.modules[module_name]
from .align_island import AlignIsland
# from .align_island import AlignIsland
from .export_constraint import ExportConstraint from .export_constraint import ExportConstraint
import bpy import bpy
@ -23,7 +26,7 @@ import bpy
def uv_menu_func(self, context): def uv_menu_func(self, context):
self.layout.separator() self.layout.separator()
self.layout.label(text="Solstice Toolbox", icon="PLUGIN") 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): def bone_menu_func(self, context):
@ -33,7 +36,7 @@ def bone_menu_func(self, context):
def register(): def register():
bpy.utils.register_class(AlignIsland) # bpy.utils.register_class(AlignIsland)
bpy.utils.register_class(ExportConstraint) bpy.utils.register_class(ExportConstraint)
bpy.types.IMAGE_MT_uvs.append(uv_menu_func) bpy.types.IMAGE_MT_uvs.append(uv_menu_func)
bpy.types.VIEW3D_MT_object.append(bone_menu_func) bpy.types.VIEW3D_MT_object.append(bone_menu_func)
@ -42,7 +45,7 @@ def register():
def unregister(): def unregister():
bpy.types.IMAGE_MT_uvs.remove(uv_menu_func) bpy.types.IMAGE_MT_uvs.remove(uv_menu_func)
bpy.types.VIEW3D_MT_object.remove(bone_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) bpy.utils.unregister_class(ExportConstraint)

View File

@ -1,3 +1,5 @@
# This is not working. Don't use now
import math import math
import bpy import bpy
import bpy.types import bpy.types
@ -12,8 +14,9 @@ def get_uv_editable_objects(context):
objs = [] objs = []
else: else:
objs = [ objs = [
o for o in bpy.data.objects o
if compat.get_object_select(o) and o.type == 'MESH' for o in bpy.data.objects
if compat.get_object_select(o) and o.type == "MESH"
] ]
ob = context.active_object ob = context.active_object
@ -38,7 +41,6 @@ class AlignIsland(bpy.types.Operator):
objs = get_uv_editable_objects(context) objs = get_uv_editable_objects(context)
for obj in objs: for obj in objs:
bm = bmesh.from_edit_mesh(obj.data) bm = bmesh.from_edit_mesh(obj.data)
if common.check_version(2, 73, 0) >= 0: if common.check_version(2, 73, 0) >= 0:
bm.faces.ensure_lookup_table() bm.faces.ensure_lookup_table()
@ -54,7 +56,9 @@ class AlignIsland(bpy.types.Operator):
self.report( self.report(
{"ERROR"}, {"ERROR"},
"Please select a single edge. Got: {} edge(s).".format( "Please select a single edge. Got: {} edge(s).".format(
len(selected_edges))) len(selected_edges)
),
)
return {"CANCELLED"} return {"CANCELLED"}
# Get the UV island that the edge belongs to # 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) selected_edge_uv.append(selected_island.uv[vert].uv)
# Check the direction of the edge in UV space as degrees # Check the direction of the edge in UV space as degrees
edge_direction = (selected_edge_uv[1] - edge_direction = (
selected_edge_uv[0]).to_track_quat( (selected_edge_uv[1] - selected_edge_uv[0])
"X", "Y").to_euler().z .to_track_quat("X", "Y")
.to_euler()
.z
)
# Choose the closest grid direction # Choose the closest grid direction
if edge_direction < 45 or edge_direction > 315: 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 # Calculate the rotation needed to align the island to the grid direction
rotation = grid_direction - edge_direction rotation = grid_direction - edge_direction
self.report({"INFO"}, self.report({"INFO"}, f"Edge direction: {math.degrees(edge_direction)}")
f"Edge direction: {math.degrees(edge_direction)}")
self.report({"INFO"}, f"Grid direction: {grid_direction}") self.report({"INFO"}, f"Grid direction: {grid_direction}")
# Select the island # Select the island

View File

@ -4,22 +4,26 @@ import bpy
def write_constraints_copy_rotation(f, constraint): def write_constraints_copy_rotation(f, constraint):
f.write(" (constraint 'copy_rotation'\n") f.write(" (constraint 'copy_rotation'\n")
f.write(" influence: {}\n".format(constraint.influence)) f.write(" influence: {}\n".format(constraint.influence))
f.write(" target: {}\n".format(constraint.target.name)) f.write(" target: '{}'\n".format(constraint.target.name))
f.write(" subtarget: {}\n".format(constraint.subtarget)) f.write(" subtarget: '{}'\n".format(constraint.subtarget))
f.write(" owner_space: {}\n".format(constraint.owner_space)) f.write(" owner_space: '{}'\n".format(constraint.owner_space))
f.write(" target_space: {}\n".format(constraint.target_space)) f.write(" target_space: '{}'\n".format(constraint.target_space))
f.write(" use_xyz: [{} {} {}]\n".format( f.write(
constraint.use_x, " use_xyz: [{} {} {}]\n".format(
constraint.use_y, constraint.use_x,
constraint.use_z, constraint.use_y,
)) constraint.use_z,
f.write(" invert_xyz: [{} {} {}]\n".format( )
constraint.invert_x, )
constraint.invert_y, f.write(
constraint.invert_z, " invert_xyz: [{} {} {}]\n".format(
)) constraint.invert_x,
f.write(" mix_mode: {}\n".format(constraint.mix_mode)) constraint.invert_y,
f.write(" euler_order: {}\n".format(constraint.euler_order)) constraint.invert_z,
)
)
f.write(" mix_mode: '{}'\n".format(constraint.mix_mode))
f.write(" euler_order: '{}'\n".format(constraint.euler_order))
f.write(" )") f.write(" )")
@ -27,21 +31,27 @@ def write_constraint_limit_rotation(f, constraint):
f.write(" (constraint 'limit_rotation'\n") f.write(" (constraint 'limit_rotation'\n")
f.write(" influence: {}\n".format(constraint.influence)) f.write(" influence: {}\n".format(constraint.influence))
f.write(" owner_space: {}\n".format(constraint.owner_space)) f.write(" owner_space: {}\n".format(constraint.owner_space))
f.write(" use_limit_xyz: [{} {} {}]\n".format( f.write(
constraint.use_limit_x, " use_limit_xyz: [{} {} {}]\n".format(
constraint.use_limit_y, constraint.use_limit_x,
constraint.use_limit_z, constraint.use_limit_y,
)) constraint.use_limit_z,
f.write(" min_xyz: [{} {} {}]\n".format( )
constraint.min_x, )
constraint.min_y, f.write(
constraint.min_z, " min_xyz: [{} {} {}]\n".format(
)) constraint.min_x,
f.write(" max_xyz: [{} {} {}]\n".format( constraint.min_y,
constraint.max_x, constraint.min_z,
constraint.max_y, )
constraint.max_z, )
)) f.write(
" max_xyz: [{} {} {}]\n".format(
constraint.max_x,
constraint.max_y,
constraint.max_z,
)
)
def write_bone_constraints(context, filepath): def write_bone_constraints(context, filepath):
@ -49,7 +59,7 @@ def write_bone_constraints(context, filepath):
obj = context.active_object obj = context.active_object
# Open the file for writing # Open the file for writing
with open(filepath, 'w') as file: with open(filepath, "w") as file:
# Iterate over all bones # Iterate over all bones
for bone in obj.pose.bones: for bone in obj.pose.bones:
# Write bone name # Write bone name
@ -57,7 +67,7 @@ def write_bone_constraints(context, filepath):
# Iterate over all bone constraints # Iterate over all bone constraints
for constraint in bone.constraints: for constraint in bone.constraints:
if not constraint.enabled or constraint.mute or not constraint.active: if not constraint.enabled:
continue continue
# Write constraint type and settings # Write constraint type and settings
@ -70,14 +80,19 @@ def write_bone_constraints(context, filepath):
else: else:
# Unknown constraint type # Unknown constraint type
# Print python class name # Print python class name
file.write("; unknown constraint.\n; class: {}\n".format( file.write(
constraint.__class__.__name__)) "; unknown constraint.\n; class: {}\n".format(
constraint.__class__.__name__
)
)
file.write("; (constraint '{}'\n".format(constraint.type)) file.write("; (constraint '{}'\n".format(constraint.type))
for prop in dir(constraint): for prop in dir(constraint):
if not prop.startswith('_') and not callable( if not prop.startswith("_") and not callable(
getattr(constraint, prop)): getattr(constraint, prop)
file.write("; {}: {}\n".format( ):
prop, getattr(constraint, prop))) file.write(
"; {}: {}\n".format(prop, getattr(constraint, prop))
)
file.write("; )\n") file.write("; )\n")
file.write(")\n") file.write(")\n")
@ -87,13 +102,13 @@ class ExportConstraint(bpy.types.Operator):
bl_idname = "solstice.export_bone_constraints" bl_idname = "solstice.export_bone_constraints"
bl_label = "Export Bone Constraints" bl_label = "Export Bone Constraints"
filepath: bpy.props.StringProperty() filepath: bpy.props.StringProperty() # type: ignore
def execute(self, context): def execute(self, context):
# Assert the selected file is a bone # Assert the selected file is a bone
if context.active_object.type != 'ARMATURE': if context.active_object and context.active_object.type != "ARMATURE":
self.report({'ERROR'}, "Active object is not an armature.") self.report({"ERROR"}, "Active object is not an armature.")
return {'CANCELLED'} return {"CANCELLED"}
# Do something with the selected file path # Do something with the selected file path
print("Selected file:", self.filepath) print("Selected file:", self.filepath)
@ -101,9 +116,10 @@ class ExportConstraint(bpy.types.Operator):
write_bone_constraints(context, self.filepath) write_bone_constraints(context, self.filepath)
return {'FINISHED'} return {"FINISHED"}
def invoke(self, context, event): def invoke(self, context, event):
# Open the file browser dialog # Open the file browser dialog
assert context.window_manager is not None
context.window_manager.fileselect_add(self) context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'} return {"RUNNING_MODAL"}

23
uv.lock generated Normal file
View File

@ -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" }]