refactor: Modernize dev experience
This commit is contained in:
parent
4a265bbfad
commit
4f9c5acde0
1
.python-version
Normal file
1
.python-version
Normal file
@ -0,0 +1 @@
|
|||||||
|
3.11
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"python.analysis.typeCheckingMode": "basic"
|
||||||
|
}
|
14
pyproject.toml
Normal file
14
pyproject.toml
Normal 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"
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
23
uv.lock
generated
Normal 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" }]
|
Loading…
x
Reference in New Issue
Block a user