diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/flow_kinds.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/flow_kinds.py index 8f37fd4..af58248 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/flow_kinds.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/flow_kinds.py @@ -96,10 +96,6 @@ class CapabilitiesFlow: for name in self.must_match ) ) - return ( - self.socket_type == other.socket_type - and self.active_kind == other.active_kind - ) or other.is_universal #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/wave_constant.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/wave_constant.py index 6bfb614..7520db8 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/wave_constant.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/wave_constant.py @@ -1,12 +1,13 @@ +"""Implements `WaveConstantNode`.""" + import typing as typ import bpy import sympy as sp import sympy.physics.units as spu +from blender_maxwell.utils import bl_cache, logger, sci_constants from blender_maxwell.utils import extra_sympy_units as spux -from blender_maxwell.utils import logger -from blender_maxwell.utils import sci_constants as constants from ... import contracts as ct from ... import sockets @@ -16,84 +17,158 @@ log = logger.get(__name__) class WaveConstantNode(base.MaxwellSimNode): + """Translates vaccum wavelength/frequency into both, either as a scalar, or as a memory-efficient uniform range of values. + + Socket Sets: + Wavelength: Input a wavelength (range) to produce both wavelength/frequency (ranges). + Frequency: Input a frequency (range) to produce both wavelength/frequency (ranges). + + Attributes: + use_range: Whether to specify a range of wavelengths/frequencies, or just one. + """ + node_type = ct.NodeType.WaveConstant bl_label = 'Wave Constant' input_socket_sets: typ.ClassVar = { - 'Wavelength': {}, - 'Frequency': {}, + 'Wavelength': { + 'WL': sockets.ExprSocketDef( + active_kind=ct.FlowKind.Value, + unit_dimension=spux.unit_dims.length, + # Defaults + default_unit=spu.nm, + default_value=500, + default_min=200, + default_max=700, + default_steps=2, + ) + }, + 'Frequency': { + 'Freq': sockets.ExprSocketDef( + active_kind=ct.FlowKind.Value, + unit_dimension=spux.unit_dims.frequency, + # Defaults + default_unit=spux.THz, + default_value=1, + default_min=0.3, + default_max=3, + default_steps=2, + ), + }, + } + output_sockets: typ.ClassVar = { + 'WL': sockets.ExprSocketDef( + active_kind=ct.FlowKind.Value, + unit_dimension=spux.unit_dims.length, + ), + 'Freq': sockets.ExprSocketDef( + active_kind=ct.FlowKind.Value, + unit_dimension=spux.unit_dims.frequency, + ), } - use_range: bpy.props.BoolProperty( - name='Range', - description='Whether to use a wavelength/frequency range', - default=False, - update=lambda self, context: self.on_prop_changed('use_range', context), - ) - - def draw_props(self, _: bpy.types.Context, col: bpy.types.UILayout): - col.prop(self, 'use_range', toggle=True) + #################### + # - Properties + #################### + use_range: bool = bl_cache.BLField(False) #################### - # - Event Methods: Wavelength Output + # - UI + #################### + def draw_props(self, _: bpy.types.Context, col: bpy.types.UILayout) -> None: + """Draws the button that allows toggling between single and range output. + + Parameters: + col: Target for defining UI elements. + """ + col.prop(self, self.blfields['use_range'], toggle=True) + + #################### + # - Events + #################### + @events.on_value_changed( + prop_name={'active_socket_set', 'use_range'}, + props='use_range', + run_on_init=True, + ) + def on_use_range_changed(self, props: dict) -> None: + """Synchronize the `active_kind` of input/output sockets, to either produce a `ct.FlowKind.Value` or a `ct.FlowKind.LazyArrayRange`.""" + if self.inputs.get('WL') is not None: + active_input = self.inputs['WL'] + else: + active_input = self.inputs['Freq'] + + # Modify Active Kind(s) + ## Input active_kind -> Value/LazyArrayRange + active_input_uses_range = active_input.active_kind == ct.FlowKind.LazyArrayRange + if active_input_uses_range != props['use_range']: + active_input.active_kind = ( + ct.FlowKind.LazyArrayRange if props['use_range'] else ct.FlowKind.Value + ) + + ## Output active_kind -> Value/LazyArrayRange + for active_output in self.outputs.values(): + active_output_uses_range = ( + active_output.active_kind == ct.FlowKind.LazyArrayRange + ) + if active_output_uses_range != props['use_range']: + active_output.active_kind = ( + ct.FlowKind.LazyArrayRange + if props['use_range'] + else ct.FlowKind.Value + ) + + #################### + # - FlowKinds #################### @events.computes_output_socket( 'WL', kind=ct.FlowKind.Value, - # Data input_sockets={'WL', 'Freq'}, input_sockets_optional={'WL': True, 'Freq': True}, ) def compute_wl_value(self, input_sockets: dict) -> sp.Expr: + """Compute a single wavelength value from either wavelength/frequency.""" if input_sockets['WL'] is not None: return input_sockets['WL'] - if input_sockets['WL'] is None and input_sockets['Freq'] is None: - msg = 'Both WL and Freq are None.' - raise RuntimeError(msg) - - return constants.vac_speed_of_light / input_sockets['Freq'] + return sci_constants.vac_speed_of_light / input_sockets['Freq'] @events.computes_output_socket( 'Freq', kind=ct.FlowKind.Value, - # Data input_sockets={'WL', 'Freq'}, input_sockets_optional={'WL': True, 'Freq': True}, ) def compute_freq_value(self, input_sockets: dict) -> sp.Expr: + """Compute a single frequency value from either wavelength/frequency.""" if input_sockets['Freq'] is not None: return input_sockets['Freq'] - if input_sockets['WL'] is None and input_sockets['Freq'] is None: - msg = 'Both WL and Freq are None.' - raise RuntimeError(msg) - - return constants.vac_speed_of_light / input_sockets['WL'] + return sci_constants.vac_speed_of_light / input_sockets['WL'] @events.computes_output_socket( 'WL', kind=ct.FlowKind.LazyArrayRange, - # Data input_sockets={'WL', 'Freq'}, + input_socket_kinds={ + 'WL': ct.FlowKind.LazyArrayRange, + 'Freq': ct.FlowKind.LazyArrayRange, + }, input_sockets_optional={'WL': True, 'Freq': True}, ) def compute_wl_range(self, input_sockets: dict) -> sp.Expr: + """Compute wavelength range from either wavelength/frequency ranges.""" if input_sockets['WL'] is not None: return input_sockets['WL'] - if input_sockets['WL'] is None and input_sockets['Freq'] is None: - msg = 'Both WL and Freq are None.' - raise RuntimeError(msg) - return input_sockets['Freq'].rescale_bounds( - lambda bound: constants.vac_speed_of_light / bound, reverse=True + lambda bound: sci_constants.vac_speed_of_light / bound, reverse=True ) @events.computes_output_socket( 'Freq', kind=ct.FlowKind.LazyArrayRange, - # Data input_sockets={'WL', 'Freq'}, input_socket_kinds={ 'WL': ct.FlowKind.LazyArrayRange, @@ -102,48 +177,14 @@ class WaveConstantNode(base.MaxwellSimNode): input_sockets_optional={'WL': True, 'Freq': True}, ) def compute_freq_range(self, input_sockets: dict) -> sp.Expr: + """Compute frequency range from either wavelength/frequency ranges.""" if input_sockets['Freq'] is not None: return input_sockets['Freq'] - if input_sockets['WL'] is None and input_sockets['Freq'] is None: - msg = 'Both WL and Freq are None.' - raise RuntimeError(msg) - return input_sockets['WL'].rescale_bounds( - lambda bound: constants.vac_speed_of_light / bound, reverse=True + lambda bound: sci_constants.vac_speed_of_light / bound, reverse=True ) - #################### - # - Event Methods - #################### - @events.on_value_changed( - prop_name={'active_socket_set', 'use_range'}, - props={'active_socket_set', 'use_range'}, - run_on_init=True, - ) - def on_input_spec_change(self, props: dict): - if props['active_socket_set'] == 'Wavelength': - self.loose_input_sockets = { - 'WL': sockets.PhysicalLengthSocketDef( - is_array=props['use_range'], - default_value=500 * spu.nm, - default_unit=spu.nm, - ) - } - else: - self.loose_input_sockets = { - 'Freq': sockets.PhysicalFreqSocketDef( - is_array=props['use_range'], - default_value=600 * spux.THz, - default_unit=spux.THz, - ) - } - - self.loose_output_sockets = { - 'WL': sockets.PhysicalLengthSocketDef(is_array=props['use_range']), - 'Freq': sockets.PhysicalFreqSocketDef(is_array=props['use_range']), - } - #################### # - Blender Registration diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/props/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/props/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/props/base.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/props/base.py deleted file mode 100644 index 23b95ad..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/props/base.py +++ /dev/null @@ -1,26 +0,0 @@ -#import typing as typ -#import bpy -# -#from .. import contracts as ct -# -# -# -#class MaxwellSimProp(bpy.types.PropertyGroup): -# """A Blender property usable in nodes and sockets.""" -# name: str = "" -# data_flow_kind: ct.FlowKind -# -# value: dict[str, tuple[bpy.types.Property, dict]] | None = None -# -# def __init_subclass__(cls, **kwargs: typ.Any): -# log.debug('Initializing Prop: %s', cls.node_type) -# super().__init_subclass__(**kwargs) -# -# # Setup Value -# if cls.value: -# cls.__annotations__['raw_value'] = value -# -# -# @property -# def value(self): -# if self.data_flow_kind diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/__init__.py index 93cb82a..0feb383 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/__init__.py @@ -1,11 +1,11 @@ from blender_maxwell.utils import logger from .. import contracts as ct -from . import basic, blender, maxwell, number, physical, tidy3d, vector +from . import basic, blender, maxwell, physical, tidy3d from .scan_socket_defs import scan_for_socket_defs log = logger.get(__name__) -sockets_modules = [basic, number, vector, physical, blender, maxwell, tidy3d] +sockets_modules = [basic, physical, blender, maxwell, tidy3d] #################### # - Scan for SocketDefs @@ -33,8 +33,6 @@ for socket_type in ct.SocketType: #################### BL_REGISTER = [ *basic.BL_REGISTER, - *number.BL_REGISTER, - *vector.BL_REGISTER, *physical.BL_REGISTER, *blender.BL_REGISTER, *maxwell.BL_REGISTER, @@ -43,8 +41,6 @@ BL_REGISTER = [ __all__ = [ 'basic', - 'number', - 'vector', 'physical', 'blender', 'maxwell', diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py index fa7fc3e..ed33796 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py @@ -1,5 +1,4 @@ import abc -import functools import typing as typ import uuid from types import MappingProxyType @@ -193,6 +192,7 @@ class MaxwellSimSocket(bpy.types.NodeSocket): log.debug('Initializing Socket: %s', cls.socket_type) super().__init_subclass__(**kwargs) # cls._assert_attrs_valid() + ## TODO: Implement this :) # Socket Properties ## Identifiers @@ -235,46 +235,10 @@ class MaxwellSimSocket(bpy.types.NodeSocket): #################### # - Units #################### - # TODO: Refactor - @functools.cached_property - def possible_units(self) -> dict[str, sp.Expr]: - if not self.use_units: - msg = "Tried to get possible units for socket {self}, but socket doesn't `use_units`" - raise ValueError(msg) - - return ct.SOCKET_UNITS[self.socket_type]['values'] - - @property - def unit(self) -> sp.Expr: - return self.possible_units[self.active_unit] - @property def prev_unit(self) -> sp.Expr: return self.possible_units[self.prev_active_unit] - @unit.setter - def unit(self, value: str | sp.Expr) -> None: - # Retrieve Unit by String - if isinstance(value, str) and value in self.possible_units: - self.active_unit = self.possible_units[value] - return - - # Retrieve =1 Matching Unit Name - matching_unit_names = [ - unit_name - for unit_name, unit_sympy in self.possible_units.items() - if value == unit_sympy - ] - if len(matching_unit_names) == 0: - msg = f"Tried to set unit for socket {self} with value {value}, but it is not one of possible units {''.join(self.possible_units.values())} for this socket (as defined in `contracts.SOCKET_UNITS`)" - raise ValueError(msg) - - if len(matching_unit_names) > 1: - msg = f"Tried to set unit for socket {self} with value {value}, but multiple possible matching units {''.join(self.possible_units.values())} for this socket (as defined in `contracts.SOCKET_UNITS`); there may only be one" - raise RuntimeError(msg) - - self.active_unit = matching_unit_names[0] - #################### # - Property Event: On Update #################### @@ -285,37 +249,9 @@ class MaxwellSimSocket(bpy.types.NodeSocket): Called by `self.on_prop_changed()` when `self.active_kind` was changed. """ self.display_shape = ( - 'SQUARE' - if self.active_kind in {ct.FlowKind.Array, ct.FlowKind.LazyValueRange} - else 'CIRCLE' + 'SQUARE' if self.active_kind == ct.FlowKind.LazyValueRange else 'CIRCLE' ) + ('_DOT' if self.use_units else '') - - def _on_unit_changed(self) -> None: - """Synchronizes the `FlowKind` data to the newly set unit. - - When a new unit is set, the internal ex. floating point properties become out of sync. - This function applies a rescaling operation based on the factor between the previous unit (`self.prev_unit`) and the new unit `(self.unit)`. - - - **Value**: Retrieve the value (with incorrect new unit), exchange the new unit for the old unit, and assign it back. - - **Array**: Replace the internal unit with the old (correct) unit, and rescale all values in the array to the new unit. - - Notes: - Called by `self.on_prop_changed()` when `self.active_unit` is changed. - - This allows for a unit-scaling operation **without needing to know anything about the data representation** (at the cost of performance). - """ - if self.active_kind == ct.FlowKind.Value: - self.value = self.value / self.unit * self.prev_unit - - elif self.active_kind in [ct.FlowKind.Array, ct.FlowKind.LazyArrayRange]: - self.lazy_value_range = self.lazy_value_range.correct_unit( - self.prev_unit - ).rescale_to_unit(self.unit) - else: - msg = f'Socket {self.bl_label} ({self.socket_type}): Active kind {self.active_kind} declares no method of scaling units from {self.prev_active_unit} to {self.active_unit})' - raise RuntimeError(msg) - - self.prev_active_unit = self.active_unit + ## TODO: Valid Active Kinds should be a subset/subenum(?) of FlowKind def on_prop_changed(self, prop_name: str, _: bpy.types.Context) -> None: """Called when a property has been updated. @@ -880,7 +816,6 @@ class MaxwellSimSocket(bpy.types.NodeSocket): col = row.column(align=True) { ct.FlowKind.Value: self.draw_value, - ct.FlowKind.Array: self.draw_array, ct.FlowKind.LazyArrayRange: self.draw_lazy_array_range, }[self.active_kind](col) @@ -922,7 +857,7 @@ class MaxwellSimSocket(bpy.types.NodeSocket): self.draw_info(info, col) #################### - # - UI Methods: Active FlowKind + # - UI Methods: Label Rows #################### def draw_label_row( self, @@ -978,6 +913,9 @@ class MaxwellSimSocket(bpy.types.NodeSocket): """ row.label(text=text) + #################### + # - UI Methods: Active FlowKind + #################### def draw_value(self, col: bpy.types.UILayout) -> None: """Draws the socket value on its own line. @@ -988,16 +926,6 @@ class MaxwellSimSocket(bpy.types.NodeSocket): col: Target for defining UI elements. """ - def draw_array(self, col: bpy.types.UILayout) -> None: - """Draws the socket array on its own line. - - Notes: - Should be overriden by individual socket classes, if they have an editable `FlowKind.Array`. - - Parameters: - col: Target for defining UI elements. - """ - def draw_lazy_array_range(self, col: bpy.types.UILayout) -> None: """Draws the socket lazy array range on its own line. diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/data.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/data.py deleted file mode 100644 index 9d84bd7..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/data.py +++ /dev/null @@ -1,205 +0,0 @@ -import enum -import typing as typ - -import bpy - -from blender_maxwell.utils import bl_cache, logger -from blender_maxwell.utils import extra_sympy_units as spux - -from ... import contracts as ct -from .. import base - -log = logger.get(__name__) - - -def unicode_superscript(n): - return ''.join(['⁰¹²³⁴⁵⁶⁷⁸⁹'[ord(c) - ord('0')] for c in str(n)]) - - -class DataInfoColumn(enum.StrEnum): - Length = enum.auto() - MathType = enum.auto() - Unit = enum.auto() - - @staticmethod - def to_name(value: typ.Self) -> str: - return { - DataInfoColumn.Length: 'L', - DataInfoColumn.MathType: '∈', - DataInfoColumn.Unit: 'U', - }[value] - - @staticmethod - def to_icon(value: typ.Self) -> str: - return { - DataInfoColumn.Length: '', - DataInfoColumn.MathType: '', - DataInfoColumn.Unit: '', - }[value] - - -#################### -# - Blender Socket -#################### -class DataBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.Data - bl_label = 'Data' - use_info_draw = True - - #################### - # - Properties: Format - #################### - format: str = bl_cache.BLField('') - ## TODO: typ.Literal['xarray', 'jax'] - - show_info_columns: bool = bl_cache.BLField( - True, - prop_ui=True, - # use_prop_update=False, - ) - info_columns: DataInfoColumn = bl_cache.BLField( - {DataInfoColumn.MathType, DataInfoColumn.Unit}, - prop_ui=True, - enum_many=True, - # use_prop_update=False, - ) - - #################### - # - FlowKind - #################### - @property - def capabilities(self) -> ct.CapabilitiesFlow: - return ct.CapabilitiesFlow( - socket_type=self.socket_type, - active_kind=self.active_kind, - must_match={'format': self.format}, - ) - - #################### - # - UI - #################### - def draw_input_label_row(self, row: bpy.types.UILayout, text) -> None: - info = self.compute_data(kind=ct.FlowKind.Info) - has_dims = ( - not ct.FlowSignal.check(info) and self.format == 'jax' and info.dim_names - ) - - if has_dims: - split = row.split(factor=0.85, align=True) - _row = split.row(align=False) - else: - _row = row - - _row.label(text=text) - if has_dims: - if self.show_info_columns: - _row.prop(self, self.blfields['info_columns']) - - _row = split.row(align=True) - _row.alignment = 'RIGHT' - _row.prop( - self, - self.blfields['show_info_columns'], - toggle=True, - text='', - icon=ct.Icon.ToggleSocketInfo, - ) - - def draw_output_label_row(self, row: bpy.types.UILayout, text) -> None: - info = self.compute_data(kind=ct.FlowKind.Info) - has_dims = ( - not ct.FlowSignal.check(info) and self.format == 'jax' and info.dim_names - ) - - if has_dims: - split = row.split(factor=0.15, align=True) - - _row = split.row(align=True) - _row.prop( - self, - self.blfields['show_info_columns'], - toggle=True, - text='', - icon=ct.Icon.ToggleSocketInfo, - ) - - _row = split.row(align=False) - _row.alignment = 'RIGHT' - if self.show_info_columns: - _row.prop(self, self.blfields['info_columns']) - else: - _col = _row.column() - _col.alignment = 'EXPAND' - _col.label(text='') - else: - _row = row - - _row.label(text=text) - - def draw_info(self, info: ct.InfoFlow, col: bpy.types.UILayout) -> None: - if self.format == 'jax' and info.dim_names and self.show_info_columns: - row = col.row() - box = row.box() - grid = box.grid_flow( - columns=len(self.info_columns) + 1, - row_major=True, - even_columns=True, - # even_rows=True, - align=True, - ) - - # Dimensions - for dim_name in info.dim_names: - dim_idx = info.dim_idx[dim_name] - grid.label(text=dim_name) - if DataInfoColumn.Length in self.info_columns: - grid.label(text=str(len(dim_idx))) - if DataInfoColumn.MathType in self.info_columns: - grid.label(text=spux.MathType.to_str(dim_idx.mathtype)) - if DataInfoColumn.Unit in self.info_columns: - grid.label(text=spux.sp_to_str(dim_idx.unit)) - - # Outputs - grid.label(text=info.output_name) - if DataInfoColumn.Length in self.info_columns: - grid.label(text='', icon=ct.Icon.DataSocketOutput) - if DataInfoColumn.MathType in self.info_columns: - grid.label( - text=( - spux.MathType.to_str(info.output_mathtype) - + ( - 'ˣ'.join( - [ - unicode_superscript(out_axis) - for out_axis in info.output_shape - ] - ) - if info.output_shape - else '' - ) - ) - ) - if DataInfoColumn.Unit in self.info_columns: - grid.label(text=f'{spux.sp_to_str(info.output_unit)}') - - -#################### -# - Socket Configuration -#################### -class DataSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.Data - - format: typ.Literal['xarray', 'jax', 'monitor_data'] - default_show_info_columns: bool = True - - def init(self, bl_socket: DataBLSocket) -> None: - bl_socket.format = self.format - bl_socket.default_show_info_columns = self.default_show_info_columns - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - DataBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/expr.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/expr.py deleted file mode 100644 index e28b726..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/expr.py +++ /dev/null @@ -1,147 +0,0 @@ -import typing as typ - -import bpy -import pydantic as pyd -import sympy as sp - -from blender_maxwell.utils import bl_cache, logger -from blender_maxwell.utils import extra_sympy_units as spux - -from ... import contracts as ct -from .. import base - -log = logger.get(__name__) - - -class ExprBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.Expr - bl_label = 'Expr' - - #################### - # - Properties - #################### - raw_value: bpy.props.StringProperty( - name='Expr', - description='Represents a symbolic expression', - default='', - update=(lambda self, context: self.on_prop_changed('raw_value', context)), - ) - - int_symbols: frozenset[spux.IntSymbol] = bl_cache.BLField(frozenset()) - real_symbols: frozenset[spux.RealSymbol] = bl_cache.BLField(frozenset()) - complex_symbols: frozenset[spux.ComplexSymbol] = bl_cache.BLField(frozenset()) - - @bl_cache.cached_bl_property(persist=False) - def symbols(self) -> list[spux.Symbol]: - """Retrieves all symbols by concatenating int, real, and complex symbols, and sorting them by name. - - The order is guaranteed to be **deterministic**. - - Returns: - All symbols valid for use in the expression. - """ - return sorted( - self.int_symbols | self.real_symbols | self.complex_symbols, - key=lambda sym: sym.name, - ) - - #################### - # - Socket UI - #################### - def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, 'raw_value', text='') - if len(self.symbols) > 0: - box = col.box() - split = box.split(factor=0.3) - - # Left Col - col = split.column() - col.label(text='Let:') - - # Right Col - col = split.column() - col.alignment = 'RIGHT' - for sym in self.symbols: - col.label(text=spux.pretty_symbol(sym)) - - #################### - # - Computation of Default Value - #################### - @property - def value(self) -> sp.Expr: - return sp.sympify( - self.raw_value, - locals={sym.name: sym for sym in self.symbols}, - strict=False, - convert_xor=True, - ).subs(spux.ALL_UNIT_SYMBOLS) - - @value.setter - def value(self, value: str) -> None: - self.raw_value = sp.sstr(value) - - @property - def lazy_value_func(self) -> ct.LazyValueFuncFlow: - return ct.LazyValueFuncFlow( - func=sp.lambdify(self.symbols, self.value, 'jax'), - func_args=[spux.sympy_to_python_type(sym) for sym in self.symbols], - supports_jax=True, - ) - - -#################### -# - Socket Configuration -#################### -class ExprSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.Expr - - int_symbols: frozenset[spux.IntSymbol] = frozenset() - real_symbols: frozenset[spux.RealSymbol] = frozenset() - complex_symbols: frozenset[spux.ComplexSymbol] = frozenset() - - @property - def symbols(self) -> list[spux.Symbol]: - """Retrieves all symbols by concatenating int, real, and complex symbols, and sorting them by name. - - The order is guaranteed to be **deterministic**. - - Returns: - All symbols valid for use in the expression. - """ - return sorted( - self.int_symbols | self.real_symbols | self.complex_symbols, - key=lambda sym: sym.name, - ) - - # Expression - default_expr: spux.SympyExpr = sp.S(1) - allow_units: bool = True - - @pyd.model_validator(mode='after') - def check_default_expr_follows_unit_allowance(self) -> typ.Self: - """Checks that `self.default_expr` only uses units if `self.allow_units` is defined. - - Raises: - ValueError: If the expression uses symbols not defined in `self.symbols`. - """ - if spux.uses_units(self.default_expr) and not self.allow_units: - msg = f'Expression {self.default_expr} uses units, but "self.allow_units" is False' - raise ValueError(msg) - - return self - - ## TODO: Validator for Symbol Usage - - def init(self, bl_socket: ExprBLSocket) -> None: - bl_socket.value = self.default_expr - bl_socket.int_symbols = self.int_symbols - bl_socket.real_symbols = self.real_symbols - bl_socket.complex_symbols = self.complex_symbols - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - ExprBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/expr.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/expr.py new file mode 100644 index 0000000..7a1be91 --- /dev/null +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/expr.py @@ -0,0 +1,617 @@ +import enum +import typing as typ + +import bpy +import sympy as sp + +from blender_maxwell.utils import bl_cache, logger +from blender_maxwell.utils import extra_sympy_units as spux + +from ... import contracts as ct +from .. import base + +## TODO: This is a big node, and there's a lot to get right. +## - Dynamically adjust the value when the user changes the unit in the UI. +## - Dynamically adjust socket color in response to, especially, the unit dimension. +## - Iron out the meaning of display shapes. +## - Generally pay attention to validity checking; it's make or break. +## - For array generation, it may pay to have both a symbolic expression (producing output according to `size` as usual) denoting how to actually make values, and how many. Enables ex. easy symbolic +## - For array generation, it may pay to have both a symbolic expression (producing output according to `size` as usual) + +log = logger.get(__name__) + +Int2: typ.TypeAlias = tuple[int, int] +Int3: typ.TypeAlias = tuple[int, int, int] +Int22: typ.TypeAlias = tuple[tuple[int, int], tuple[int, int]] +Int32: typ.TypeAlias = tuple[tuple[int, int], tuple[int, int], tuple[int, int]] +Float2: typ.TypeAlias = tuple[float, float] +Float3: typ.TypeAlias = tuple[float, float, float] +Float22: typ.TypeAlias = tuple[tuple[float, float], tuple[float, float]] +Float32: typ.TypeAlias = tuple[ + tuple[float, float], tuple[float, float], tuple[float, float] +] + + +def unicode_superscript(n): + return ''.join(['⁰¹²³⁴⁵⁶⁷⁸⁹'[ord(c) - ord('0')] for c in str(n)]) + + +class InfoDisplayCol(enum.StrEnum): + """Valid columns for specifying displayed information from an `ct.InfoFlow`.""" + + Length = enum.auto() + MathType = enum.auto() + Unit = enum.auto() + + @staticmethod + def to_name(value: typ.Self) -> str: + IDC = InfoDisplayCol + return { + IDC.Length: 'L', + IDC.MathType: '∈', + IDC.Unit: 'U', + }[value] + + @staticmethod + def to_icon(value: typ.Self) -> str: + IDC = InfoDisplayCol + return { + IDC.Length: '', + IDC.MathType: '', + IDC.Unit: '', + }[value] + + +class ExprBLSocket(base.MaxwellSimSocket): + socket_type = ct.SocketType.Expr + bl_label = 'Expr' + + #################### + # - Properties + #################### + size: typ.Literal[None, 2, 3] = bl_cache.BLField(None, prop_ui=True) + mathtype: spux.MathType = bl_cache.BLField(spux.MathType.Real, prop_ui=True) + symbols: frozenset[spux.Symbol] = bl_cache.BLField(frozenset()) + + ## Units + unit_dim: spux.UnitDimension | None = bl_cache.BLField(None) + active_unit: enum.Enum = bl_cache.BLField( + None, enum_cb=lambda self, _: self.search_units(), prop_ui=True + ) + + ## Info Display + show_info_columns: bool = bl_cache.BLField(False, prop_ui=True) + info_columns: InfoDisplayCol = bl_cache.BLField( + {InfoDisplayCol.MathType, InfoDisplayCol.Unit}, + prop_ui=True, + enum_many=True, + ) + + # UI: Value + ## Expression + raw_value_spstr: str = bl_cache.BLField('', prop_ui=True) + ## 1D + raw_value_int: int = bl_cache.BLField(0, prop_ui=True) + raw_value_rat: Int2 = bl_cache.BLField((0, 1), prop_ui=True) + raw_value_float: float = bl_cache.BLField(0.0, float_prec=4, prop_ui=True) + raw_value_complex: Float2 = bl_cache.BLField((0, 1), float_prec=4, prop_ui=True) + ## 2D + raw_value_int2: Int2 = bl_cache.BLField((0, 0), prop_ui=True) + raw_value_rat2: Int22 = bl_cache.BLField(((0, 1), (0, 1)), prop_ui=True) + raw_value_float2: Float2 = bl_cache.BLField((0.0, 0.0), float_prec=4, prop_ui=True) + raw_value_complex2: Float22 = bl_cache.BLField( + ((0.0, 0.0), (0.0, 0.0)), float_prec=4, prop_ui=True + ) + ## 3D + raw_value_int3: Int3 = bl_cache.BLField((0, 0, 0), prop_ui=True) + raw_value_rat3: Int32 = bl_cache.BLField(((0, 1), (0, 1), (0, 1)), prop_ui=True) + raw_value_float3: Float3 = bl_cache.BLField((0.0, 0.0), float_prec=4, prop_ui=True) + raw_value_complex3: Float32 = bl_cache.BLField( + ((0.0, 0.0), (0.0, 0.0), (0.0, 0.0)), float_prec=4, prop_ui=True + ) + + # UI: LazyArrayRange + steps: int = bl_cache.BLField(2, abs_min=2) + ## Expression + raw_min_spstr: str = bl_cache.BLField('', prop_ui=True) + raw_max_spstr: str = bl_cache.BLField('', prop_ui=True) + ## By MathType + raw_range_int: Int2 = bl_cache.BLField((0, 1), prop_ui=True) + raw_range_rat: Int22 = bl_cache.BLField(((0, 1), (1, 1)), prop_ui=True) + raw_range_float: Float2 = bl_cache.BLField((0.0, 1.0), prop_ui=True) + raw_range_complex: Float22 = bl_cache.BLField( + ((0.0, 0.0), (1.0, 1.0)), float_prec=4, prop_ui=True + ) + + #################### + # - Computed: Raw Expressions + #################### + @property + def raw_value_sp(self) -> spux.SympyExpr: + return self._parse_expr_str(self.raw_value_spstr) + + @property + def raw_min_sp(self) -> spux.SympyExpr: + return self._parse_expr_str(self.raw_min_spstr) + + @property + def raw_max_sp(self) -> spux.SympyExpr: + return self._parse_expr_str(self.raw_max_spstr) + + #################### + # - Computed: Units + #################### + def search_units(self, _: bpy.types.Context) -> list[ct.BLEnumElement]: + if self.unit_dim is not None: + return [ + (sp.sstr(unit), spux.sp_to_str(unit), sp.sstr(unit), '', i) + for i, unit in enumerate(spux.unit_dim_units(self.unit_dim)) + ] + return [] + + @property + def unit(self) -> spux.Unit | None: + if self.active_unit != 'NONE': + return spux.unit_str_to_unit(self.active_unit) + + return None + + @unit.setter + def unit(self, unit: spux.Unit) -> None: + valid_units = spux.unit_dim_units(self.unit_dim) + if unit in valid_units: + self.active_unit = sp.sstr(unit) + + msg = f'Tried to set invalid unit {unit} (unit dim "{self.unit_dim}" only supports "{valid_units}")' + raise ValueError(msg) + + #################### + # - Methods + #################### + def _parse_expr_info( + self, expr: spux.SympyExpr + ) -> tuple[spux.MathType, typ.Literal[None, 2, 3], spux.UnitDimension]: + # Parse MathType + mathtype = spux.MathType.from_expr(expr) + if self.mathtype != mathtype: + msg = f'MathType is {self.mathtype}, but tried to set expr {expr} with mathtype {mathtype}' + raise ValueError(msg) + + # Parse Symbols + if expr.free_symbols: + if self.mathtype is not None: + msg = f'MathType is {self.mathtype}, but tried to set expr {expr} with free symbols {expr.free_symbols}' + raise ValueError(msg) + + if not expr.free_symbols.issubset(self.symbols): + msg = f'Tried to set expr {expr} with free symbols {expr.free_symbols}, which is incompatible with socket symbols {self.symbols}' + raise ValueError(msg) + + # Parse Dimensions + size = spux.parse_size(expr) + if size != self.size: + msg = f'Expr {expr} has {size} dimensions, which is incompatible with the expr socket ({self.size} dimensions)' + raise ValueError(msg) + + # Parse Unit Dimension + unit_dim = spux.parse_unit_dim(expr) + if unit_dim != self.unit_dim: + msg = f'Expr {expr} has unit dimension {unit_dim}, which is incompatible with socket unit dimension {self.unit_dim}' + raise ValueError(msg) + + return mathtype, size, unit_dim + + def _to_raw_value(self, expr: spux.SympyExpr): + if self.unit is not None: + return spux.sympy_to_python(spux.scale_to_unit(expr, self.unit)) + return spux.sympy_to_python(expr) + + def _parse_expr_str(self, expr_spstr: str) -> None: + expr = sp.sympify( + expr_spstr, + locals={sym.name: sym for sym in self.symbols}, + strict=False, + convert_xor=True, + ).subs(spux.ALL_UNIT_SYMBOLS) * (self.unit if self.unit is not None else 1) + + # Try Parsing and Returning the Expression + try: + self._parse_expr_info(expr) + except ValueError(expr) as ex: + log.exception( + 'Couldn\'t parse expression "%s" in Expr socket.', + expr_spstr, + ) + else: + return expr + + return None + + #################### + # - FlowKind: Value + #################### + @property + def value(self) -> spux.SympyExpr: + """Return the expression defined by the socket. + + - **Num Dims**: Determine which property dimensionality to pull data from. + - **MathType**: Determine which property type to pull data from. + + When `self.mathtype` is `None`, the expression is parsed from the string `self.raw_value_spstr`. + + Notes: + Called to compute the internal `FlowKind.Value` of this socket. + + Return: + The expression defined by the socket, in the socket's unit. + """ + if self.symbols: + expr = self.raw_value_sp + if expr is None: + return ct.FlowSignal.FlowPending + + MT_Z = spux.MathType.Integer + MT_Q = spux.MathType.Rational + MT_R = spux.MathType.Real + MT_C = spux.MathType.Complex + Z = sp.Integer + Q = sp.Rational + R = sp.RealNumber + return { + None: { + MT_Z: lambda: Z(self.raw_value_int), + MT_Q: lambda: Q(self.raw_value_rat[0], self.raw_value_rat[1]), + MT_R: lambda: R(self.raw_value_float), + MT_C: lambda: ( + self.raw_value_complex[0] + sp.I * self.raw_value_complex[1] + ), + }, + 2: { + MT_Z: lambda: sp.Matrix([Z(i) for i in self.raw_value_int2]), + MT_Q: lambda: sp.Matrix([Q(q[0], q[1]) for q in self.raw_value_rat2]), + MT_R: lambda: sp.Matrix([R(r) for r in self.raw_value_float2]), + MT_C: lambda: sp.Matrix( + [c[0] + sp.I * c[1] for c in self.raw_value_complex2] + ), + }, + 3: { + MT_Z: lambda: sp.Matrix([Z(i) for i in self.raw_value_int3]), + MT_Q: lambda: sp.Matrix([Q(q[0], q[1]) for q in self.raw_value_rat3]), + MT_R: lambda: sp.Matrix([R(r) for r in self.raw_value_float3]), + MT_C: lambda: sp.Matrix( + [c[0] + sp.I * c[1] for c in self.raw_value_complex3] + ), + }, + }[self.size][self.mathtype]() * (self.unit if self.unit is not None else 1) + + @value.setter + def value(self, expr: spux.SympyExpr) -> None: + """Set the expression defined by the socket. + + Notes: + Called to set the internal `FlowKind.Value` of this socket. + """ + mathtype, size, unit_dim = self._parse_expr_info(expr) + if self.symbols: + self.raw_value_spstr = sp.sstr(expr) + + else: + MT_Z = spux.MathType.Integer + MT_Q = spux.MathType.Rational + MT_R = spux.MathType.Real + MT_C = spux.MathType.Complex + if size is None: + if mathtype == MT_Z: + self.raw_value_int = self._to_raw_value(expr) + elif mathtype == MT_Q: + self.raw_value_rat = self._to_raw_value(expr) + elif mathtype == MT_R: + self.raw_value_float = self._to_raw_value(expr) + elif mathtype == MT_C: + self.raw_value_complex = self._to_raw_value(expr) + elif size == 2: + if mathtype == MT_Z: + self.raw_value_int2 = self._to_raw_value(expr) + elif mathtype == MT_Q: + self.raw_value_rat2 = self._to_raw_value(expr) + elif mathtype == MT_R: + self.raw_value_float2 = self._to_raw_value(expr) + elif mathtype == MT_C: + self.raw_value_complex2 = self._to_raw_value(expr) + elif size == 3: + if mathtype == MT_Z: + self.raw_value_int3 = self._to_raw_value(expr) + elif mathtype == MT_Q: + self.raw_value_rat3 = self._to_raw_value(expr) + elif mathtype == MT_R: + self.raw_value_float3 = self._to_raw_value(expr) + elif mathtype == MT_C: + self.raw_value_complex3 = self._to_raw_value(expr) + + #################### + # - FlowKind: LazyArrayRange + #################### + @property + def lazy_array_range(self) -> ct.LazyArrayRangeFlow: + """Return the not-yet-computed uniform array defined by the socket. + + Notes: + Called to compute the internal `FlowKind.LazyArrayRange` of this socket. + + Return: + The range of lengths, which uses no symbols. + """ + return ct.LazyArrayRangeFlow( + start=sp.S(self.min_value) * self.unit, + stop=sp.S(self.max_value) * self.unit, + steps=self.steps, + scaling='lin', + unit=self.unit, + ) + + @lazy_array_range.setter + def lazy_array_range(self, value: ct.LazyArrayRangeFlow) -> None: + """Set the not-yet-computed uniform array defined by the socket. + + Notes: + Called to compute the internal `FlowKind.LazyArrayRange` of this socket. + """ + self.min_value = spux.sympy_to_python( + spux.scale_to_unit(value.start * value.unit, self.unit) + ) + self.max_value = spux.sympy_to_python( + spux.scale_to_unit(value.stop * value.unit, self.unit) + ) + self.steps = value.steps + + #################### + # - FlowKind: LazyValueFunc + #################### + @property + def lazy_value_func(self) -> ct.LazyValueFuncFlow: + return ct.LazyValueFuncFlow( + func=sp.lambdify(self.symbols, self.value, 'jax'), + func_args=[spux.sympy_to_python_type(sym) for sym in self.symbols], + supports_jax=True, + ) + + #################### + # - FlowKind: Array + #################### + @property + def array(self) -> ct.ArrayFlow: + if not self.symbols: + return ct.ArrayFlow( + values=self.lazy_value_func.func_jax(), + unit=self.unit, + ) + + msg = "Expr socket can't produce array from expression with free symbols" + raise ValueError(msg) + + #################### + # - FlowKind: Info + #################### + @property + def info(self) -> ct.ArrayFlow: + return ct.InfoFlow( + output_name='_', ## TODO: Something else + output_shape=(self.size,) if self.size is not None else None, + output_mathtype=self.mathtype, + output_unit=self.unit, + ) + + #################### + # - FlowKind: Capabilities + #################### + @property + def capabilities(self) -> None: + return ct.CapabilitiesFlow( + socket_type=self.socket_type, + active_kind=self.active_kind, + ) + ## TODO: Prevent all invalid linkage between sockets used as expressions, but don't be too brutal :) + ## - This really is a killer feature. But we want to get it right. So we leave it as todo until we know exactly how to tailor CapabilitiesFlow to these needs. + + #################### + # - UI + #################### + def draw_value(self, col: bpy.types.UILayout) -> None: + # Property Interface + if self.symbols: + col.prop(self, self.blfields['raw_value_spstr'], text='') + + else: + MT_Z = spux.MathType.Integer + MT_Q = spux.MathType.Rational + MT_R = spux.MathType.Real + MT_C = spux.MathType.Complex + if self.size is None: + if self.mathtype == MT_Z: + col.prop(self, self.blfields['raw_value_int'], text='') + elif self.mathtype == MT_Q: + col.prop(self, self.blfields['raw_value_rat'], text='') + elif self.mathtype == MT_R: + col.prop(self, self.blfields['raw_value_float'], text='') + elif self.mathtype == MT_C: + col.prop(self, self.blfields['raw_value_complex'], text='') + elif self.size == 2: + if self.mathtype == MT_Z: + col.prop(self, self.blfields['raw_value_int2'], text='') + elif self.mathtype == MT_Q: + col.prop(self, self.blfields['raw_value_rat2'], text='') + elif self.mathtype == MT_R: + col.prop(self, self.blfields['raw_value_float2'], text='') + elif self.mathtype == MT_C: + col.prop(self, self.blfields['raw_value_complex2'], text='') + elif self.size == 3: + if self.mathtype == MT_Z: + col.prop(self, self.blfields['raw_value_int3'], text='') + elif self.mathtype == MT_Q: + col.prop(self, self.blfields['raw_value_rat3'], text='') + elif self.mathtype == MT_R: + col.prop(self, self.blfields['raw_value_float3'], text='') + elif self.mathtype == MT_C: + col.prop(self, self.blfields['raw_value_complex3'], text='') + + # Symbol Information + if self.symbols: + box = col.box() + split = box.split(factor=0.3) + + # Left Col + col = split.column() + col.label(text='Let:') + + # Right Col + col = split.column() + col.alignment = 'RIGHT' + for sym in self.symbols: + col.label(text=spux.pretty_symbol(sym)) + + def draw_input_label_row(self, row: bpy.types.UILayout, text) -> None: + info = self.compute_data(kind=ct.FlowKind.Info) + has_dims = not ct.FlowSignal.check(info) and info.dim_names + + if has_dims: + split = row.split(factor=0.85, align=True) + _row = split.row(align=False) + else: + _row = row + + _row.label(text=text) + if has_dims: + if self.show_info_columns: + _row.prop(self, self.blfields['info_columns']) + + _row = split.row(align=True) + _row.alignment = 'RIGHT' + _row.prop( + self, + self.blfields['show_info_columns'], + toggle=True, + text='', + icon=ct.Icon.ToggleSocketInfo, + ) + + def draw_output_label_row(self, row: bpy.types.UILayout, text) -> None: + info = self.compute_data(kind=ct.FlowKind.Info) + has_info = not ct.FlowSignal.check(info) + + if has_info: + split = row.split(factor=0.15, align=True) + + _row = split.row(align=True) + _row.prop( + self, + self.blfields['show_info_columns'], + toggle=True, + text='', + icon=ct.Icon.ToggleSocketInfo, + ) + + _row = split.row(align=False) + _row.alignment = 'RIGHT' + if self.show_info_columns: + _row.prop(self, self.blfields['info_columns']) + else: + _col = _row.column() + _col.alignment = 'EXPAND' + _col.label(text='') + else: + _row = row + + _row.label(text=text) + + def draw_info(self, info: ct.InfoFlow, col: bpy.types.UILayout) -> None: + if info.dim_names and self.show_info_columns: + row = col.row() + box = row.box() + grid = box.grid_flow( + columns=len(self.info_columns) + 1, + row_major=True, + even_columns=True, + # even_rows=True, + align=True, + ) + + # Dimensions + for dim_name in info.dim_names: + dim_idx = info.dim_idx[dim_name] + grid.label(text=dim_name) + if InfoDisplayCol.Length in self.info_columns: + grid.label(text=str(len(dim_idx))) + if InfoDisplayCol.MathType in self.info_columns: + grid.label(text=spux.MathType.to_str(dim_idx.mathtype)) + if InfoDisplayCol.Unit in self.info_columns: + grid.label(text=spux.sp_to_str(dim_idx.unit)) + + # Outputs + grid.label(text=info.output_name) + if InfoDisplayCol.Length in self.info_columns: + grid.label(text='', icon=ct.Icon.DataSocketOutput) + if InfoDisplayCol.MathType in self.info_columns: + grid.label( + text=( + spux.MathType.to_str(info.output_mathtype) + + ( + 'ˣ'.join( + [ + unicode_superscript(out_axis) + for out_axis in info.output_shape + ] + ) + if info.output_shape + else '' + ) + ) + ) + if InfoDisplayCol.Unit in self.info_columns: + grid.label(text=f'{spux.sp_to_str(info.output_unit)}') + + +#################### +# - Socket Configuration +#################### +class ExprSocketDef(base.SocketDef): + socket_type: ct.SocketType = ct.SocketType.Expr + active_kind: typ.Literal[ct.FlowKind.Value, ct.FlowKind.LazyArrayRange] = ( + ct.FlowKind.Value + ) + + # Properties + size: typ.Literal[None, 2, 3] = None + mathtype: spux.MathType = spux.MathType.Real + symbols: frozenset[spux.Symbol] = frozenset() + ## Units + unit_dim: spux.UnitDimension | None = None + ## Info Display + show_info_columns: bool = False + + ## TODO: Buncha validation :) + + # Defaults + default_unit: spux.Unit | None = None + default_value: spux.SympyExpr = sp.S(1) + default_min: spux.SympyExpr = sp.S(0) + default_max: spux.SympyExpr = sp.S(1) + default_steps: spux.SympyExpr = sp.S(2) + + def init(self, bl_socket: ExprBLSocket) -> None: + bl_socket.active_kind = self.active_kind + bl_socket.size = self.size + bl_socket.mathtype = self.size + bl_socket.symbols = self.symbols + bl_socket.unit_dim = self.size + bl_socket.unit = self.symbols + bl_socket.show_info_columns = self.show_info_columns + + bl_socket.value = self.default + + +#################### +# - Blender Registration +#################### +BL_REGISTER = [ + ExprBLSocket, +] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/__init__.py deleted file mode 100644 index 8df878d..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -from . import integer_number - -IntegerNumberSocketDef = integer_number.IntegerNumberSocketDef - -from . import rational_number - -RationalNumberSocketDef = rational_number.RationalNumberSocketDef - -from . import real_number - -RealNumberSocketDef = real_number.RealNumberSocketDef - -from . import complex_number - -ComplexNumberSocketDef = complex_number.ComplexNumberSocketDef - - -BL_REGISTER = [ - *integer_number.BL_REGISTER, - *rational_number.BL_REGISTER, - *real_number.BL_REGISTER, - *complex_number.BL_REGISTER, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/complex_number.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/complex_number.py deleted file mode 100644 index cc895a3..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/complex_number.py +++ /dev/null @@ -1,146 +0,0 @@ -import typing as typ - -import bpy -import sympy as sp - -from blender_maxwell.utils import extra_sympy_units as spux - -from ... import contracts as ct -from .. import base - - -#################### -# - Blender Socket -#################### -class ComplexNumberBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.ComplexNumber - bl_label = 'Complex Number' - - #################### - # - Properties - #################### - raw_value: bpy.props.FloatVectorProperty( - name='Complex Number', - description='Represents a complex number (real, imaginary)', - size=2, - default=(0.0, 0.0), - update=(lambda self, context: self.on_prop_changed('raw_value', context)), - ) - coord_sys: bpy.props.EnumProperty( - name='Coordinate System', - description='Choose between cartesian and polar form', - items=[ - ( - 'CARTESIAN', - 'Cartesian', - 'Use Cartesian Coordinates', - 'EMPTY_AXIS', - 0, - ), - ( - 'POLAR', - 'Polar', - 'Use Polar Coordinates', - 'DRIVER_ROTATIONAL_DIFFERENCE', - 1, - ), - ], - default='CARTESIAN', - update=lambda self, context: self.on_coord_sys_changed(context), - ) - - #################### - # - Event Methods - #################### - def on_coord_sys_changed(self, context: bpy.types.Context): - r"""Transforms values when the coordinate system changes. - - Notes: - Cartesian coordinates with $y=0$ has no corresponding $\theta$ - Therefore, we manually set $\theta=0$. - - """ - if self.coord_sys == 'CARTESIAN': - r, theta_rad = self.raw_value - self.raw_value = ( - r * sp.cos(theta_rad), - r * sp.sin(theta_rad), - ) - elif self.coord_sys == 'POLAR': - x, y = self.raw_value - cart_value = x + sp.I * y - self.raw_value = ( - float(sp.Abs(cart_value)), - float(sp.arg(cart_value)) if y != 0 else float(0), - ) - - self.on_prop_changed('coord_sys', context) - - #################### - # - Socket UI - #################### - def draw_value(self, col: bpy.types.UILayout) -> None: - """Draw the value of the complex number, including a toggle for specifying the active coordinate system.""" - # Value Row - row = col.row() - row.prop(self, 'raw_value', text='') - - # Coordinate System Dropdown - col.prop(self, 'coord_sys', text='') - - #################### - # - Computation of Default Value - #################### - @property - def value(self) -> spux.ComplexNumber: - """Return the complex number as a sympy expression, of a form determined by the coordinate system. - - - **Cartesian**: $(a,b) -> a + ib$ - - **Polar**: $(r,t) -> re^(it)$ - - Returns: - The complex number as a `sympy` type. - """ - v1, v2 = self.raw_value - - return { - 'CARTESIAN': v1 + sp.I * v2, - 'POLAR': v1 * sp.exp(sp.I * v2), - }[self.coord_sys] - - @value.setter - def value(self, value: spux.ComplexNumber) -> None: - """Set the complex number from a sympy expression, by numerically simplifying it into coordinate-system determined components. - - - **Cartesian**: $(a,b) -> a + ib$ - - **Polar**: $(r,t) -> re^(it)$ - - Parameters: - value: The complex number as a `sympy` type. - """ - self.raw_value = { - 'CARTESIAN': (float(sp.re(value)), float(sp.im(value))), - 'POLAR': (float(sp.Abs(value)), float(sp.arg(value))), - }[self.coord_sys] - - -#################### -# - Socket Configuration -#################### -class ComplexNumberSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.ComplexNumber - - default_value: spux.ComplexNumber = sp.S(0) - coord_sys: typ.Literal['CARTESIAN', 'POLAR'] = 'CARTESIAN' - - def init(self, bl_socket: ComplexNumberBLSocket) -> None: - bl_socket.value = self.default_value - bl_socket.coord_sys = self.coord_sys - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - ComplexNumberBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/integer_number.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/integer_number.py deleted file mode 100644 index daba926..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/integer_number.py +++ /dev/null @@ -1,58 +0,0 @@ -import bpy - -from ... import contracts as ct -from .. import base - - -#################### -# - Blender Socket -#################### -class IntegerNumberBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.IntegerNumber - bl_label = 'Integer Number' - - #################### - # - Properties - #################### - raw_value: bpy.props.IntProperty( - name='Integer', - description='Represents an integer', - default=0, - update=(lambda self, context: self.on_prop_changed('raw_value', context)), - ) - - #################### - # - Socket UI - #################### - def draw_value(self, col: bpy.types.UILayout) -> None: - col_row = col.row() - col_row.prop(self, 'raw_value', text='') - - #################### - # - Default Value - #################### - @property - def value(self) -> int: - return self.raw_value - - @value.setter - def value(self, value: int) -> None: - self.raw_value = value - - -#################### -# - Socket Configuration -#################### -class IntegerNumberSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.IntegerNumber - - default_value: int = 0 - - def init(self, bl_socket: IntegerNumberBLSocket) -> None: - bl_socket.value = self.default_value - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [IntegerNumberBLSocket] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/rational_number.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/rational_number.py deleted file mode 100644 index 2f44d05..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/rational_number.py +++ /dev/null @@ -1,72 +0,0 @@ -import bpy -import sympy as sp - -from blender_maxwell.utils.pydantic_sympy import SympyExpr - -from ... import contracts as ct -from .. import base - - -#################### -# - Blender Socket -#################### -class RationalNumberBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.RationalNumber - bl_label = 'Rational Number' - - #################### - # - Properties - #################### - raw_value: bpy.props.IntVectorProperty( - name='Rational Number', - description='Represents a rational number (int / int)', - size=2, - default=(1, 1), - subtype='NONE', - update=(lambda self, context: self.on_prop_changed('raw_value', context)), - ) - - #################### - # - UI - #################### - def draw_value(self, col: bpy.types.UILayout) -> None: - col_row = col.row(align=True) - col_row.prop(self, 'raw_value', text='') - - #################### - # - Default Value - #################### - @property - def value(self) -> sp.Rational: - p, q = self.raw_value - return sp.Rational(p, q) - - @value.setter - def value(self, value: float | tuple[int, int] | SympyExpr) -> None: - if isinstance(value, float): - approx_rational = sp.nsimplify(value) - self.raw_value = (approx_rational.p, approx_rational.q) - elif isinstance(value, tuple): - self.raw_value = value - else: - self.raw_value = (value.p, value.q) - - -#################### -# - Socket Configuration -#################### -class RationalNumberSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.RationalNumber - - default_value: SympyExpr = sp.Rational(0, 1) - - def init(self, bl_socket: RationalNumberBLSocket) -> None: - bl_socket.value = self.default_value - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - RationalNumberBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/real_number.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/real_number.py deleted file mode 100644 index b07edeb..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/real_number.py +++ /dev/null @@ -1,66 +0,0 @@ -import bpy - -from blender_maxwell.utils.pydantic_sympy import SympyExpr - -from ... import contracts as ct -from .. import base - - -#################### -# - Blender Socket -#################### -class RealNumberBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.RealNumber - bl_label = 'Real Number' - - #################### - # - Properties - #################### - raw_value: bpy.props.FloatProperty( - name='Real Number', - description='Represents a real number', - default=0.0, - precision=6, - update=(lambda self, context: self.on_prop_changed('raw_value', context)), - ) - - #################### - # - Socket UI - #################### - def draw_value(self, col: bpy.types.UILayout) -> None: - col_row = col.row() - col_row.prop(self, 'raw_value', text='') - - #################### - # - Computation of Default Value - #################### - @property - def value(self) -> float: - return self.raw_value - - @value.setter - def value(self, value: float | SympyExpr) -> None: - if isinstance(value, float): - self.raw_value = value - else: - float(value.n()) - - -#################### -# - Socket Configuration -#################### -class RealNumberSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.RealNumber - - default_value: float = 0.0 - - def init(self, bl_socket: RealNumberBLSocket) -> None: - bl_socket.value = self.default_value - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - RealNumberBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/__init__.py index 7cecef8..f7120bf 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/__init__.py @@ -1,61 +1,9 @@ -from . import unit_system - -PhysicalUnitSystemSocketDef = unit_system.PhysicalUnitSystemSocketDef - -from . import time - -PhysicalTimeSocketDef = time.PhysicalTimeSocketDef - -from . import angle - -PhysicalAngleSocketDef = angle.PhysicalAngleSocketDef - -from . import area, length, volume - -PhysicalLengthSocketDef = length.PhysicalLengthSocketDef -PhysicalAreaSocketDef = area.PhysicalAreaSocketDef -PhysicalVolumeSocketDef = volume.PhysicalVolumeSocketDef - -from . import point_3d - -PhysicalPoint3DSocketDef = point_3d.PhysicalPoint3DSocketDef - -from . import size_3d - -PhysicalSize3DSocketDef = size_3d.PhysicalSize3DSocketDef - -from . import mass - -PhysicalMassSocketDef = mass.PhysicalMassSocketDef - -from . import accel_scalar, force_scalar, speed - -PhysicalSpeedSocketDef = speed.PhysicalSpeedSocketDef -PhysicalAccelScalarSocketDef = accel_scalar.PhysicalAccelScalarSocketDef -PhysicalForceScalarSocketDef = force_scalar.PhysicalForceScalarSocketDef - -from . import pol +from . import pol, unit_system PhysicalPolSocketDef = pol.PhysicalPolSocketDef -from . import freq - -PhysicalFreqSocketDef = freq.PhysicalFreqSocketDef - BL_REGISTER = [ *unit_system.BL_REGISTER, - *time.BL_REGISTER, - *angle.BL_REGISTER, - *length.BL_REGISTER, - *area.BL_REGISTER, - *volume.BL_REGISTER, - *point_3d.BL_REGISTER, - *size_3d.BL_REGISTER, - *mass.BL_REGISTER, - *speed.BL_REGISTER, - *accel_scalar.BL_REGISTER, - *force_scalar.BL_REGISTER, *pol.BL_REGISTER, - *freq.BL_REGISTER, ] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/accel_scalar.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/accel_scalar.py deleted file mode 100644 index 9644bb4..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/accel_scalar.py +++ /dev/null @@ -1,65 +0,0 @@ -import bpy -import sympy.physics.units as spu - -from blender_maxwell.utils.pydantic_sympy import SympyExpr - -from ... import contracts as ct -from .. import base - - -#################### -# - Blender Socket -#################### -class PhysicalAccelScalarBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.PhysicalAccelScalar - bl_label = 'Accel Scalar' - use_units = True - - #################### - # - Properties - #################### - raw_value: bpy.props.FloatProperty( - name='Unitless Acceleration', - description='Represents the unitless part of the acceleration', - default=0.0, - precision=6, - update=(lambda self, context: self.on_prop_changed('raw_value', context)), - ) - - #################### - # - Socket UI - #################### - def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, 'raw_value', text='') - - #################### - # - Default Value - #################### - @property - def value(self) -> SympyExpr: - return self.raw_value * self.unit - - @value.setter - def value(self, value: SympyExpr) -> None: - self.raw_value = spu.convert_to(value, self.unit) / self.unit - - -#################### -# - Socket Configuration -#################### -class PhysicalAccelScalarSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.PhysicalAccelScalar - - default_unit: SympyExpr | None = None - - def init(self, bl_socket: PhysicalAccelScalarBLSocket) -> None: - if self.default_unit: - bl_socket.unit = self.default_unit - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - PhysicalAccelScalarBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/angle.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/angle.py deleted file mode 100644 index dd66336..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/angle.py +++ /dev/null @@ -1,65 +0,0 @@ -import bpy -import sympy.physics.units as spu - -from blender_maxwell.utils.pydantic_sympy import SympyExpr - -from ... import contracts as ct -from .. import base - - -#################### -# - Blender Socket -#################### -class PhysicalAngleBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.PhysicalAngle - bl_label = 'Physical Angle' - use_units = True - - #################### - # - Properties - #################### - raw_value: bpy.props.FloatProperty( - name='Unitless Acceleration', - description='Represents the unitless part of the acceleration', - default=0.0, - precision=4, - update=(lambda self, context: self.on_prop_changed('raw_value', context)), - ) - - #################### - # - Socket UI - #################### - def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, 'raw_value', text='') - - #################### - # - Default Value - #################### - @property - def value(self) -> SympyExpr: - return self.raw_value * self.unit - - @value.setter - def value(self, value: SympyExpr) -> None: - self.raw_value = spu.convert_to(value, self.unit) / self.unit - - -#################### -# - Socket Configuration -#################### -class PhysicalAngleSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.PhysicalAngle - - default_unit: SympyExpr | None = None - - def init(self, bl_socket: PhysicalAngleBLSocket) -> None: - if self.default_unit: - bl_socket.unit = self.default_unit - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - PhysicalAngleBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/area.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/area.py deleted file mode 100644 index 013c9e1..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/area.py +++ /dev/null @@ -1,72 +0,0 @@ -import typing as typ - -import bpy -import sympy as sp - -from ... import contracts as ct -from .. import base - - -class PhysicalAreaBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.PhysicalArea - bl_label = 'Physical Area' - use_units = True - - #################### - # - Properties - #################### - raw_value: bpy.props.FloatProperty( - name='Unitless Area', - description='Represents the unitless part of the area', - default=0.0, - precision=6, - update=(lambda self, context: self.on_prop_changed('raw_value', context)), - ) - - #################### - # - Socket UI - #################### - def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, 'raw_value', text='') - - #################### - # - Computation of Default Value - #################### - @property - def default_value(self) -> sp.Expr: - """Return the area as a sympy expression, which is a pure real - number perfectly expressed as the active unit. - - Returns: - The area as a sympy expression (with units). - """ - return self.raw_value * self.unit - - @default_value.setter - def default_value(self, value: typ.Any) -> None: - """Set the area from a sympy expression, including any required - unit conversions to normalize the input value to the selected - units. - """ - self.raw_value = self.value_as_unit(value) - - -#################### -# - Socket Configuration -#################### -class PhysicalAreaSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.PhysicalArea - - default_unit: typ.Any | None = None - - def init(self, bl_socket: PhysicalAreaBLSocket) -> None: - if self.default_unit: - bl_socket.unit = self.default_unit - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - PhysicalAreaBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/force_scalar.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/force_scalar.py deleted file mode 100644 index f33b79b..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/force_scalar.py +++ /dev/null @@ -1,65 +0,0 @@ -import bpy -import sympy.physics.units as spu - -from blender_maxwell.utils.pydantic_sympy import SympyExpr - -from ... import contracts as ct -from .. import base - - -#################### -# - Blender Socket -#################### -class PhysicalForceScalarBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.PhysicalForceScalar - bl_label = 'Force Scalar' - use_units = True - - #################### - # - Properties - #################### - raw_value: bpy.props.FloatProperty( - name='Unitless Force', - description='Represents the unitless part of the force', - default=0.0, - precision=6, - update=(lambda self, context: self.on_prop_changed('raw_value', context)), - ) - - #################### - # - Socket UI - #################### - def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, 'raw_value', text='') - - #################### - # - Default Value - #################### - @property - def value(self) -> SympyExpr: - return self.raw_value * self.unit - - @value.setter - def value(self, value: SympyExpr) -> None: - self.raw_value = spu.convert_to(value, self.unit) / self.unit - - -#################### -# - Socket Configuration -#################### -class PhysicalForceScalarSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.PhysicalForceScalar - - default_unit: SympyExpr | None = None - - def init(self, bl_socket: PhysicalForceScalarBLSocket) -> None: - if self.default_unit: - bl_socket.unit = self.default_unit - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - PhysicalForceScalarBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/freq.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/freq.py deleted file mode 100644 index 625a7a2..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/freq.py +++ /dev/null @@ -1,126 +0,0 @@ -import bpy -import sympy as sp - -from blender_maxwell.utils import extra_sympy_units as spux -from blender_maxwell.utils import logger -from blender_maxwell.utils.pydantic_sympy import SympyExpr - -from ... import contracts as ct -from .. import base - -log = logger.get(__name__) - - -#################### -# - Blender Socket -#################### -class PhysicalFreqBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.PhysicalFreq - bl_label = 'Frequency' - use_units = True - - #################### - # - Properties - #################### - raw_value: bpy.props.FloatProperty( - name='Unitless Frequency', - description='Represents the unitless part of the frequency', - default=0.0, - precision=6, - update=(lambda self, context: self.on_prop_changed('raw_value', context)), - ) - - min_freq: bpy.props.FloatProperty( - name='Min Frequency', - description='Lowest frequency', - default=0.0, - precision=4, - update=(lambda self, context: self.on_prop_changed('min_freq', context)), - ) - max_freq: bpy.props.FloatProperty( - name='Max Frequency', - description='Highest frequency', - default=0.0, - precision=4, - update=(lambda self, context: self.on_prop_changed('max_freq', context)), - ) - steps: bpy.props.IntProperty( - name='Frequency Steps', - description='# of steps between min and max', - default=2, - update=(lambda self, context: self.on_prop_changed('steps', context)), - ) - - #################### - # - Socket UI - #################### - def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, 'raw_value', text='') - - def draw_lazy_value_range(self, col: bpy.types.UILayout) -> None: - col.prop(self, 'min_freq', text='Min') - col.prop(self, 'max_freq', text='Max') - col.prop(self, 'steps', text='Steps') - - #################### - # - Default Value - #################### - @property - def value(self) -> SympyExpr: - return self.raw_value * self.unit - - @value.setter - def value(self, value: SympyExpr) -> None: - self.raw_value = spux.sympy_to_python(spux.scale_to_unit(value, self.unit)) - - @property - def lazy_array_range(self) -> ct.LazyArrayRangeFlow: - return ct.LazyArrayRangeFlow( - symbols=set(), - unit=self.unit, - start=sp.S(self.min_freq) * self.unit, - stop=sp.S(self.max_freq) * self.unit, - steps=self.steps, - scaling='lin', - ) - - @lazy_array_range.setter - def lazy_array_range(self, value: ct.LazyArrayRangeFlow) -> None: - self.min_freq = spux.sympy_to_python( - spux.scale_to_unit(value.start * value.unit, self.unit) - ) - self.max_freq = spux.sympy_to_python( - spux.scale_to_unit(value.stop * value.unit, self.unit) - ) - self.steps = value.steps - - -#################### -# - Socket Configuration -#################### -class PhysicalFreqSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.PhysicalFreq - is_array: bool = False - - default_value: SympyExpr = 500 * spux.terahertz - default_unit: SympyExpr = spux.terahertz - - min_freq: SympyExpr = 400.0 * spux.terahertz - max_freq: SympyExpr = 600.0 * spux.terahertz - steps: int = 50 - - def init(self, bl_socket: PhysicalFreqBLSocket) -> None: - bl_socket.unit = self.default_unit - - bl_socket.value = self.default_value - if self.is_array: - bl_socket.active_kind = ct.FlowKind.LazyArrayRange - bl_socket.lazy_value_range = (self.min_freq, self.max_freq, self.steps) - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - PhysicalFreqBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/length.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/length.py deleted file mode 100644 index 7008460..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/length.py +++ /dev/null @@ -1,128 +0,0 @@ -import bpy -import sympy as sp -import sympy.physics.units as spu - -from blender_maxwell.utils import extra_sympy_units as spux -from blender_maxwell.utils import logger -from blender_maxwell.utils.pydantic_sympy import SympyExpr - -from ... import contracts as ct -from .. import base - -log = logger.get(__name__) - - -#################### -# - Blender Socket -#################### -class PhysicalLengthBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.PhysicalLength - bl_label = 'PhysicalLength' - use_units = True - - #################### - # - Properties - #################### - raw_value: bpy.props.FloatProperty( - name='Unitless Length', - description='Represents the unitless part of the length', - default=0.0, - precision=6, - update=(lambda self, context: self.on_prop_changed('raw_value', context)), - ) - - min_len: bpy.props.FloatProperty( - name='Min Length', - description='Lowest length', - default=0.0, - precision=4, - update=(lambda self, context: self.on_prop_changed('min_len', context)), - ) - max_len: bpy.props.FloatProperty( - name='Max Length', - description='Highest length', - default=0.0, - precision=4, - update=(lambda self, context: self.on_prop_changed('max_len', context)), - ) - steps: bpy.props.IntProperty( - name='Length Steps', - description='# of steps between min and max', - default=2, - update=(lambda self, context: self.on_prop_changed('steps', context)), - ) - - #################### - # - Socket UI - #################### - def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, 'raw_value', text='') - - def draw_lazy_value_range(self, col: bpy.types.UILayout) -> None: - col.prop(self, 'min_len', text='Min') - col.prop(self, 'max_len', text='Max') - col.prop(self, 'steps', text='Steps') - - #################### - # - Default Value - #################### - @property - def value(self) -> SympyExpr: - return self.raw_value * self.unit - - @value.setter - def value(self, value: SympyExpr) -> None: - self.raw_value = spux.sympy_to_python(spux.scale_to_unit(value, self.unit)) - - @property - def lazy_array_range(self) -> ct.LazyArrayRangeFlow: - return ct.LazyArrayRangeFlow( - symbols=set(), - unit=self.unit, - start=sp.S(self.min_len) * self.unit, - stop=sp.S(self.max_len) * self.unit, - steps=self.steps, - scaling='lin', - ) - - @lazy_array_range.setter - def lazy_array_range(self, value: ct.LazyArrayRangeFlow) -> None: - self.min_len = spux.sympy_to_python( - spux.scale_to_unit(value.start * value.unit, self.unit) - ) - self.max_len = spux.sympy_to_python( - spux.scale_to_unit(value.stop * value.unit, self.unit) - ) - self.steps = value.steps - - -#################### -# - Socket Configuration -#################### -class PhysicalLengthSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.PhysicalLength - is_array: bool = False - - default_value: SympyExpr = 1 * spu.um - default_unit: SympyExpr | None = None - - min_len: SympyExpr = 400.0 * spu.nm - max_len: SympyExpr = 700.0 * spu.nm - steps: SympyExpr = 50 - - def init(self, bl_socket: PhysicalLengthBLSocket) -> None: - if self.default_unit: - bl_socket.unit = self.default_unit - - bl_socket.value = self.default_value - if self.is_array: - bl_socket.active_kind = ct.FlowKind.LazyArrayRange - bl_socket.lazy_value_range = (self.min_len, self.max_len, self.steps) - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - PhysicalLengthBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/mass.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/mass.py deleted file mode 100644 index 2d10b0c..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/mass.py +++ /dev/null @@ -1,64 +0,0 @@ -import bpy -import sympy.physics.units as spu - -from blender_maxwell.utils.pydantic_sympy import SympyExpr - -from ... import contracts as ct -from .. import base - - -#################### -# - Blender Socket -#################### -class PhysicalMassBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.PhysicalMass - bl_label = 'Mass' - use_units = True - - #################### - # - Properties - #################### - raw_value: bpy.props.FloatProperty( - name='Unitless Mass', - description='Represents the unitless part of mass', - default=0.0, - precision=6, - update=(lambda self, context: self.on_prop_changed('raw_value', context)), - ) - - #################### - # - Socket UI - #################### - def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, 'raw_value', text='') - - #################### - # - Default Value - #################### - @property - def value(self) -> SympyExpr: - return self.raw_value * self.unit - - @value.setter - def value(self, value: SympyExpr) -> None: - self.raw_value = spu.convert_to(value, self.unit) / self.unit - - -#################### -# - Socket Configuration -#################### -class PhysicalMassSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.PhysicalMass - - default_unit: SympyExpr | None = None - - def init(self, bl_socket: PhysicalMassBLSocket) -> None: - pass - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - PhysicalMassBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/point_3d.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/point_3d.py deleted file mode 100644 index 12945ce..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/point_3d.py +++ /dev/null @@ -1,66 +0,0 @@ -import typing as typ - -import bpy -import sympy as sp -import sympy.physics.units as spu - -from blender_maxwell.utils.pydantic_sympy import SympyExpr - -from ... import contracts as ct -from .. import base - - -class PhysicalPoint3DBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.PhysicalPoint3D - bl_label = 'Volume' - use_units = True - - #################### - # - Properties - #################### - raw_value: bpy.props.FloatVectorProperty( - name='Unitless 3D Point (global coordinate system)', - description='Represents the unitless part of the 3D point', - size=3, - default=(0.0, 0.0, 0.0), - precision=4, - update=(lambda self, context: self.on_prop_changed('raw_value', context)), - ) - - #################### - # - Socket UI - #################### - def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, 'raw_value', text='') - - #################### - # - Default Value - #################### - @property - def value(self) -> sp.MatrixBase: - return sp.Matrix(tuple(self.raw_value)) * self.unit - - @value.setter - def value(self, value: SympyExpr) -> None: - self.raw_value = tuple(spu.convert_to(value, self.unit) / self.unit) - - -#################### -# - Socket Configuration -#################### -class PhysicalPoint3DSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.PhysicalPoint3D - - default_unit: typ.Any | None = None - - def init(self, bl_socket: PhysicalPoint3DBLSocket) -> None: - if self.default_unit: - bl_socket.unit = self.default_unit - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - PhysicalPoint3DBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/size_3d.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/size_3d.py deleted file mode 100644 index bd65505..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/size_3d.py +++ /dev/null @@ -1,66 +0,0 @@ -import bpy -import sympy as sp -import sympy.physics.units as spu - -from blender_maxwell.utils.pydantic_sympy import SympyExpr - -from ... import contracts as ct -from .. import base - - -class PhysicalSize3DBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.PhysicalSize3D - bl_label = '3D Size' - use_units = True - - #################### - # - Properties - #################### - raw_value: bpy.props.FloatVectorProperty( - name='Unitless 3D Size', - description='Represents the unitless part of the 3D size', - size=3, - default=(1.0, 1.0, 1.0), - precision=4, - update=(lambda self, context: self.on_prop_changed('raw_value', context)), - ) - - #################### - # - Socket UI - #################### - def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, 'raw_value', text='') - - #################### - # - Computation of Default Value - #################### - @property - def value(self) -> SympyExpr: - return sp.Matrix(tuple(self.raw_value)) * self.unit - - @value.setter - def value(self, value: SympyExpr) -> None: - self.raw_value = tuple(spu.convert_to(value, self.unit) / self.unit) - - -#################### -# - Socket Configuration -#################### -class PhysicalSize3DSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.PhysicalSize3D - - default_value: SympyExpr = sp.Matrix([1, 1, 1]) * spu.um - default_unit: SympyExpr | None = None - - def init(self, bl_socket: PhysicalSize3DBLSocket) -> None: - bl_socket.value = self.default_value - if self.default_unit: - bl_socket.unit = self.default_unit - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - PhysicalSize3DBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/speed.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/speed.py deleted file mode 100644 index f87e65d..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/speed.py +++ /dev/null @@ -1,65 +0,0 @@ -import bpy -import sympy.physics.units as spu - -from blender_maxwell.utils.pydantic_sympy import SympyExpr - -from ... import contracts as ct -from .. import base - - -#################### -# - Blender Socket -#################### -class PhysicalSpeedBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.PhysicalSpeed - bl_label = 'Speed' - use_units = True - - #################### - # - Properties - #################### - raw_value: bpy.props.FloatProperty( - name='Unitless Speed', - description='Represents the unitless part of the speed', - default=0.0, - precision=6, - update=(lambda self, context: self.on_prop_changed('raw_value', context)), - ) - - #################### - # - Socket UI - #################### - def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, 'raw_value', text='') - - #################### - # - Default Value - #################### - @property - def value(self) -> SympyExpr: - return self.raw_value * self.unit - - @value.setter - def value(self, value: SympyExpr) -> None: - self.raw_value = spu.convert_to(value, self.unit) / self.unit - - -#################### -# - Socket Configuration -#################### -class PhysicalSpeedSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.PhysicalSpeed - - default_unit: SympyExpr | None = None - - def init(self, bl_socket: PhysicalSpeedBLSocket) -> None: - if self.default_unit: - bl_socket.unit = self.default_unit - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - PhysicalSpeedBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/time.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/time.py deleted file mode 100644 index d555335..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/time.py +++ /dev/null @@ -1,70 +0,0 @@ -import typing as typ - -import bpy -import sympy.physics.units as spu - -from blender_maxwell.utils.pydantic_sympy import SympyExpr - -from ... import contracts as ct -from .. import base - - -#################### -# - Blender Socket -#################### -class PhysicalTimeBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.PhysicalTime - bl_label = 'Time' - use_units = True - - #################### - # - Properties - #################### - raw_value: bpy.props.FloatProperty( - name='Unitless Time', - description='Represents the unitless part of time', - default=0.0, - precision=4, - update=(lambda self, context: self.on_prop_changed('raw_value', context)), - ) - - #################### - # - Socket UI - #################### - def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, 'raw_value', text='') - - #################### - # - Default Value - #################### - @property - def value(self) -> SympyExpr: - return self.raw_value * self.unit - - @value.setter - def value(self, value: SympyExpr) -> None: - self.raw_value = spu.convert_to(value, self.unit) / self.unit - - -#################### -# - Socket Configuration -#################### -class PhysicalTimeSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.PhysicalTime - - default_value: SympyExpr | None = None - default_unit: typ.Any | None = None - - def init(self, bl_socket: PhysicalTimeBLSocket) -> None: - if self.default_value: - bl_socket.value = self.default_value - if self.default_unit: - bl_socket.unit = self.default_unit - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - PhysicalTimeBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/volume.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/volume.py deleted file mode 100644 index 35f93b1..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/volume.py +++ /dev/null @@ -1,62 +0,0 @@ -import bpy -import sympy.physics.units as spu - -from blender_maxwell.utils.pydantic_sympy import SympyExpr - -from ... import contracts as ct -from .. import base - - -class PhysicalVolumeBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.PhysicalVolume - bl_label = 'Volume' - use_units = True - - #################### - # - Properties - #################### - raw_value: bpy.props.FloatProperty( - name='Unitless Volume', - description='Represents the unitless part of the area', - default=0.0, - precision=6, - update=(lambda self, context: self.on_prop_changed('raw_value', context)), - ) - - #################### - # - Socket UI - #################### - def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, 'raw_value', text='') - - #################### - # - Default Value - #################### - @property - def value(self) -> SympyExpr: - return self.raw_value * self.unit - - @value.setter - def value(self, value: SympyExpr) -> None: - self.raw_value = spu.convert_to(value, self.unit) / self.unit - - -#################### -# - Socket Configuration -#################### -class PhysicalVolumeSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.PhysicalVolume - - default_unit: SympyExpr | None = None - - def init(self, bl_socket: PhysicalVolumeBLSocket) -> None: - if self.default_unit: - bl_socket.unit = self.default_unit - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - PhysicalVolumeBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/__init__.py deleted file mode 100644 index 4011a7f..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -from . import complex_2d_vector, real_2d_vector - -Real2DVectorSocketDef = real_2d_vector.Real2DVectorSocketDef -Complex2DVectorSocketDef = complex_2d_vector.Complex2DVectorSocketDef - -from . import complex_3d_vector, integer_3d_vector, real_3d_vector - -Integer3DVectorSocketDef = integer_3d_vector.Integer3DVectorSocketDef -Real3DVectorSocketDef = real_3d_vector.Real3DVectorSocketDef -Complex3DVectorSocketDef = complex_3d_vector.Complex3DVectorSocketDef - - -BL_REGISTER = [ - *real_2d_vector.BL_REGISTER, - *complex_2d_vector.BL_REGISTER, - *integer_3d_vector.BL_REGISTER, - *real_3d_vector.BL_REGISTER, - *complex_3d_vector.BL_REGISTER, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_2d_vector.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_2d_vector.py deleted file mode 100644 index 7424ac2..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_2d_vector.py +++ /dev/null @@ -1,29 +0,0 @@ - -from ... import contracts as ct -from .. import base - - -#################### -# - Blender Socket -#################### -class Complex2DVectorBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.Complex2DVector - bl_label = 'Complex 2D Vector' - - -#################### -# - Socket Configuration -#################### -class Complex2DVectorSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.Complex2DVector - - def init(self, bl_socket: Complex2DVectorBLSocket) -> None: - pass - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - Complex2DVectorBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_3d_vector.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_3d_vector.py deleted file mode 100644 index ebb1cb4..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_3d_vector.py +++ /dev/null @@ -1,29 +0,0 @@ - -from ... import contracts as ct -from .. import base - - -#################### -# - Blender Socket -#################### -class Complex3DVectorBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.Complex3DVector - bl_label = 'Complex 3D Vector' - - -#################### -# - Socket Configuration -#################### -class Complex3DVectorSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.Complex3DVector - - def init(self, bl_socket: Complex3DVectorBLSocket) -> None: - pass - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - Complex3DVectorBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/integer_3d_vector.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/integer_3d_vector.py deleted file mode 100644 index 04e83f4..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/integer_3d_vector.py +++ /dev/null @@ -1,71 +0,0 @@ -import bpy -import sympy as sp - -from blender_maxwell.utils.pydantic_sympy import ConstrSympyExpr - -from ... import contracts as ct -from .. import base - -Integer3DVector = ConstrSympyExpr( - allow_variables=False, - allow_units=False, - allowed_sets={'integer'}, - allowed_structures={'matrix'}, - allowed_matrix_shapes={(3, 1)}, -) - - -#################### -# - Blender Socket -#################### -class Integer3DVectorBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.Integer3DVector - bl_label = 'Integer 3D Vector' - - #################### - # - Properties - #################### - raw_value: bpy.props.IntVectorProperty( - name='Int 3D Vector', - description='Represents an integer 3D (coordinate) vector', - size=3, - default=(0, 0, 0), - update=(lambda self, context: self.on_prop_changed('raw_value', context)), - ) - - #################### - # - Socket UI - #################### - def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, 'raw_value', text='') - - #################### - # - Computation of Default Value - #################### - @property - def value(self) -> Integer3DVector: - return sp.Matrix(tuple(self.raw_value)) - - @value.setter - def value(self, value: Integer3DVector) -> None: - self.raw_value = tuple(int(el) for el in value) - - -#################### -# - Socket Configuration -#################### -class Integer3DVectorSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.Integer3DVector - - default_value: Integer3DVector = sp.Matrix([0, 0, 0]) - - def init(self, bl_socket: Integer3DVectorBLSocket) -> None: - bl_socket.value = self.default_value - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - Integer3DVectorBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_2d_vector.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_2d_vector.py deleted file mode 100644 index 7342508..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_2d_vector.py +++ /dev/null @@ -1,72 +0,0 @@ -import bpy -import sympy as sp - -from blender_maxwell.utils.pydantic_sympy import ConstrSympyExpr - -from ... import contracts as ct -from .. import base - -Real2DVector = ConstrSympyExpr( - allow_variables=False, - allow_units=False, - allowed_sets={'integer', 'rational', 'real'}, - allowed_structures={'matrix'}, - allowed_matrix_shapes={(2, 1)}, -) - - -#################### -# - Blender Socket -#################### -class Real2DVectorBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.Real2DVector - bl_label = 'Real2DVector' - - #################### - # - Properties - #################### - raw_value: bpy.props.FloatVectorProperty( - name='Unitless 2D Vector (global coordinate system)', - description='Represents a real 2D (coordinate) vector', - size=2, - default=(0.0, 0.0), - precision=4, - update=(lambda self, context: self.on_prop_changed('raw_value', context)), - ) - - #################### - # - Socket UI - #################### - def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, 'raw_value', text='') - - #################### - # - Computation of Default Value - #################### - @property - def value(self) -> Real2DVector: - return sp.Matrix(tuple(self.raw_value)) - - @value.setter - def value(self, value: Real2DVector) -> None: - self.raw_value = tuple(value) - - -#################### -# - Socket Configuration -#################### -class Real2DVectorSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.Real2DVector - - default_value: Real2DVector = sp.Matrix([0.0, 0.0]) - - def init(self, bl_socket: Real2DVectorBLSocket) -> None: - bl_socket.value = self.default_value - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - Real2DVectorBLSocket, -] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_3d_vector.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_3d_vector.py deleted file mode 100644 index e789e00..0000000 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_3d_vector.py +++ /dev/null @@ -1,64 +0,0 @@ -import bpy -import sympy as sp - -import blender_maxwell.utils.extra_sympy_units as spux - -from ... import contracts as ct -from .. import base - - -#################### -# - Blender Socket -#################### -class Real3DVectorBLSocket(base.MaxwellSimSocket): - socket_type = ct.SocketType.Real3DVector - bl_label = 'Real 3D Vector' - - #################### - # - Properties - #################### - raw_value: bpy.props.FloatVectorProperty( - name='Real 3D Vector', - description='Represents a real 3D (coordinate) vector', - size=3, - default=(0.0, 0.0, 0.0), - precision=4, - update=(lambda self, context: self.on_prop_changed('raw_value', context)), - ) - - #################### - # - Socket UI - #################### - def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, 'raw_value', text='') - - #################### - # - Computation of Default Value - #################### - @property - def value(self) -> spux.Real3DVector: - return sp.Matrix(tuple(self.raw_value)) - - @value.setter - def value(self, value: spux.Real3DVector) -> None: - self.raw_value = tuple(value) - - -#################### -# - Socket Configuration -#################### -class Real3DVectorSocketDef(base.SocketDef): - socket_type: ct.SocketType = ct.SocketType.Real3DVector - - default_value: spux.Real3DVector = sp.Matrix([0.0, 0.0, 0.0]) - - def init(self, bl_socket: Real3DVectorBLSocket) -> None: - bl_socket.value = self.default_value - - -#################### -# - Blender Registration -#################### -BL_REGISTER = [ - Real3DVectorBLSocket, -] diff --git a/src/blender_maxwell/utils/extra_sympy_units.py b/src/blender_maxwell/utils/extra_sympy_units.py index 78d57e9..a127f80 100644 --- a/src/blender_maxwell/utils/extra_sympy_units.py +++ b/src/blender_maxwell/utils/extra_sympy_units.py @@ -14,7 +14,6 @@ import enum import itertools import typing as typ -import jax.numpy as jnp import pydantic as pyd import sympy as sp import sympy.physics.units as spu @@ -25,6 +24,8 @@ SympyType = sp.Basic | sp.Expr | sp.MatrixBase | sp.MutableDenseMatrix | spu.Qua class MathType(enum.StrEnum): + """Set identities encompassing common mathematical objects.""" + Bool = enum.auto() Integer = enum.auto() Rational = enum.auto() @@ -49,7 +50,9 @@ class MathType(enum.StrEnum): return MathType.Bool if sp_obj.is_integer: return MathType.Integer - if sp_obj.is_rational or sp_obj.is_real: + if sp_obj.is_rational: + return MathType.Rational + if sp_obj.is_real: return MathType.Real if sp_obj.is_complex: return MathType.Complex @@ -580,6 +583,7 @@ Symbol: typ.TypeAlias = IntSymbol | RealSymbol | ComplexSymbol # Unit ## Technically a "unit expression", which includes compound types. ## Support for this is the killer feature compared to spu.Quantity. +UnitDimension: typ.TypeAlias = spu.Dimension Unit: typ.TypeAlias = ConstrSympyExpr( allow_variables=False, allow_units=True,