292 lines
9.0 KiB
Python
292 lines
9.0 KiB
Python
|
import typing as typ
|
||
|
|
||
|
import bpy
|
||
|
import sympy as sp
|
||
|
import sympy.physics.units as spu
|
||
|
import pydantic as pyd
|
||
|
|
||
|
from .....utils.pydantic_sympy import SympyExpr
|
||
|
from .. import base
|
||
|
from ... import contracts as ct
|
||
|
|
||
|
ST = ct.SocketType
|
||
|
SU = lambda socket_type: ct.SOCKET_UNITS[
|
||
|
socket_type
|
||
|
]["values"]
|
||
|
|
||
|
####################
|
||
|
# - Utilities
|
||
|
####################
|
||
|
def contract_units_to_items(
|
||
|
socket_type: ST
|
||
|
) -> list[tuple[str, str, str]]:
|
||
|
"""For a given socket type, get a bpy.props.EnumProperty-compatible list-tuple of items to display in the enum dropdown menu.
|
||
|
"""
|
||
|
return [
|
||
|
(
|
||
|
unit_key,
|
||
|
str(unit),
|
||
|
f"{socket_type}-compatible unit",
|
||
|
)
|
||
|
for unit_key, unit in SU(socket_type).items()
|
||
|
]
|
||
|
|
||
|
def default_unit_key_for(socket_type: ST) -> str:
|
||
|
"""For a given socket type, return the default key corresponding to the default unit.
|
||
|
"""
|
||
|
return ct.SOCKET_UNITS[
|
||
|
socket_type
|
||
|
]["default"]
|
||
|
|
||
|
####################
|
||
|
# - Blender Socket
|
||
|
####################
|
||
|
class PhysicalUnitSystemBLSocket(base.MaxwellSimSocket):
|
||
|
socket_type = ST.PhysicalUnitSystem
|
||
|
bl_label = "Unit System"
|
||
|
|
||
|
####################
|
||
|
# - Properties
|
||
|
####################
|
||
|
show_definition: bpy.props.BoolProperty(
|
||
|
name="Show Unit System Definition",
|
||
|
description="Toggle to show unit system definition",
|
||
|
default=False,
|
||
|
update=(lambda self, context: self.sync_prop("show_definition", context)),
|
||
|
)
|
||
|
|
||
|
unit_time: bpy.props.EnumProperty(
|
||
|
name="Time Unit",
|
||
|
description="Unit of time",
|
||
|
items=contract_units_to_items(ST.PhysicalTime),
|
||
|
default=default_unit_key_for(ST.PhysicalTime),
|
||
|
update=(lambda self, context: self.sync_prop("unit_time", context)),
|
||
|
)
|
||
|
|
||
|
unit_angle: bpy.props.EnumProperty(
|
||
|
name="Angle Unit",
|
||
|
description="Unit of angle",
|
||
|
items=contract_units_to_items(ST.PhysicalAngle),
|
||
|
default=default_unit_key_for(ST.PhysicalAngle),
|
||
|
update=(lambda self, context: self.sync_prop("unit_angle", context)),
|
||
|
)
|
||
|
|
||
|
unit_length: bpy.props.EnumProperty(
|
||
|
name="Length Unit",
|
||
|
description="Unit of length",
|
||
|
items=contract_units_to_items(ST.PhysicalLength),
|
||
|
default=default_unit_key_for(ST.PhysicalLength),
|
||
|
update=(lambda self, context: self.sync_prop("unit_length", context)),
|
||
|
)
|
||
|
unit_area: bpy.props.EnumProperty(
|
||
|
name="Area Unit",
|
||
|
description="Unit of area",
|
||
|
items=contract_units_to_items(ST.PhysicalArea),
|
||
|
default=default_unit_key_for(ST.PhysicalArea),
|
||
|
update=(lambda self, context: self.sync_prop("unit_area", context)),
|
||
|
)
|
||
|
unit_volume: bpy.props.EnumProperty(
|
||
|
name="Volume Unit",
|
||
|
description="Unit of time",
|
||
|
items=contract_units_to_items(ST.PhysicalVolume),
|
||
|
default=default_unit_key_for(ST.PhysicalVolume),
|
||
|
update=(lambda self, context: self.sync_prop("unit_volume", context)),
|
||
|
)
|
||
|
|
||
|
unit_point_2d: bpy.props.EnumProperty(
|
||
|
name="Point2D Unit",
|
||
|
description="Unit of 2D points",
|
||
|
items=contract_units_to_items(ST.PhysicalPoint2D),
|
||
|
default=default_unit_key_for(ST.PhysicalPoint2D),
|
||
|
update=(lambda self, context: self.sync_prop("unit_point_2d", context)),
|
||
|
)
|
||
|
unit_point_3d: bpy.props.EnumProperty(
|
||
|
name="Point3D Unit",
|
||
|
description="Unit of 3D points",
|
||
|
items=contract_units_to_items(ST.PhysicalPoint3D),
|
||
|
default=default_unit_key_for(ST.PhysicalPoint3D),
|
||
|
update=(lambda self, context: self.sync_prop("unit_point_3d", context)),
|
||
|
)
|
||
|
|
||
|
unit_size_2d: bpy.props.EnumProperty(
|
||
|
name="Size2D Unit",
|
||
|
description="Unit of 2D sizes",
|
||
|
items=contract_units_to_items(ST.PhysicalSize2D),
|
||
|
default=default_unit_key_for(ST.PhysicalSize2D),
|
||
|
update=(lambda self, context: self.sync_prop("unit_size_2d", context)),
|
||
|
)
|
||
|
unit_size_3d: bpy.props.EnumProperty(
|
||
|
name="Size3D Unit",
|
||
|
description="Unit of 3D sizes",
|
||
|
items=contract_units_to_items(ST.PhysicalSize3D),
|
||
|
default=default_unit_key_for(ST.PhysicalSize3D),
|
||
|
update=(lambda self, context: self.sync_prop("unit_size_3d", context)),
|
||
|
)
|
||
|
|
||
|
unit_mass: bpy.props.EnumProperty(
|
||
|
name="Mass Unit",
|
||
|
description="Unit of mass",
|
||
|
items=contract_units_to_items(ST.PhysicalMass),
|
||
|
default=default_unit_key_for(ST.PhysicalMass),
|
||
|
update=(lambda self, context: self.sync_prop("unit_mass", context)),
|
||
|
)
|
||
|
|
||
|
unit_speed: bpy.props.EnumProperty(
|
||
|
name="Speed Unit",
|
||
|
description="Unit of speed",
|
||
|
items=contract_units_to_items(ST.PhysicalSpeed),
|
||
|
default=default_unit_key_for(ST.PhysicalSpeed),
|
||
|
update=(lambda self, context: self.sync_prop("unit_speed", context)),
|
||
|
)
|
||
|
unit_accel_scalar: bpy.props.EnumProperty(
|
||
|
name="Accel Unit",
|
||
|
description="Unit of acceleration",
|
||
|
items=contract_units_to_items(ST.PhysicalAccelScalar),
|
||
|
default=default_unit_key_for(ST.PhysicalAccelScalar),
|
||
|
update=(lambda self, context: self.sync_prop("unit_accel_scalar", context)),
|
||
|
)
|
||
|
unit_force_scalar: bpy.props.EnumProperty(
|
||
|
name="Force Scalar Unit",
|
||
|
description="Unit of scalar force",
|
||
|
items=contract_units_to_items(ST.PhysicalForceScalar),
|
||
|
default=default_unit_key_for(ST.PhysicalForceScalar),
|
||
|
update=(lambda self, context: self.sync_prop("unit_force_scalar", context)),
|
||
|
)
|
||
|
unit_accel_3d: bpy.props.EnumProperty(
|
||
|
name="Accel3D Unit",
|
||
|
description="Unit of 3D vector acceleration",
|
||
|
items=contract_units_to_items(ST.PhysicalAccel3D),
|
||
|
default=default_unit_key_for(ST.PhysicalAccel3D),
|
||
|
update=(lambda self, context: self.sync_prop("unit_accel_3d", context)),
|
||
|
)
|
||
|
unit_force_3d: bpy.props.EnumProperty(
|
||
|
name="Force3D Unit",
|
||
|
description="Unit of 3D vector force",
|
||
|
items=contract_units_to_items(ST.PhysicalForce3D),
|
||
|
default=default_unit_key_for(ST.PhysicalForce3D),
|
||
|
update=(lambda self, context: self.sync_prop("unit_force_3d", context)),
|
||
|
)
|
||
|
|
||
|
unit_freq: bpy.props.EnumProperty(
|
||
|
name="Freq Unit",
|
||
|
description="Unit of frequency",
|
||
|
items=contract_units_to_items(ST.PhysicalFreq),
|
||
|
default=default_unit_key_for(ST.PhysicalFreq),
|
||
|
update=(lambda self, context: self.sync_prop("unit_freq", context)),
|
||
|
)
|
||
|
|
||
|
####################
|
||
|
# - UI
|
||
|
####################
|
||
|
def draw_label_row(self, row: bpy.types.UILayout, text) -> None:
|
||
|
row.label(text=text)
|
||
|
row.prop(
|
||
|
self, "show_definition", toggle=True, text="", icon="MOD_LENGTH"
|
||
|
)
|
||
|
|
||
|
def draw_value(self, col: bpy.types.UILayout) -> None:
|
||
|
if self.show_definition:
|
||
|
# TODO: We need panels instead of rows!!
|
||
|
col_row=col.row(align=False)
|
||
|
col_row.alignment = "EXPAND"
|
||
|
col_row.prop(self, "unit_time", text="")
|
||
|
col_row.prop(self, "unit_angle", text="")
|
||
|
|
||
|
col_row=col.row(align=False)
|
||
|
col_row.alignment = "EXPAND"
|
||
|
col_row.prop(self, "unit_length", text="")
|
||
|
col_row.prop(self, "unit_area", text="")
|
||
|
col_row.prop(self, "unit_volume", text="")
|
||
|
|
||
|
col_row=col.row(align=True)
|
||
|
col_row.alignment = "EXPAND"
|
||
|
col_row.label(text="Point")
|
||
|
col_row.prop(self, "unit_point_2d", text="")
|
||
|
col_row.prop(self, "unit_point_3d", text="")
|
||
|
|
||
|
col_row=col.row(align=True)
|
||
|
col_row.alignment = "EXPAND"
|
||
|
col_row.label(text="Size")
|
||
|
col_row.prop(self, "unit_size_2d", text="")
|
||
|
col_row.prop(self, "unit_size_3d", text="")
|
||
|
|
||
|
col_row=col.row(align=True)
|
||
|
col_row.alignment = "EXPAND"
|
||
|
col_row.label(text="Mass")
|
||
|
col_row.prop(self, "unit_mass", text="")
|
||
|
|
||
|
col_row=col.row(align=True)
|
||
|
col_row.alignment = "EXPAND"
|
||
|
col_row.label(text="Vel")
|
||
|
col_row.prop(self, "unit_speed", text="")
|
||
|
#col_row.prop(self, "unit_vel_2d_vector", text="")
|
||
|
#col_row.prop(self, "unit_vel_3d_vector", text="")
|
||
|
|
||
|
col_row=col.row(align=True)
|
||
|
col_row.label(text="Accel")
|
||
|
col_row.prop(self, "unit_accel_scalar", text="")
|
||
|
#col_row.prop(self, "unit_accel_2d_vector", text="")
|
||
|
col_row.prop(self, "unit_accel_3d", text="")
|
||
|
|
||
|
col_row=col.row(align=True)
|
||
|
col_row.label(text="Force")
|
||
|
col_row.prop(self, "unit_force_scalar", text="")
|
||
|
#col_row.prop(self, "unit_force_2d_vector", text="")
|
||
|
col_row.prop(self, "unit_force_3d", text="")
|
||
|
|
||
|
col_row=col.row(align=True)
|
||
|
col_row.alignment = "EXPAND"
|
||
|
col_row.label(text="Freq")
|
||
|
col_row.prop(self, "unit_freq", text="")
|
||
|
|
||
|
####################
|
||
|
# - Default Value
|
||
|
####################
|
||
|
@property
|
||
|
def value(self) -> dict[ST, SympyExpr]:
|
||
|
return {
|
||
|
socket_type: SU(socket_type)[socket_unit_prop]
|
||
|
for socket_type, socket_unit_prop in [
|
||
|
(ST.PhysicalTime, self.unit_time),
|
||
|
(ST.PhysicalAngle, self.unit_angle),
|
||
|
|
||
|
(ST.PhysicalLength, self.unit_length),
|
||
|
(ST.PhysicalArea, self.unit_area),
|
||
|
(ST.PhysicalVolume, self.unit_volume),
|
||
|
|
||
|
(ST.PhysicalPoint2D, self.unit_point_2d),
|
||
|
(ST.PhysicalPoint3D, self.unit_point_3d),
|
||
|
|
||
|
(ST.PhysicalSize2D, self.unit_size_2d),
|
||
|
(ST.PhysicalSize3D, self.unit_size_3d),
|
||
|
|
||
|
(ST.PhysicalMass, self.unit_mass),
|
||
|
|
||
|
(ST.PhysicalSpeed, self.unit_speed),
|
||
|
(ST.PhysicalAccelScalar, self.unit_accel_scalar),
|
||
|
(ST.PhysicalForceScalar, self.unit_force_scalar),
|
||
|
(ST.PhysicalAccel3D, self.unit_accel_3d),
|
||
|
(ST.PhysicalForce3D, self.unit_force_3d),
|
||
|
|
||
|
(ST.PhysicalFreq, self.unit_freq),
|
||
|
]
|
||
|
}
|
||
|
|
||
|
####################
|
||
|
# - Socket Configuration
|
||
|
####################
|
||
|
class PhysicalUnitSystemSocketDef(pyd.BaseModel):
|
||
|
socket_type: ST = ST.PhysicalUnitSystem
|
||
|
|
||
|
show_by_default: bool = False
|
||
|
|
||
|
def init(self, bl_socket: PhysicalUnitSystemBLSocket) -> None:
|
||
|
bl_socket.show_definition = self.show_by_default
|
||
|
|
||
|
####################
|
||
|
# - Blender Registration
|
||
|
####################
|
||
|
BL_REGISTER = [
|
||
|
PhysicalUnitSystemBLSocket,
|
||
|
]
|