refactor: Big breakthrough on Expr socket (non working)
parent
d8170a67e4
commit
80d7b21c34
|
@ -96,10 +96,6 @@ class CapabilitiesFlow:
|
||||||
for name in self.must_match
|
for name in self.must_match
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return (
|
|
||||||
self.socket_type == other.socket_type
|
|
||||||
and self.active_kind == other.active_kind
|
|
||||||
) or other.is_universal
|
|
||||||
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
|
"""Implements `WaveConstantNode`."""
|
||||||
|
|
||||||
import typing as typ
|
import typing as typ
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import sympy as sp
|
import sympy as sp
|
||||||
import sympy.physics.units as spu
|
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 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 contracts as ct
|
||||||
from ... import sockets
|
from ... import sockets
|
||||||
|
@ -16,84 +17,158 @@ log = logger.get(__name__)
|
||||||
|
|
||||||
|
|
||||||
class WaveConstantNode(base.MaxwellSimNode):
|
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
|
node_type = ct.NodeType.WaveConstant
|
||||||
bl_label = 'Wave Constant'
|
bl_label = 'Wave Constant'
|
||||||
|
|
||||||
input_socket_sets: typ.ClassVar = {
|
input_socket_sets: typ.ClassVar = {
|
||||||
'Wavelength': {},
|
'Wavelength': {
|
||||||
'Frequency': {},
|
'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',
|
# - Properties
|
||||||
description='Whether to use a wavelength/frequency range',
|
####################
|
||||||
default=False,
|
use_range: bool = bl_cache.BLField(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)
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# - 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(
|
@events.computes_output_socket(
|
||||||
'WL',
|
'WL',
|
||||||
kind=ct.FlowKind.Value,
|
kind=ct.FlowKind.Value,
|
||||||
# Data
|
|
||||||
input_sockets={'WL', 'Freq'},
|
input_sockets={'WL', 'Freq'},
|
||||||
input_sockets_optional={'WL': True, 'Freq': True},
|
input_sockets_optional={'WL': True, 'Freq': True},
|
||||||
)
|
)
|
||||||
def compute_wl_value(self, input_sockets: dict) -> sp.Expr:
|
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:
|
if input_sockets['WL'] is not None:
|
||||||
return input_sockets['WL']
|
return input_sockets['WL']
|
||||||
|
|
||||||
if input_sockets['WL'] is None and input_sockets['Freq'] is None:
|
return sci_constants.vac_speed_of_light / input_sockets['Freq']
|
||||||
msg = 'Both WL and Freq are None.'
|
|
||||||
raise RuntimeError(msg)
|
|
||||||
|
|
||||||
return constants.vac_speed_of_light / input_sockets['Freq']
|
|
||||||
|
|
||||||
@events.computes_output_socket(
|
@events.computes_output_socket(
|
||||||
'Freq',
|
'Freq',
|
||||||
kind=ct.FlowKind.Value,
|
kind=ct.FlowKind.Value,
|
||||||
# Data
|
|
||||||
input_sockets={'WL', 'Freq'},
|
input_sockets={'WL', 'Freq'},
|
||||||
input_sockets_optional={'WL': True, 'Freq': True},
|
input_sockets_optional={'WL': True, 'Freq': True},
|
||||||
)
|
)
|
||||||
def compute_freq_value(self, input_sockets: dict) -> sp.Expr:
|
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:
|
if input_sockets['Freq'] is not None:
|
||||||
return input_sockets['Freq']
|
return input_sockets['Freq']
|
||||||
|
|
||||||
if input_sockets['WL'] is None and input_sockets['Freq'] is None:
|
return sci_constants.vac_speed_of_light / input_sockets['WL']
|
||||||
msg = 'Both WL and Freq are None.'
|
|
||||||
raise RuntimeError(msg)
|
|
||||||
|
|
||||||
return constants.vac_speed_of_light / input_sockets['WL']
|
|
||||||
|
|
||||||
@events.computes_output_socket(
|
@events.computes_output_socket(
|
||||||
'WL',
|
'WL',
|
||||||
kind=ct.FlowKind.LazyArrayRange,
|
kind=ct.FlowKind.LazyArrayRange,
|
||||||
# Data
|
|
||||||
input_sockets={'WL', 'Freq'},
|
input_sockets={'WL', 'Freq'},
|
||||||
|
input_socket_kinds={
|
||||||
|
'WL': ct.FlowKind.LazyArrayRange,
|
||||||
|
'Freq': ct.FlowKind.LazyArrayRange,
|
||||||
|
},
|
||||||
input_sockets_optional={'WL': True, 'Freq': True},
|
input_sockets_optional={'WL': True, 'Freq': True},
|
||||||
)
|
)
|
||||||
def compute_wl_range(self, input_sockets: dict) -> sp.Expr:
|
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:
|
if input_sockets['WL'] is not None:
|
||||||
return input_sockets['WL']
|
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(
|
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(
|
@events.computes_output_socket(
|
||||||
'Freq',
|
'Freq',
|
||||||
kind=ct.FlowKind.LazyArrayRange,
|
kind=ct.FlowKind.LazyArrayRange,
|
||||||
# Data
|
|
||||||
input_sockets={'WL', 'Freq'},
|
input_sockets={'WL', 'Freq'},
|
||||||
input_socket_kinds={
|
input_socket_kinds={
|
||||||
'WL': ct.FlowKind.LazyArrayRange,
|
'WL': ct.FlowKind.LazyArrayRange,
|
||||||
|
@ -102,48 +177,14 @@ class WaveConstantNode(base.MaxwellSimNode):
|
||||||
input_sockets_optional={'WL': True, 'Freq': True},
|
input_sockets_optional={'WL': True, 'Freq': True},
|
||||||
)
|
)
|
||||||
def compute_freq_range(self, input_sockets: dict) -> sp.Expr:
|
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:
|
if input_sockets['Freq'] is not None:
|
||||||
return input_sockets['Freq']
|
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(
|
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
|
# - Blender Registration
|
||||||
|
|
|
@ -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
|
|
|
@ -1,11 +1,11 @@
|
||||||
from blender_maxwell.utils import logger
|
from blender_maxwell.utils import logger
|
||||||
|
|
||||||
from .. import contracts as ct
|
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
|
from .scan_socket_defs import scan_for_socket_defs
|
||||||
|
|
||||||
log = logger.get(__name__)
|
log = logger.get(__name__)
|
||||||
sockets_modules = [basic, number, vector, physical, blender, maxwell, tidy3d]
|
sockets_modules = [basic, physical, blender, maxwell, tidy3d]
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# - Scan for SocketDefs
|
# - Scan for SocketDefs
|
||||||
|
@ -33,8 +33,6 @@ for socket_type in ct.SocketType:
|
||||||
####################
|
####################
|
||||||
BL_REGISTER = [
|
BL_REGISTER = [
|
||||||
*basic.BL_REGISTER,
|
*basic.BL_REGISTER,
|
||||||
*number.BL_REGISTER,
|
|
||||||
*vector.BL_REGISTER,
|
|
||||||
*physical.BL_REGISTER,
|
*physical.BL_REGISTER,
|
||||||
*blender.BL_REGISTER,
|
*blender.BL_REGISTER,
|
||||||
*maxwell.BL_REGISTER,
|
*maxwell.BL_REGISTER,
|
||||||
|
@ -43,8 +41,6 @@ BL_REGISTER = [
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'basic',
|
'basic',
|
||||||
'number',
|
|
||||||
'vector',
|
|
||||||
'physical',
|
'physical',
|
||||||
'blender',
|
'blender',
|
||||||
'maxwell',
|
'maxwell',
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import abc
|
import abc
|
||||||
import functools
|
|
||||||
import typing as typ
|
import typing as typ
|
||||||
import uuid
|
import uuid
|
||||||
from types import MappingProxyType
|
from types import MappingProxyType
|
||||||
|
@ -193,6 +192,7 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
|
||||||
log.debug('Initializing Socket: %s', cls.socket_type)
|
log.debug('Initializing Socket: %s', cls.socket_type)
|
||||||
super().__init_subclass__(**kwargs)
|
super().__init_subclass__(**kwargs)
|
||||||
# cls._assert_attrs_valid()
|
# cls._assert_attrs_valid()
|
||||||
|
## TODO: Implement this :)
|
||||||
|
|
||||||
# Socket Properties
|
# Socket Properties
|
||||||
## Identifiers
|
## Identifiers
|
||||||
|
@ -235,46 +235,10 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
|
||||||
####################
|
####################
|
||||||
# - Units
|
# - 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
|
@property
|
||||||
def prev_unit(self) -> sp.Expr:
|
def prev_unit(self) -> sp.Expr:
|
||||||
return self.possible_units[self.prev_active_unit]
|
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
|
# - 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.
|
Called by `self.on_prop_changed()` when `self.active_kind` was changed.
|
||||||
"""
|
"""
|
||||||
self.display_shape = (
|
self.display_shape = (
|
||||||
'SQUARE'
|
'SQUARE' if self.active_kind == ct.FlowKind.LazyValueRange else 'CIRCLE'
|
||||||
if self.active_kind in {ct.FlowKind.Array, ct.FlowKind.LazyValueRange}
|
|
||||||
else 'CIRCLE'
|
|
||||||
) + ('_DOT' if self.use_units else '')
|
) + ('_DOT' if self.use_units else '')
|
||||||
|
## TODO: Valid Active Kinds should be a subset/subenum(?) of FlowKind
|
||||||
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
|
|
||||||
|
|
||||||
def on_prop_changed(self, prop_name: str, _: bpy.types.Context) -> None:
|
def on_prop_changed(self, prop_name: str, _: bpy.types.Context) -> None:
|
||||||
"""Called when a property has been updated.
|
"""Called when a property has been updated.
|
||||||
|
@ -880,7 +816,6 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
|
||||||
col = row.column(align=True)
|
col = row.column(align=True)
|
||||||
{
|
{
|
||||||
ct.FlowKind.Value: self.draw_value,
|
ct.FlowKind.Value: self.draw_value,
|
||||||
ct.FlowKind.Array: self.draw_array,
|
|
||||||
ct.FlowKind.LazyArrayRange: self.draw_lazy_array_range,
|
ct.FlowKind.LazyArrayRange: self.draw_lazy_array_range,
|
||||||
}[self.active_kind](col)
|
}[self.active_kind](col)
|
||||||
|
|
||||||
|
@ -922,7 +857,7 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
|
||||||
self.draw_info(info, col)
|
self.draw_info(info, col)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# - UI Methods: Active FlowKind
|
# - UI Methods: Label Rows
|
||||||
####################
|
####################
|
||||||
def draw_label_row(
|
def draw_label_row(
|
||||||
self,
|
self,
|
||||||
|
@ -978,6 +913,9 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
|
||||||
"""
|
"""
|
||||||
row.label(text=text)
|
row.label(text=text)
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - UI Methods: Active FlowKind
|
||||||
|
####################
|
||||||
def draw_value(self, col: bpy.types.UILayout) -> None:
|
def draw_value(self, col: bpy.types.UILayout) -> None:
|
||||||
"""Draws the socket value on its own line.
|
"""Draws the socket value on its own line.
|
||||||
|
|
||||||
|
@ -988,16 +926,6 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
|
||||||
col: Target for defining UI elements.
|
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:
|
def draw_lazy_array_range(self, col: bpy.types.UILayout) -> None:
|
||||||
"""Draws the socket lazy array range on its own line.
|
"""Draws the socket lazy array range on its own line.
|
||||||
|
|
||||||
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
||||||
|
]
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -1,61 +1,9 @@
|
||||||
from . import unit_system
|
from . import pol, 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
|
|
||||||
|
|
||||||
PhysicalPolSocketDef = pol.PhysicalPolSocketDef
|
PhysicalPolSocketDef = pol.PhysicalPolSocketDef
|
||||||
|
|
||||||
from . import freq
|
|
||||||
|
|
||||||
PhysicalFreqSocketDef = freq.PhysicalFreqSocketDef
|
|
||||||
|
|
||||||
|
|
||||||
BL_REGISTER = [
|
BL_REGISTER = [
|
||||||
*unit_system.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,
|
*pol.BL_REGISTER,
|
||||||
*freq.BL_REGISTER,
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
]
|
|
|
@ -14,7 +14,6 @@ import enum
|
||||||
import itertools
|
import itertools
|
||||||
import typing as typ
|
import typing as typ
|
||||||
|
|
||||||
import jax.numpy as jnp
|
|
||||||
import pydantic as pyd
|
import pydantic as pyd
|
||||||
import sympy as sp
|
import sympy as sp
|
||||||
import sympy.physics.units as spu
|
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):
|
class MathType(enum.StrEnum):
|
||||||
|
"""Set identities encompassing common mathematical objects."""
|
||||||
|
|
||||||
Bool = enum.auto()
|
Bool = enum.auto()
|
||||||
Integer = enum.auto()
|
Integer = enum.auto()
|
||||||
Rational = enum.auto()
|
Rational = enum.auto()
|
||||||
|
@ -49,7 +50,9 @@ class MathType(enum.StrEnum):
|
||||||
return MathType.Bool
|
return MathType.Bool
|
||||||
if sp_obj.is_integer:
|
if sp_obj.is_integer:
|
||||||
return MathType.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
|
return MathType.Real
|
||||||
if sp_obj.is_complex:
|
if sp_obj.is_complex:
|
||||||
return MathType.Complex
|
return MathType.Complex
|
||||||
|
@ -580,6 +583,7 @@ Symbol: typ.TypeAlias = IntSymbol | RealSymbol | ComplexSymbol
|
||||||
# Unit
|
# Unit
|
||||||
## Technically a "unit expression", which includes compound types.
|
## Technically a "unit expression", which includes compound types.
|
||||||
## Support for this is the killer feature compared to spu.Quantity.
|
## Support for this is the killer feature compared to spu.Quantity.
|
||||||
|
UnitDimension: typ.TypeAlias = spu.Dimension
|
||||||
Unit: typ.TypeAlias = ConstrSympyExpr(
|
Unit: typ.TypeAlias = ConstrSympyExpr(
|
||||||
allow_variables=False,
|
allow_variables=False,
|
||||||
allow_units=True,
|
allow_units=True,
|
||||||
|
|
Loading…
Reference in New Issue