feat: expr constant w/viz fixes
Driven solely by the Expr socket, we've completely replaced the dedicated NumberConstant and PhysicalConstant nodes. We also demonstrated symbolic variable operations w/visualization and end-node realization, validating that this approach to Expr sockets is key. We prepared for a new `BLPropType`, namely dynamic lists of dataclasses, which will represent dynamic user-adjustable lists. This is the only proper UI design for declaring symbols directly in an Expr node. For now, we'll do without symbols, but will be a core feature for design space exploration (aka. batch-run a bunch of sims), and for inverse design. Else, a few fixes: Naming of `ManagedBLImage` was updated, the expr socket info display was fixed to display even when only output information is available, A few TODOs remain in the Expr Constant, but they are less important before dynamic symbol declarations are in place.main
parent
92be84ec8a
commit
060f54bd94
|
@ -38,8 +38,6 @@ class NodeType(blender_type_enum.BlenderTypeEnum):
|
|||
Scene = enum.auto()
|
||||
## Inputs / Constants
|
||||
ExprConstant = enum.auto()
|
||||
NumberConstant = enum.auto()
|
||||
PhysicalConstant = enum.auto()
|
||||
ScientificConstant = enum.auto()
|
||||
UnitSystemConstant = enum.auto()
|
||||
BlenderConstant = enum.auto()
|
||||
|
|
|
@ -21,9 +21,11 @@ import enum
|
|||
import typing as typ
|
||||
|
||||
import jax.numpy as jnp
|
||||
import sympy as sp
|
||||
import tidy3d as td
|
||||
|
||||
from blender_maxwell.services import tdcloud
|
||||
from blender_maxwell.utils import extra_sympy_units as spux
|
||||
|
||||
|
||||
####################
|
||||
|
|
|
@ -47,8 +47,13 @@ class ManagedBLImage(base.ManagedObj):
|
|||
managed_obj_type = ct.ManagedObjType.ManagedBLImage
|
||||
_bl_image_name: str
|
||||
|
||||
def __init__(self, name: str):
|
||||
self._bl_image_name = name
|
||||
def __init__(self, name: str, prev_name: str | None = None):
|
||||
if prev_name is not None:
|
||||
self._bl_image_name = prev_name
|
||||
else:
|
||||
self._bl_image_name = name
|
||||
|
||||
self.name = name
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
@ -57,26 +62,29 @@ class ManagedBLImage(base.ManagedObj):
|
|||
@name.setter
|
||||
def name(self, value: str):
|
||||
log.info(
|
||||
'Setting ManagedBLImage from "%s" to "%s"',
|
||||
'Changing ManagedBLImage from "%s" to "%s"',
|
||||
self.name,
|
||||
value,
|
||||
)
|
||||
current_bl_image = bpy.data.images.get(self._bl_image_name)
|
||||
wanted_bl_image = bpy.data.images.get(value)
|
||||
existing_bl_image = bpy.data.images.get(self.name)
|
||||
|
||||
# Yoink Image Name
|
||||
if current_bl_image is None and wanted_bl_image is None:
|
||||
# No Existing Image: Set Value to Name
|
||||
if existing_bl_image is None:
|
||||
self._bl_image_name = value
|
||||
|
||||
# Alter Image Name
|
||||
elif current_bl_image is not None and wanted_bl_image is None:
|
||||
# Existing Image: Rename to New Name
|
||||
else:
|
||||
existing_bl_image.name = value
|
||||
self._bl_image_name = value
|
||||
current_bl_image.name = value
|
||||
|
||||
# Overlapping Image Name
|
||||
elif wanted_bl_image is not None:
|
||||
msg = f'ManagedBLImage "{self._bl_image_name}" could not change its name to "{value}", since it already exists.'
|
||||
raise ValueError(msg)
|
||||
# Check: Blender Rename -> Synchronization Error
|
||||
## -> We can't do much else than report to the user & free().
|
||||
if existing_bl_image.name != self._bl_image_name:
|
||||
log.critical(
|
||||
'BLImage: Failed to set name of %s to %s, as %s already exists.'
|
||||
)
|
||||
self._bl_image_name = existing_bl_image.name
|
||||
self.free()
|
||||
|
||||
def free(self):
|
||||
bl_image = bpy.data.images.get(self.name)
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
import typing as typ
|
||||
|
||||
import sympy as sp
|
||||
|
||||
from .... import contracts as ct
|
||||
from .... import sockets
|
||||
from ... import base, events
|
||||
|
@ -26,27 +28,69 @@ class ExprConstantNode(base.MaxwellSimNode):
|
|||
bl_label = 'Expr Constant'
|
||||
|
||||
input_sockets: typ.ClassVar = {
|
||||
'Expr': sockets.ExprSocketDef(),
|
||||
'Expr': sockets.ExprSocketDef(
|
||||
active_kind=ct.FlowKind.LazyValueFunc,
|
||||
),
|
||||
}
|
||||
output_sockets: typ.ClassVar = {
|
||||
'Expr': sockets.ExprSocketDef(),
|
||||
'Expr': sockets.ExprSocketDef(
|
||||
active_kind=ct.FlowKind.LazyValueFunc,
|
||||
show_info_columns=True,
|
||||
),
|
||||
}
|
||||
|
||||
## TODO: Symbols (defined w/props?)
|
||||
## - Currently expr constant isn't excessively useful, since there are no variables.
|
||||
## - We'll define the #, type, name with props.
|
||||
## - We'll add loose-socket inputs as int/real/complex/physical socket (based on type) for Param.
|
||||
## - We the output expr would support `Value` (just the expression), `LazyValueFunc` (evaluate w/symbol support), `Param` (example values for symbols).
|
||||
## TODO: Allow immediately realizing any symbol, or just passing it along.
|
||||
## TODO: Alter output physical_type when the input PhysicalType changes.
|
||||
|
||||
####################
|
||||
# - Callbacks
|
||||
# - FlowKinds
|
||||
####################
|
||||
@events.computes_output_socket(
|
||||
'Expr', kind=ct.FlowKind.Value, input_sockets={'Expr'}
|
||||
# Trigger
|
||||
'Expr',
|
||||
kind=ct.FlowKind.Value,
|
||||
# Loaded
|
||||
input_sockets={'Expr'},
|
||||
)
|
||||
def compute_value(self, input_sockets: dict) -> typ.Any:
|
||||
return input_sockets['Expr']
|
||||
|
||||
@events.computes_output_socket(
|
||||
# Trigger
|
||||
'Expr',
|
||||
kind=ct.FlowKind.LazyValueFunc,
|
||||
# Loaded
|
||||
input_sockets={'Expr'},
|
||||
input_socket_kinds={'Expr': ct.FlowKind.LazyValueFunc},
|
||||
)
|
||||
def compute_lazy_value_func(self, input_sockets: dict) -> typ.Any:
|
||||
return input_sockets['Expr']
|
||||
|
||||
####################
|
||||
# - FlowKinds: Auxiliary
|
||||
####################
|
||||
@events.computes_output_socket(
|
||||
# Trigger
|
||||
'Expr',
|
||||
kind=ct.FlowKind.Info,
|
||||
# Loaded
|
||||
input_sockets={'Expr'},
|
||||
input_socket_kinds={'Expr': ct.FlowKind.Info},
|
||||
)
|
||||
def compute_info(self, input_sockets: dict) -> typ.Any:
|
||||
return input_sockets['Expr']
|
||||
|
||||
@events.computes_output_socket(
|
||||
# Trigger
|
||||
'Expr',
|
||||
kind=ct.FlowKind.Params,
|
||||
# Loaded
|
||||
input_sockets={'Expr'},
|
||||
input_socket_kinds={'Expr': ct.FlowKind.Params},
|
||||
)
|
||||
def compute_params(self, input_sockets: dict) -> typ.Any:
|
||||
return input_sockets['Expr']
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
# blender_maxwell
|
||||
# Copyright (C) 2024 blender_maxwell Project Contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import typing as typ
|
||||
|
||||
import bpy
|
||||
|
||||
from blender_maxwell.utils import bl_cache
|
||||
from blender_maxwell.utils import extra_sympy_units as spux
|
||||
|
||||
from .... import contracts as ct
|
||||
from .... import sockets
|
||||
from ... import base, events
|
||||
|
||||
|
||||
class NumberConstantNode(base.MaxwellSimNode):
|
||||
"""A unitless number of configurable math type ex. integer, real, etc. .
|
||||
|
||||
Attributes:
|
||||
mathtype: The math type to specify the number as.
|
||||
"""
|
||||
|
||||
node_type = ct.NodeType.NumberConstant
|
||||
bl_label = 'Numerical Constant'
|
||||
|
||||
input_sockets: typ.ClassVar = {
|
||||
'Value': sockets.ExprSocketDef(),
|
||||
}
|
||||
output_sockets: typ.ClassVar = {
|
||||
'Value': sockets.ExprSocketDef(),
|
||||
}
|
||||
|
||||
####################
|
||||
# - Properties
|
||||
####################
|
||||
mathtype: spux.MathType = bl_cache.BLField(
|
||||
spux.MathType.Integer,
|
||||
prop_ui=True,
|
||||
)
|
||||
|
||||
size: spux.NumberSize1D = bl_cache.BLField(
|
||||
spux.NumberSize1D.Scalar,
|
||||
prop_ui=True,
|
||||
)
|
||||
|
||||
####################
|
||||
# - UI
|
||||
####################
|
||||
def draw_props(self, _, col: bpy.types.UILayout) -> None:
|
||||
row = col.row(align=True)
|
||||
row.prop(self, self.blfields['mathtype'], text='')
|
||||
row.prop(self, self.blfields['size'], text='')
|
||||
|
||||
####################
|
||||
# - Events
|
||||
####################
|
||||
@events.on_value_changed(prop_name={'mathtype', 'size'}, props={'mathtype', 'size'})
|
||||
def on_mathtype_size_changed(self, props) -> None:
|
||||
"""Change the input/output expression sockets to match the mathtype declared in the node."""
|
||||
self.inputs['Value'].mathtype = props['mathtype']
|
||||
self.inputs['Value'].shape = props['size'].shape
|
||||
|
||||
####################
|
||||
# - FlowKind
|
||||
####################
|
||||
@events.computes_output_socket('Value', input_sockets={'Value'})
|
||||
def compute_value(self, input_sockets) -> typ.Any:
|
||||
return input_sockets['Value']
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
BL_REGISTER = [
|
||||
NumberConstantNode,
|
||||
]
|
||||
BL_NODES = {ct.NodeType.NumberConstant: (ct.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS)}
|
|
@ -1,143 +0,0 @@
|
|||
# blender_maxwell
|
||||
# Copyright (C) 2024 blender_maxwell Project Contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import typing as typ
|
||||
|
||||
import bpy
|
||||
import sympy as sp
|
||||
|
||||
from blender_maxwell.utils import bl_cache
|
||||
from blender_maxwell.utils import extra_sympy_units as spux
|
||||
|
||||
from .... import contracts, sockets
|
||||
from ... import base, events
|
||||
|
||||
|
||||
class PhysicalConstantNode(base.MaxwellSimNode):
|
||||
"""A number of configurable unit dimension, ex. time, length, etc. .
|
||||
|
||||
Attributes:
|
||||
physical_type: The physical type to specify.
|
||||
size: The size of the physical type, if it can be a vector.
|
||||
"""
|
||||
|
||||
node_type = contracts.NodeType.PhysicalConstant
|
||||
bl_label = 'Physical Constant'
|
||||
|
||||
input_sockets: typ.ClassVar = {
|
||||
'Value': sockets.ExprSocketDef(),
|
||||
}
|
||||
output_sockets: typ.ClassVar = {
|
||||
'Value': sockets.ExprSocketDef(),
|
||||
}
|
||||
|
||||
####################
|
||||
# - Properties
|
||||
####################
|
||||
physical_type: spux.PhysicalType = bl_cache.BLField(
|
||||
spux.PhysicalType.Time,
|
||||
prop_ui=True,
|
||||
)
|
||||
|
||||
mathtype: spux.MathType = bl_cache.BLField(
|
||||
enum_cb=lambda self, _: self.search_mathtypes(),
|
||||
prop_ui=True,
|
||||
)
|
||||
|
||||
size: spux.NumberSize1D = bl_cache.BLField(
|
||||
enum_cb=lambda self, _: self.search_sizes(),
|
||||
)
|
||||
|
||||
####################
|
||||
# - Searchers
|
||||
####################
|
||||
def search_mathtypes(self):
|
||||
return [
|
||||
mathtype.bl_enum_element(i)
|
||||
for i, mathtype in enumerate(self.physical_type.valid_mathtypes)
|
||||
]
|
||||
|
||||
def search_sizes(self):
|
||||
return [
|
||||
spux.NumberSize1D.from_shape(shape).bl_enum_element(i)
|
||||
for i, shape in enumerate(self.physical_type.valid_shapes)
|
||||
if spux.NumberSize1D.has_shape(shape)
|
||||
]
|
||||
|
||||
####################
|
||||
# - UI
|
||||
####################
|
||||
def draw_props(self, _, col: bpy.types.UILayout) -> None:
|
||||
col.prop(self, self.blfields['physical_type'], text='')
|
||||
|
||||
row = col.row(align=True)
|
||||
row.prop(self, self.blfields['mathtype'], text='')
|
||||
row.prop(self, self.blfields['size'], text='')
|
||||
|
||||
####################
|
||||
# - Events
|
||||
####################
|
||||
@events.on_value_changed(
|
||||
# Trigger
|
||||
prop_name={'physical_type'},
|
||||
run_on_init=True,
|
||||
# Loaded
|
||||
props={'physical_type'},
|
||||
)
|
||||
def on_physical_type_changed(self, props) -> None:
|
||||
"""Change the input/output expression sockets to match the mathtype and size declared in the node."""
|
||||
# Set Input Socket Physical Type
|
||||
if self.inputs['Value'].physical_type != props['physical_type']:
|
||||
self.inputs['Value'].physical_type = props['physical_type']
|
||||
self.mathtype = bl_cache.Signal.ResetEnumItems
|
||||
self.size = bl_cache.Signal.ResetEnumItems
|
||||
|
||||
@events.on_value_changed(
|
||||
# Trigger
|
||||
prop_name={'mathtype', 'size'},
|
||||
run_on_init=True,
|
||||
# Loaded
|
||||
props={'physical_type', 'mathtype', 'size'},
|
||||
)
|
||||
def on_mathtype_or_size_changed(self, props) -> None:
|
||||
# Set Input Socket Math Type
|
||||
if self.inputs['Value'].mathtype != props['mathtype']:
|
||||
self.inputs['Value'].mathtype = props['mathtype']
|
||||
|
||||
# Set Input Socket Shape
|
||||
shape = props['size'].shape
|
||||
if self.inputs['Value'].shape != shape:
|
||||
self.inputs['Value'].shape = shape
|
||||
|
||||
####################
|
||||
# - Callbacks
|
||||
####################
|
||||
@events.computes_output_socket('Value', input_sockets={'Value'})
|
||||
def compute_value(self, input_sockets) -> sp.Expr:
|
||||
return input_sockets['Value']
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Registration
|
||||
####################
|
||||
BL_REGISTER = [
|
||||
PhysicalConstantNode,
|
||||
]
|
||||
BL_NODES = {
|
||||
contracts.NodeType.PhysicalConstant: (
|
||||
contracts.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS
|
||||
)
|
||||
}
|
|
@ -116,13 +116,22 @@ class ExprBLSocket(base.MaxwellSimSocket):
|
|||
size: spux.NumberSize1D = bl_cache.BLField(spux.NumberSize1D.Scalar)
|
||||
mathtype: spux.MathType = bl_cache.BLField(spux.MathType.Real)
|
||||
physical_type: spux.PhysicalType = bl_cache.BLField(spux.PhysicalType.NonPhysical)
|
||||
|
||||
# Symbols
|
||||
# active_symbols: list[sim_symbols.SimSymbol] = bl_cache.BLField([])
|
||||
symbols: frozenset[sp.Symbol] = bl_cache.BLField(frozenset())
|
||||
|
||||
# @property
|
||||
# def symbols(self) -> set[sp.Symbol]:
|
||||
# """Current symbols as an unordered set."""
|
||||
# return {sim_symbol.sp_symbol for sim_symbol in self.active_symbols}
|
||||
|
||||
@bl_cache.cached_bl_property(depends_on={'symbols'})
|
||||
def sorted_symbols(self) -> list[sp.Symbol]:
|
||||
"""Name-sorted symbols."""
|
||||
"""Current symbols as a sorted list."""
|
||||
return sorted(self.symbols, key=lambda sym: sym.name)
|
||||
|
||||
# Unit
|
||||
active_unit: enum.StrEnum = bl_cache.BLField(
|
||||
enum_cb=lambda self, _: self.search_valid_units(),
|
||||
cb_depends_on={'physical_type'},
|
||||
|
@ -672,16 +681,16 @@ class ExprBLSocket(base.MaxwellSimSocket):
|
|||
Whether information about the expression passing through a linked socket is shown is governed by `self.show_info_columns`.
|
||||
"""
|
||||
info = self.compute_data(kind=ct.FlowKind.Info)
|
||||
has_dims = not ct.FlowSignal.check(info) and info.dim_names
|
||||
has_info = not ct.FlowSignal.check(info)
|
||||
|
||||
if has_dims:
|
||||
if has_info:
|
||||
split = row.split(factor=0.85, align=True)
|
||||
_row = split.row(align=False)
|
||||
else:
|
||||
_row = row
|
||||
|
||||
_row.label(text=text)
|
||||
if has_dims:
|
||||
if has_info:
|
||||
if self.show_info_columns:
|
||||
_row.prop(self, self.blfields['info_columns'])
|
||||
|
||||
|
@ -735,9 +744,17 @@ class ExprBLSocket(base.MaxwellSimSocket):
|
|||
# - UI: Active FlowKind
|
||||
####################
|
||||
def draw_value(self, col: bpy.types.UILayout) -> None:
|
||||
"""Draw the socket body for a single values/expression.
|
||||
"""Draw the socket body for a single value/expression.
|
||||
|
||||
Drawn when `self.active_kind == FlowKind.Value`.
|
||||
This implements the base UI for `ExprSocket`, for when `self.size`, `self.mathtype`, `self.physical_type`, and `self.symbols` are set.
|
||||
|
||||
Notes:
|
||||
Drawn when `self.active_kind == FlowKind.Value`.
|
||||
|
||||
Alone, `draw_value` provides no mechanism for altering expression constraints like size.
|
||||
Thus, `FlowKind.Value` is a good choice for when the expression must be of a very particular type.
|
||||
|
||||
However, `draw_value` may also be called by the `draw_*` methods of other `FlowKinds`, who may choose to layer more flexibility around this base UI.
|
||||
"""
|
||||
if self.symbols:
|
||||
col.prop(self, self.blfields['raw_value_spstr'], text='')
|
||||
|
@ -819,23 +836,43 @@ class ExprBLSocket(base.MaxwellSimSocket):
|
|||
col.prop(self, self.blfields['steps'], text='')
|
||||
|
||||
def draw_lazy_value_func(self, col: bpy.types.UILayout) -> None:
|
||||
"""Draw the socket body for a value/expression meant for use in a lazy function composition chain.
|
||||
"""Draw the socket body for a single flexible value/expression, for down-chain lazy evaluation.
|
||||
|
||||
Drawn when `self.active_kind == FlowKind.LazyValueFunc`.
|
||||
This implements the most flexible variant of the `ExprSocket` UI, providing the user with full runtime-configuration of the exact `self.size`, `self.mathtype`, `self.physical_type`, and `self.symbols` of the expression.
|
||||
|
||||
Notes:
|
||||
Drawn when `self.active_kind == FlowKind.LazyValueFunc`.
|
||||
|
||||
This is an ideal choice for ex. math nodes that need to accept arbitrary expressions as inputs, with an eye towards lazy evaluation of ex. symbolic terms.
|
||||
|
||||
Uses `draw_value` to draw the base UI
|
||||
"""
|
||||
# Physical Type Selector
|
||||
## -> Determines whether/which unit-dropdown will be shown.
|
||||
col.prop(self, self.blfields['physical_type'], text='')
|
||||
|
||||
# Non-Symbolic: Size/Mathtype Selector
|
||||
## -> Symbols imply str expr input.
|
||||
## -> For arbitrary str exprs, size/mathtype are derived from the expr.
|
||||
## -> Otherwise, size/mathtype must be pre-specified for a nice UI.
|
||||
if not self.symbols:
|
||||
row = col.row(align=True)
|
||||
row.prop(self, self.blfields['size'], text='')
|
||||
row.prop(self, self.blfields['mathtype'], text='')
|
||||
|
||||
# Base UI
|
||||
## -> Draws the UI appropriate for the above choice of constraints.
|
||||
self.draw_value(col)
|
||||
|
||||
# Symbol UI
|
||||
## -> Draws the UI appropriate for the above choice of constraints.
|
||||
## -> TODO
|
||||
|
||||
####################
|
||||
# - UI: InfoFlow
|
||||
####################
|
||||
def draw_info(self, info: ct.InfoFlow, col: bpy.types.UILayout) -> None:
|
||||
if self.active_kind == ct.FlowKind.Array and self.show_info_columns:
|
||||
if self.active_kind == ct.FlowKind.LazyValueFunc and self.show_info_columns:
|
||||
row = col.row()
|
||||
box = row.box()
|
||||
grid = box.grid_flow(
|
||||
|
@ -899,7 +936,8 @@ class ExprSocketDef(base.SocketDef):
|
|||
physical_type: spux.PhysicalType = spux.PhysicalType.NonPhysical
|
||||
|
||||
default_unit: spux.Unit | None = None
|
||||
symbols: frozenset[spux.Symbol] = frozenset()
|
||||
# symbols: list[sim_symbols.SimSymbol] = frozenset()
|
||||
symbols: frozenset[spux.SympyExpr] = frozenset()
|
||||
|
||||
# FlowKind: Value
|
||||
default_value: spux.SympyExpr = 0
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
# blender_maxwell
|
||||
# Copyright (C) 2024 blender_maxwell Project Contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import dataclasses
|
||||
import enum
|
||||
import typing as typ
|
||||
|
||||
import sympy as sp
|
||||
|
||||
from . import extra_sympy_units as spux
|
||||
|
||||
|
||||
class SimSymbolNames(enum.StrEnum):
|
||||
LowerA = enum.auto()
|
||||
LowerLambda = enum.auto()
|
||||
|
||||
@staticmethod
|
||||
def to_name(v: typ.Self) -> str:
|
||||
"""Convert the enum value to a human-friendly name.
|
||||
|
||||
Notes:
|
||||
Used to print names in `EnumProperty`s based on this enum.
|
||||
|
||||
Returns:
|
||||
A human-friendly name corresponding to the enum value.
|
||||
"""
|
||||
SSN = SimSymbolNames
|
||||
return {
|
||||
SSN.LowerA: 'a',
|
||||
SSN.LowerLambda: 'λ',
|
||||
}[v]
|
||||
|
||||
@staticmethod
|
||||
def to_icon(_: typ.Self) -> str:
|
||||
"""Convert the enum value to a Blender icon.
|
||||
|
||||
Notes:
|
||||
Used to print icons in `EnumProperty`s based on this enum.
|
||||
|
||||
Returns:
|
||||
A human-friendly name corresponding to the enum value.
|
||||
"""
|
||||
return ''
|
||||
|
||||
|
||||
@dataclasses.dataclass(kw_only=True, frozen=True)
|
||||
class SimSymbol:
|
||||
name: SimSymbolNames = SimSymbolNames.LowerLambda
|
||||
mathtype: spux.MathType = spux.MathType.Real
|
||||
|
||||
## TODO:
|
||||
## -> Physical Type: Track unit dimension information on the side.
|
||||
## -> Domain: Ability to constrain mathtype ex. (-pi,pi]
|
||||
## -> Shape: For using sp.MatrixSymbol w/predefined rows/cols.
|
||||
|
||||
@property
|
||||
def sp_symbol(self):
|
||||
mathtype_kwarg = {}
|
||||
match self.mathtype:
|
||||
case spux.MathType.Real:
|
||||
mathtype_kwarg = {}
|
||||
|
||||
return sp.Symbol(self.name, **mathtype_kwarg)
|
Loading…
Reference in New Issue