diff --git a/scripts/info.py b/scripts/info.py index ce21788..32b0288 100644 --- a/scripts/info.py +++ b/scripts/info.py @@ -41,7 +41,7 @@ PATH_ADDON_ZIP = ( PATH_ADDON_BLEND_STARTER = PATH_ADDON_PKG / 'blenders' / 'starter.blend' - # Install the ZIPped Addon +# Install the ZIPped Addon #################### # - Development Information #################### @@ -49,4 +49,3 @@ PATH_ADDON_DEV_BLEND = PATH_DEV / 'demo.blend' PATH_ADDON_DEV_DEPS = PATH_DEV / '.cached-dev-dependencies' PATH_ADDON_DEV_DEPS.mkdir(exist_ok=True) - diff --git a/scripts/pack.py b/scripts/pack.py index 9cce486..e4664ef 100644 --- a/scripts/pack.py +++ b/scripts/pack.py @@ -47,7 +47,10 @@ def zipped_addon( tempfile.NamedTemporaryFile(mode='w') as f_tmp, ): initpy = f_init.read() - for to_replace, replacement in BL_INFO_REPLACEMENTS.items(): + for ( + to_replace, + replacement, + ) in BL_INFO_REPLACEMENTS.items(): initpy = initpy.replace(to_replace, replacement) f_tmp.write(initpy) @@ -67,10 +70,7 @@ def zipped_addon( f_zip.write( path_pyproject_toml, str( - ( - Path(path_addon_pkg.name) - / Path(path_pyproject_toml.name) - ) + (Path(path_addon_pkg.name) / Path(path_pyproject_toml.name)) .with_suffix('') .with_suffix('.toml') ), diff --git a/scripts/run.py b/scripts/run.py index 64848bd..58a8041 100644 --- a/scripts/run.py +++ b/scripts/run.py @@ -11,7 +11,7 @@ import info def run_blender(py_script: Path, print_live: bool = False): process = subprocess.Popen( ['blender', '--python', str(py_script)], - env = os.environ | {'PYTHONUNBUFFERED': '1'}, + env=os.environ | {'PYTHONUNBUFFERED': '1'}, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, @@ -28,7 +28,7 @@ def run_blender(py_script: Path, print_live: bool = False): print(line, end='') # noqa: T201 elif ( info.SIGNAL_START_CLEAN_BLENDER in line - #or 'Traceback (most recent call last)' in line + # or 'Traceback (most recent call last)' in line ): printing_live = True print(''.join(output)) # noqa: T201 diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/__init__.py index e268bfa..3f0adff 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/__init__.py @@ -1,4 +1,5 @@ import sympy as sp + sp.printing.str.StrPrinter._default_settings['abbrev'] = True ## In this tree, all Sympy unit printing must be abbreviated. ## By configuring this in __init__.py, we guarantee it for all subimports. diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/bl_socket_map.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/bl_socket_map.py index cd2d665..0859ce6 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/bl_socket_map.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/bl_socket_map.py @@ -26,47 +26,47 @@ Unit = typ.Any ## Type of a valid unit SOCKET_DEFS = { socket_type: getattr( sck, - socket_type.value.removesuffix("SocketType") + "SocketDef", + socket_type.value.removesuffix('SocketType') + 'SocketDef', ) for socket_type in ST - if hasattr( - sck, - socket_type.value.removesuffix("SocketType") + "SocketDef" - ) + if hasattr(sck, socket_type.value.removesuffix('SocketType') + 'SocketDef') } ## TODO: Bit of a hack. Is it robust enough? for socket_type in ST: if not hasattr( sck, - socket_type.value.removesuffix("SocketType") + "SocketDef", + socket_type.value.removesuffix('SocketType') + 'SocketDef', ): - print("Missing SocketDef for", socket_type.value) + print('Missing SocketDef for', socket_type.value) #################### # - BL Socket Size Parser #################### BL_SOCKET_3D_TYPE_PREFIXES = { - "NodeSocketVector", - "NodeSocketRotation", + 'NodeSocketVector', + 'NodeSocketRotation', } BL_SOCKET_4D_TYPE_PREFIXES = { - "NodeSocketColor", + 'NodeSocketColor', } + + def size_from_bl_interface_socket( - bl_interface_socket: bpy.types.NodeTreeInterfaceSocket + bl_interface_socket: bpy.types.NodeTreeInterfaceSocket, ) -> typx.Literal[1, 2, 3, 4]: """Parses the `size`, aka. number of elements, contained within the `default_value` of a Blender interface socket. - + Since there are no 2D sockets in Blender, the user can specify "2D" in the Blender socket's description to "promise" that only the first two values will be used. When this is done, the third value is left entirely untouched by this entire system. - + A hard-coded set of NodeSocket prefixes are used to determine which interface sockets are, in fact, 3D. - For 3D sockets, a hard-coded list of Blender node socket types is used. - Else, it is a 1D socket type. """ - if bl_interface_socket.description.startswith("2D"): return 2 + if bl_interface_socket.description.startswith('2D'): + return 2 if any( bl_interface_socket.socket_type.startswith(bl_socket_3d_type_prefix) for bl_socket_3d_type_prefix in BL_SOCKET_3D_TYPE_PREFIXES @@ -77,7 +77,7 @@ def size_from_bl_interface_socket( for bl_socket_4d_type_prefix in BL_SOCKET_4D_TYPE_PREFIXES ): return 4 - + return 1 @@ -88,15 +88,15 @@ def parse_bl_interface_socket( bl_interface_socket: bpy.types.NodeTreeInterfaceSocket, ) -> tuple[ST, sp.Expr | None]: """Parse a Blender interface socket by parsing its description, falling back to any direct type links. - + Arguments: bl_interface_socket: An interface socket associated with the global input to a node tree. - + Returns: The type of a corresponding MaxwellSimSocket, as well as a unit (if a particular unit was requested by the Blender interface socket). """ size = size_from_bl_interface_socket(bl_interface_socket) - + # Determine Direct Socket Type if ( direct_socket_type := ct.BL_SOCKET_DIRECT_TYPE_MAP.get( @@ -105,44 +105,55 @@ def parse_bl_interface_socket( ) is None: msg = "Blender interface socket has no mapping among 'MaxwellSimSocket's." raise ValueError(msg) - + # (Maybe) Return Direct Socket Type ## When there's no description, that's it; return. if not ct.BL_SOCKET_DESCR_ANNOT_STRING in bl_interface_socket.description: return (direct_socket_type, None) - + # Parse Description for Socket Type tokens = ( _tokens - if (_tokens := bl_interface_socket.description.split(" "))[0] != "2D" + if (_tokens := bl_interface_socket.description.split(' '))[0] != '2D' else _tokens[1:] - ) ## Don't include the "2D" token, if defined. + ) ## Don't include the "2D" token, if defined. if ( socket_type := ct.BL_SOCKET_DESCR_TYPE_MAP.get( (tokens[0], bl_interface_socket.socket_type, size) ) ) is None: - return (direct_socket_type, None) ## Description doesn't map to anything - + return ( + direct_socket_type, + None, + ) ## Description doesn't map to anything + # Determine Socket Unit (to use instead of "unit system") ## This is entirely OPTIONAL socket_unit = None if socket_type in ct.SOCKET_UNITS: ## Case: Unit is User-Defined - if len(tokens) > 1 and "(" in tokens[1] and ")" in tokens[1]: + if len(tokens) > 1 and '(' in tokens[1] and ')' in tokens[1]: # Compute () as Unit Token - unit_token = tokens[1].removeprefix("(").removesuffix(")") - + unit_token = tokens[1].removeprefix('(').removesuffix(')') + # Compare Unit Token to Valid Sympy-Printed Units - socket_unit = _socket_unit if (_socket_unit := [ - unit - for unit in ct.SOCKET_UNITS[socket_type]["values"].values() - if str(unit) == unit_token - ]) else ct.SOCKET_UNITS[socket_type]["values"][ - ct.SOCKET_UNITS[socket_type]["default"] - ] + socket_unit = ( + _socket_unit + if ( + _socket_unit := [ + unit + for unit in ct.SOCKET_UNITS[socket_type][ + 'values' + ].values() + if str(unit) == unit_token + ] + ) + else ct.SOCKET_UNITS[socket_type]['values'][ + ct.SOCKET_UNITS[socket_type]['default'] + ] + ) ## TODO: Enforce abbreviated sympy printing here, not globally - + return (socket_type, socket_unit) @@ -152,11 +163,8 @@ def parse_bl_interface_socket( def socket_def_from_bl_interface_socket( bl_interface_socket: bpy.types.NodeTreeInterfaceSocket, ): - """Computes an appropriate (no-arg) SocketDef from the given `bl_interface_socket`, by parsing it. - """ - return SOCKET_DEFS[ - parse_bl_interface_socket(bl_interface_socket)[0] - ] + """Computes an appropriate (no-arg) SocketDef from the given `bl_interface_socket`, by parsing it.""" + return SOCKET_DEFS[parse_bl_interface_socket(bl_interface_socket)[0]] #################### @@ -169,7 +177,7 @@ def value_from_bl( """Reads the value of any Blender socket, and writes its `default_value` to the `value` of any `MaxwellSimSocket`. - If the size of the Blender socket is >1, then `value` is written to as a `sympy.Matrix`. - If a unit system is given, then the Blender socket is matched to a `MaxwellSimSocket`, which is used to lookup an appropriate unit in the given `unit_system`. - + """ ## TODO: Consider sympy.S()'ing the default_value parsed_bl_socket_value = { @@ -179,24 +187,25 @@ def value_from_bl( 4: lambda: sp.Matrix(tuple(bl_interface_socket.default_value)), }[size_from_bl_interface_socket(bl_interface_socket)]() ## The 'lambda' delays construction until size is determined - + socket_type, unit = parse_bl_interface_socket(bl_interface_socket) - + # Add Unit to Parsed (if relevant) if unit is not None: parsed_bl_socket_value *= unit elif unit_system is not None: parsed_bl_socket_value *= unit_system[socket_type] - + return parsed_bl_socket_value + #################### # - Convert to Blender-Compatible Value #################### def make_scalar_bl_compat(scalar: typ.Any) -> typ.Any: """Blender doesn't accept ex. Sympy numbers as values. Therefore, we need to do some conforming. - + Currently hard-coded; this is probably best. """ if isinstance(scalar, sp.Integer): @@ -208,44 +217,43 @@ def make_scalar_bl_compat(scalar: typ.Any) -> typ.Any: elif isinstance(scalar, sp.Expr): return float(scalar.n()) ## TODO: More? - + return scalar + def value_to_bl( bl_interface_socket: bpy.types.NodeSocket, value: typ.Any, unit_system: dict | None = None, ) -> typ.Any: socket_type, unit = parse_bl_interface_socket(bl_interface_socket) - + # Set Socket if unit is not None: bl_socket_value = spu.convert_to(value, unit) / unit - elif ( - unit_system is not None - and socket_type in unit_system - ): - bl_socket_value = spu.convert_to( - value, unit_system[socket_type] - ) / unit_system[socket_type] + elif unit_system is not None and socket_type in unit_system: + bl_socket_value = ( + spu.convert_to(value, unit_system[socket_type]) + / unit_system[socket_type] + ) else: bl_socket_value = value - + return { 1: lambda: make_scalar_bl_compat(bl_socket_value), - 2: lambda: tuple([ - make_scalar_bl_compat(bl_socket_value[0]), - make_scalar_bl_compat(bl_socket_value[1]), - bl_interface_socket.default_value[2] - ## Don't touch (unused) 3rd bl_socket coordinate - ]), - 3: lambda: tuple([ - make_scalar_bl_compat(el) - for el in bl_socket_value - ]), - 4: lambda: tuple([ - make_scalar_bl_compat(el) - for el in bl_socket_value - ]), + 2: lambda: tuple( + [ + make_scalar_bl_compat(bl_socket_value[0]), + make_scalar_bl_compat(bl_socket_value[1]), + bl_interface_socket.default_value[2], + ## Don't touch (unused) 3rd bl_socket coordinate + ] + ), + 3: lambda: tuple( + [make_scalar_bl_compat(el) for el in bl_socket_value] + ), + 4: lambda: tuple( + [make_scalar_bl_compat(el) for el in bl_socket_value] + ), }[size_from_bl_interface_socket(bl_interface_socket)]() ## The 'lambda' delays construction until size is determined diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/categories.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/categories.py index 3be0191..070b9b5 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/categories.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/categories.py @@ -6,34 +6,35 @@ from . import contracts as ct from .nodes import BL_NODES DYNAMIC_SUBMENU_REGISTRATIONS = [] + + def mk_node_categories( tree, - syllable_prefix = [], - #root = True, + syllable_prefix=[], + # root = True, ): global DYNAMIC_SUBMENU_REGISTRATIONS items = [] - + # Add Node Items - base_category = ct.NodeCategory["_".join(syllable_prefix)] + base_category = ct.NodeCategory['_'.join(syllable_prefix)] for node_type, node_category in BL_NODES.items(): if node_category == base_category: items.append(nodeitems_utils.NodeItem(node_type.value)) - + # Add Node Sub-Menus for syllable, sub_tree in tree.items(): current_syllable_path = syllable_prefix + [syllable] - current_category = ct.NodeCategory[ - "_".join(current_syllable_path) - ] - + current_category = ct.NodeCategory['_'.join(current_syllable_path)] + # Build Items for Sub-Categories subitems = mk_node_categories( sub_tree, current_syllable_path, ) - if len(subitems) == 0: continue - + if len(subitems) == 0: + continue + # Define Dynamic Node Submenu def draw_factory(items): def draw(self, context): @@ -44,7 +45,7 @@ def mk_node_categories( ): nodeitem = nodeitem_or_submenu op_add_node_cfg = self.layout.operator( - "node.add_node", + 'node.add_node', text=nodeitem.label, ) op_add_node_cfg.type = nodeitem.nodetype @@ -52,34 +53,39 @@ def mk_node_categories( elif isinstance(nodeitem_or_submenu, str): submenu_id = nodeitem_or_submenu self.layout.menu(submenu_id) + return draw - - menu_class = type(str(current_category.value), (bpy.types.Menu,), { - 'bl_idname': current_category.value, - 'bl_label': ct.NODE_CAT_LABELS[current_category], - 'draw': draw_factory(tuple(subitems)), - }) - + + menu_class = type( + str(current_category.value), + (bpy.types.Menu,), + { + 'bl_idname': current_category.value, + 'bl_label': ct.NODE_CAT_LABELS[current_category], + 'draw': draw_factory(tuple(subitems)), + }, + ) + # Report to Items and Registration List items.append(current_category.value) DYNAMIC_SUBMENU_REGISTRATIONS.append(menu_class) - - return items + return items #################### # - Blender Registration #################### BL_NODE_CATEGORIES = mk_node_categories( - ct.NodeCategory.get_tree()["MAXWELLSIM"], - syllable_prefix = ["MAXWELLSIM"], + ct.NodeCategory.get_tree()['MAXWELLSIM'], + syllable_prefix=['MAXWELLSIM'], ) ## TODO: refactor, this has a big code smell BL_REGISTER = [ *DYNAMIC_SUBMENU_REGISTRATIONS ] ## Must be run after, right now. + ## TEST - TODO this is a big code smell def menu_draw(self, context): if context.space_data.tree_type == ct.TreeType.MaxwellSim.value: @@ -87,5 +93,6 @@ def menu_draw(self, context): if isinstance(nodeitem_or_submenu, str): submenu_id = nodeitem_or_submenu self.layout.menu(submenu_id) - + + bpy.types.NODE_MT_add.append(menu_draw) diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/bl.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/bl.py index fd75323..3bc0218 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/bl.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/bl.py @@ -7,20 +7,32 @@ import bpy #################### # - Pure BL Types #################### -BLEnumID = pytypes_ext.Annotated[str, pyd.StringConstraints( - pattern=r'^[A-Z_]+$', -)] -SocketName = pytypes_ext.Annotated[str, pyd.StringConstraints( - pattern=r'^[a-zA-Z0-9_]+$', -)] -PresetName = pytypes_ext.Annotated[str, pyd.StringConstraints( - pattern=r'^[a-zA-Z0-9_]+$', -)] +BLEnumID = pytypes_ext.Annotated[ + str, + pyd.StringConstraints( + pattern=r'^[A-Z_]+$', + ), +] +SocketName = pytypes_ext.Annotated[ + str, + pyd.StringConstraints( + pattern=r'^[a-zA-Z0-9_]+$', + ), +] +PresetName = pytypes_ext.Annotated[ + str, + pyd.StringConstraints( + pattern=r'^[a-zA-Z0-9_]+$', + ), +] BLColorRGBA = tuple[float, float, float, float] #################### # - Shared-With-BL Types #################### -ManagedObjName = pytypes_ext.Annotated[str, pyd.StringConstraints( - pattern=r'^[a-z_]+$', -)] +ManagedObjName = pytypes_ext.Annotated[ + str, + pyd.StringConstraints( + pattern=r'^[a-z_]+$', + ), +] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/data_flows.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/data_flows.py index 98ad666..ebd7121 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/data_flows.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/data_flows.py @@ -2,11 +2,12 @@ import enum from ....utils.blender_type_enum import BlenderTypeEnum + class DataFlowKind(BlenderTypeEnum): """Defines a shape/kind of data that may flow through a node tree. - + Since a node socket may define one of each, we can support several related kinds of data flow through the same node-graph infrastructure. - + Attributes: Value: A value usable without new data. - Basic types aka. float, int, list, string, etc. . @@ -14,43 +15,43 @@ class DataFlowKind(BlenderTypeEnum): - A usable constructed object, ex. a `tidy3d.Box`. - Expressions (`sp.Expr`) that don't have unknown variables. - Lazy sequences aka. generators, with all data bound. - + LazyValue: An object which, when given new data, can make many values. - An `sp.Expr`, which might need `simplify`ing, `jax` JIT'ing, unit cancellations, variable substitutions, etc. before use. - Lazy objects, for which all parameters aren't yet known. - - A computational graph aka. `aesara`, which may even need to be handled before - + - A computational graph aka. `aesara`, which may even need to be handled before + Capabilities: A `ValueCapability` object providing compatibility. - + # Value Data Flow Simply passing values is the simplest and easiest use case. - + This doesn't mean it's "dumb" - ex. a `sp.Expr` might, before use, have `simplify`, rewriting, unit cancellation, etc. run. All of this is okay, as long as there is no *introduction of new data* ex. variable substitutions. - - + + # Lazy Value Data Flow By passing (essentially) functions, one supports: - **Lightness**: While lazy values can be made expensive to construct, they will generally not be nearly as heavy to handle when trying to work with ex. operations on voxel arrays. - **Performance**: Parameterizing ex. `sp.Expr` with variables allows one to build very optimized functions, which can make ex. node graph updates very fast if the only operation run is the `jax` JIT'ed function (aka. GPU accelerated) generated from the final full expression. - **Numerical Stability**: Libraries like `aesara` build a computational graph, which can be automatically rewritten to avoid many obvious conditioning / cancellation errors. - **Lazy Output**: The goal of a node-graph may not be the definition of a single value, but rather, a parameterized expression for generating *many values* with known properties. This is especially interesting for use cases where one wishes to build an optimization step using nodes. - - + + # Capability Passing By being able to pass "capabilities" next to other kinds of values, nodes can quickly determine whether a given link is valid without having to actually compute it. - - + + # Lazy Parameter Value When using parameterized LazyValues, one may wish to independently pass parameter values through the graph, so they can be inserted into the final (cached) high-performance expression without. - + The advantage of using a different data flow would be changing this kind of value would ONLY invalidate lazy parameter value caches, which would allow an incredibly fast path of getting the value into the lazy expression for high-performance computation. - + Implementation TBD - though, ostensibly, one would have a "parameter" node which both would only provide a LazyValue (aka. a symbolic variable), but would also be able to provide a LazyParamValue, which would be a particular value of some kind (probably via the `value` of some other node socket). """ - + Value = enum.auto() LazyValue = enum.auto() Capabilities = enum.auto() - + LazyParamValue = enum.auto() diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/icons.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/icons.py index 04fdb16..9ece465 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/icons.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/icons.py @@ -1,4 +1,5 @@ from ....utils.blender_type_enum import BlenderTypeEnum + class Icon(BlenderTypeEnum): - SimNodeEditor = "MOD_SIMPLEDEFORM" + SimNodeEditor = 'MOD_SIMPLEDEFORM' diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/managed_obj_type.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/managed_obj_type.py index 9dad6f4..e744b07 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/managed_obj_type.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/managed_obj_type.py @@ -1,8 +1,7 @@ import enum -from ....utils.blender_type_enum import ( - BlenderTypeEnum -) +from ....utils.blender_type_enum import BlenderTypeEnum + class ManagedObjType(BlenderTypeEnum): ManagedBLObject = enum.auto() diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cat_labels.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cat_labels.py index 05d406c..580dead 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cat_labels.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cat_labels.py @@ -2,48 +2,39 @@ from .node_cats import NodeCategory as NC NODE_CAT_LABELS = { # Inputs/ - NC.MAXWELLSIM_INPUTS: "Inputs", - NC.MAXWELLSIM_INPUTS_IMPORTERS: "Importers", - NC.MAXWELLSIM_INPUTS_SCENE: "Scene", - NC.MAXWELLSIM_INPUTS_PARAMETERS: "Parameters", - NC.MAXWELLSIM_INPUTS_CONSTANTS: "Constants", - NC.MAXWELLSIM_INPUTS_LISTS: "Lists", - + NC.MAXWELLSIM_INPUTS: 'Inputs', + NC.MAXWELLSIM_INPUTS_IMPORTERS: 'Importers', + NC.MAXWELLSIM_INPUTS_SCENE: 'Scene', + NC.MAXWELLSIM_INPUTS_PARAMETERS: 'Parameters', + NC.MAXWELLSIM_INPUTS_CONSTANTS: 'Constants', + NC.MAXWELLSIM_INPUTS_LISTS: 'Lists', # Outputs/ - NC.MAXWELLSIM_OUTPUTS: "Outputs", - NC.MAXWELLSIM_OUTPUTS_VIEWERS: "Viewers", - NC.MAXWELLSIM_OUTPUTS_EXPORTERS: "Exporters", - NC.MAXWELLSIM_OUTPUTS_PLOTTERS: "Plotters", - + NC.MAXWELLSIM_OUTPUTS: 'Outputs', + NC.MAXWELLSIM_OUTPUTS_VIEWERS: 'Viewers', + NC.MAXWELLSIM_OUTPUTS_EXPORTERS: 'Exporters', + NC.MAXWELLSIM_OUTPUTS_PLOTTERS: 'Plotters', # Sources/ - NC.MAXWELLSIM_SOURCES: "Sources", - NC.MAXWELLSIM_SOURCES_TEMPORALSHAPES: "Temporal Shapes", - + NC.MAXWELLSIM_SOURCES: 'Sources', + NC.MAXWELLSIM_SOURCES_TEMPORALSHAPES: 'Temporal Shapes', # Mediums/ - NC.MAXWELLSIM_MEDIUMS: "Mediums", - NC.MAXWELLSIM_MEDIUMS_NONLINEARITIES: "Non-Linearities", - + NC.MAXWELLSIM_MEDIUMS: 'Mediums', + NC.MAXWELLSIM_MEDIUMS_NONLINEARITIES: 'Non-Linearities', # Structures/ - NC.MAXWELLSIM_STRUCTURES: "Structures", - NC.MAXWELLSIM_STRUCTURES_PRIMITIVES: "Primitives", - + NC.MAXWELLSIM_STRUCTURES: 'Structures', + NC.MAXWELLSIM_STRUCTURES_PRIMITIVES: 'Primitives', # Bounds/ - NC.MAXWELLSIM_BOUNDS: "Bounds", - NC.MAXWELLSIM_BOUNDS_BOUNDCONDS: "Bound Conds", - + NC.MAXWELLSIM_BOUNDS: 'Bounds', + NC.MAXWELLSIM_BOUNDS_BOUNDCONDS: 'Bound Conds', # Monitors/ - NC.MAXWELLSIM_MONITORS: "Monitors", - NC.MAXWELLSIM_MONITORS_NEARFIELDPROJECTIONS: "Near-Field Projections", - + NC.MAXWELLSIM_MONITORS: 'Monitors', + NC.MAXWELLSIM_MONITORS_NEARFIELDPROJECTIONS: 'Near-Field Projections', # Simulations/ - NC.MAXWELLSIM_SIMS: "Simulations", - NC.MAXWELLSIM_SIMGRIDAXES: "Sim Grid Axes", - + NC.MAXWELLSIM_SIMS: 'Simulations', + NC.MAXWELLSIM_SIMGRIDAXES: 'Sim Grid Axes', # Utilities/ - NC.MAXWELLSIM_UTILITIES: "Utilities", - NC.MAXWELLSIM_UTILITIES_CONVERTERS: "Converters", - NC.MAXWELLSIM_UTILITIES_OPERATIONS: "Operations", - + NC.MAXWELLSIM_UTILITIES: 'Utilities', + NC.MAXWELLSIM_UTILITIES_CONVERTERS: 'Converters', + NC.MAXWELLSIM_UTILITIES_OPERATIONS: 'Operations', # Viz/ - NC.MAXWELLSIM_VIZ: "Viz", + NC.MAXWELLSIM_VIZ: 'Viz', } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cats.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cats.py index be8ffb4..35e2b4c 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cats.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_cats.py @@ -1,13 +1,12 @@ import enum -from ....utils.blender_type_enum import ( - BlenderTypeEnum, wrap_values_in_MT -) +from ....utils.blender_type_enum import BlenderTypeEnum, wrap_values_in_MT + @wrap_values_in_MT class NodeCategory(BlenderTypeEnum): MAXWELLSIM = enum.auto() - + # Inputs/ MAXWELLSIM_INPUTS = enum.auto() MAXWELLSIM_INPUTS_IMPORTERS = enum.auto() @@ -15,63 +14,63 @@ class NodeCategory(BlenderTypeEnum): MAXWELLSIM_INPUTS_PARAMETERS = enum.auto() MAXWELLSIM_INPUTS_CONSTANTS = enum.auto() MAXWELLSIM_INPUTS_LISTS = enum.auto() - + # Outputs/ MAXWELLSIM_OUTPUTS = enum.auto() MAXWELLSIM_OUTPUTS_VIEWERS = enum.auto() MAXWELLSIM_OUTPUTS_EXPORTERS = enum.auto() MAXWELLSIM_OUTPUTS_PLOTTERS = enum.auto() - + # Sources/ MAXWELLSIM_SOURCES = enum.auto() MAXWELLSIM_SOURCES_TEMPORALSHAPES = enum.auto() - + # Mediums/ MAXWELLSIM_MEDIUMS = enum.auto() MAXWELLSIM_MEDIUMS_NONLINEARITIES = enum.auto() - + # Structures/ MAXWELLSIM_STRUCTURES = enum.auto() MAXWELLSIM_STRUCTURES_PRIMITIVES = enum.auto() - + # Bounds/ MAXWELLSIM_BOUNDS = enum.auto() MAXWELLSIM_BOUNDS_BOUNDCONDS = enum.auto() - + # Monitors/ MAXWELLSIM_MONITORS = enum.auto() MAXWELLSIM_MONITORS_NEARFIELDPROJECTIONS = enum.auto() - + # Simulations/ MAXWELLSIM_SIMS = enum.auto() MAXWELLSIM_SIMGRIDAXES = enum.auto() - + # Utilities/ MAXWELLSIM_UTILITIES = enum.auto() MAXWELLSIM_UTILITIES_CONVERTERS = enum.auto() MAXWELLSIM_UTILITIES_OPERATIONS = enum.auto() - + # Viz/ MAXWELLSIM_VIZ = enum.auto() - + @classmethod def get_tree(cls): ## TODO: Refactor syllable_categories = [ - str(node_category.value).split("_") + str(node_category.value).split('_') for node_category in cls - if node_category.value != "MAXWELLSIM" + if node_category.value != 'MAXWELLSIM' ] - + category_tree = {} for syllable_category in syllable_categories: # Set Current Subtree to Root current_category_subtree = category_tree - + for i, syllable in enumerate(syllable_category): # Create New Category Subtree and/or Step to Subtree if syllable not in current_category_subtree: current_category_subtree[syllable] = {} current_category_subtree = current_category_subtree[syllable] - + return category_tree diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_types.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_types.py index de0a5bc..0883fc6 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_types.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/node_types.py @@ -1,58 +1,58 @@ import enum from ....utils.blender_type_enum import ( - BlenderTypeEnum, append_cls_name_to_values + BlenderTypeEnum, + append_cls_name_to_values, ) + @append_cls_name_to_values class NodeType(BlenderTypeEnum): KitchenSink = enum.auto() - + # Inputs UnitSystem = enum.auto() - + ## Inputs / Scene Time = enum.auto() - + ## Inputs / Importers Tidy3DWebImporter = enum.auto() - + ## Inputs / Parameters NumberParameter = enum.auto() PhysicalParameter = enum.auto() - + ## Inputs / Constants WaveConstant = enum.auto() ScientificConstant = enum.auto() NumberConstant = enum.auto() PhysicalConstant = enum.auto() BlenderConstant = enum.auto() - + ## Inputs / Lists RealList = enum.auto() ComplexList = enum.auto() - - ## Inputs / + + ## Inputs / InputFile = enum.auto() - - + # Outputs ## Outputs / Viewers Viewer = enum.auto() ValueViewer = enum.auto() ConsoleViewer = enum.auto() - + ## Outputs / Exporters JSONFileExporter = enum.auto() Tidy3DWebExporter = enum.auto() - - + # Sources ## Sources / Temporal Shapes GaussianPulseTemporalShape = enum.auto() ContinuousWaveTemporalShape = enum.auto() ListTemporalShape = enum.auto() - + ## Sources / PointDipoleSource = enum.auto() UniformCurrentSource = enum.auto() @@ -61,92 +61,86 @@ class NodeType(BlenderTypeEnum): GaussianBeamSource = enum.auto() AstigmaticGaussianBeamSource = enum.auto() TFSFSource = enum.auto() - + EHEquivalenceSource = enum.auto() EHSource = enum.auto() - + # Mediums LibraryMedium = enum.auto() - + PECMedium = enum.auto() IsotropicMedium = enum.auto() AnisotropicMedium = enum.auto() - + TripleSellmeierMedium = enum.auto() SellmeierMedium = enum.auto() PoleResidueMedium = enum.auto() DrudeMedium = enum.auto() DrudeLorentzMedium = enum.auto() DebyeMedium = enum.auto() - + ## Mediums / Non-Linearities AddNonLinearity = enum.auto() ChiThreeSusceptibilityNonLinearity = enum.auto() TwoPhotonAbsorptionNonLinearity = enum.auto() KerrNonLinearity = enum.auto() - + # Structures ObjectStructure = enum.auto() GeoNodesStructure = enum.auto() ScriptedStructure = enum.auto() - + ## Structures / Primitives BoxStructure = enum.auto() SphereStructure = enum.auto() CylinderStructure = enum.auto() - - + # Bounds BoundConds = enum.auto() - + ## Bounds / Bound Faces PMLBoundCond = enum.auto() PECBoundCond = enum.auto() PMCBoundCond = enum.auto() - + BlochBoundCond = enum.auto() PeriodicBoundCond = enum.auto() AbsorbingBoundCond = enum.auto() - - + # Monitors EHFieldMonitor = enum.auto() FieldPowerFluxMonitor = enum.auto() EpsilonTensorMonitor = enum.auto() DiffractionMonitor = enum.auto() - + ## Monitors / Near-Field Projections CartesianNearFieldProjectionMonitor = enum.auto() ObservationAngleNearFieldProjectionMonitor = enum.auto() KSpaceNearFieldProjectionMonitor = enum.auto() - - + # Sims SimDomain = enum.auto() SimGrid = enum.auto() - + ## Sims / Sim Grid Axis AutomaticSimGridAxis = enum.auto() ManualSimGridAxis = enum.auto() UniformSimGridAxis = enum.auto() ArraySimGridAxis = enum.auto() - + ## Sim / FDTDSim = enum.auto() - - + # Utilities Combine = enum.auto() Separate = enum.auto() Math = enum.auto() - + ## Utilities / Converters WaveConverter = enum.auto() - + ## Utilities / Operations ArrayOperation = enum.auto() - - - + # Viz FDTDSimDataViz = enum.auto() diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/schemas/managed_obj.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/schemas/managed_obj.py index da3cfa4..e0a3b77 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/schemas/managed_obj.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/schemas/managed_obj.py @@ -6,21 +6,19 @@ from ..managed_obj_type import ManagedObjType class ManagedObj(typ.Protocol): managed_obj_type: ManagedObjType - + def __init__( self, name: ManagedObjName, - ): - ... - + ): ... + @property def name(self) -> str: ... @name.setter def name(self, value: str): ... - - def free(self): - ... - + + def free(self): ... + def bl_select(self): """If this is a managed Blender object, and the operation "select this in Blender" makes sense, then do so. diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/schemas/managed_obj_def.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/schemas/managed_obj_def.py index 1854c58..9e80804 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/schemas/managed_obj_def.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/schemas/managed_obj_def.py @@ -6,6 +6,7 @@ import pydantic as pyd from ..bl import PresetName, SocketName, BLEnumID from .managed_obj import ManagedObj + class ManagedObjDef(pyd.BaseModel): mk: typ.Callable[[str], ManagedObj] - name_prefix: str = "" + name_prefix: str = '' diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/schemas/preset_def.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/schemas/preset_def.py index 695d5e8..7089809 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/schemas/preset_def.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/schemas/preset_def.py @@ -4,6 +4,7 @@ import pydantic as pyd from ..bl import PresetName, SocketName, BLEnumID + class PresetDef(pyd.BaseModel): label: PresetName description: str diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_colors.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_colors.py index 61d970e..8f198d9 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_colors.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_colors.py @@ -10,13 +10,11 @@ SOCKET_COLORS = { ST.Bool: (0.7, 0.7, 0.7, 1.0), # Medium Light Grey ST.String: (0.7, 0.7, 0.7, 1.0), # Medium Light Grey ST.FilePath: (0.6, 0.6, 0.6, 1.0), # Medium Grey - # Number ST.IntegerNumber: (0.5, 0.5, 1.0, 1.0), # Light Blue ST.RationalNumber: (0.4, 0.4, 0.9, 1.0), # Medium Light Blue ST.RealNumber: (0.3, 0.3, 0.8, 1.0), # Medium Blue ST.ComplexNumber: (0.2, 0.2, 0.7, 1.0), # Dark Blue - # Vector ST.Integer2DVector: (0.5, 1.0, 0.5, 1.0), # Light Green ST.Real2DVector: (0.5, 1.0, 0.5, 1.0), # Light Green @@ -24,7 +22,6 @@ SOCKET_COLORS = { ST.Integer3DVector: (0.3, 0.8, 0.3, 1.0), # Medium Green ST.Real3DVector: (0.3, 0.8, 0.3, 1.0), # Medium Green ST.Complex3DVector: (0.2, 0.7, 0.2, 1.0), # Dark Green - # Physical ST.PhysicalUnitSystem: (1.0, 0.5, 0.5, 1.0), # Light Red ST.PhysicalTime: (1.0, 0.5, 0.5, 1.0), # Light Red @@ -44,14 +41,12 @@ SOCKET_COLORS = { ST.PhysicalForce3D: (0.6, 0.45, 0.25, 1.0), # Medium Dark Orange ST.PhysicalPol: (0.5, 0.4, 0.2, 1.0), # Dark Orange ST.PhysicalFreq: (1.0, 0.7, 0.5, 1.0), # Light Peach - # Blender ST.BlenderObject: (0.7, 0.5, 1.0, 1.0), # Light Purple ST.BlenderCollection: (0.6, 0.45, 0.9, 1.0), # Medium Light Purple ST.BlenderImage: (0.5, 0.4, 0.8, 1.0), # Medium Purple ST.BlenderGeoNodes: (0.3, 0.3, 0.6, 1.0), # Dark Purple ST.BlenderText: (0.5, 0.5, 0.75, 1.0), # Light Lavender - # Maxwell ST.MaxwellSource: (1.0, 1.0, 0.5, 1.0), # Light Yellow ST.MaxwellTemporalShape: (0.9, 0.9, 0.45, 1.0), # Medium Light Yellow @@ -66,8 +61,6 @@ SOCKET_COLORS = { ST.MaxwellSimGrid: (0.5, 0.4, 0.3, 1.0), # Dark Gold ST.MaxwellSimGridAxis: (0.4, 0.3, 0.25, 1.0), # Darkest Gold ST.MaxwellSimDomain: (0.4, 0.3, 0.25, 1.0), # Darkest Gold - # Tidy3D ST.Tidy3DCloudTask: (0.4, 0.3, 0.25, 1.0), # Darkest Gold } - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_from_bl_desc.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_from_bl_desc.py index 0a68a5d..07a72ec 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_from_bl_desc.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_from_bl_desc.py @@ -1,78 +1,62 @@ from .socket_types import SocketType as ST -BL_SOCKET_DESCR_ANNOT_STRING = ":: " +BL_SOCKET_DESCR_ANNOT_STRING = ':: ' BL_SOCKET_DESCR_TYPE_MAP = { - ("Time", "NodeSocketFloat", 1): ST.PhysicalTime, - - ("Angle", "NodeSocketFloat", 1): ST.PhysicalAngle, - ("SolidAngle", "NodeSocketFloat", 1): ST.PhysicalSolidAngle, - - ("Rotation", "NodeSocketVector", 2): ST.PhysicalRot2D, - ("Rotation", "NodeSocketVector", 3): ST.PhysicalRot3D, - - ("Freq", "NodeSocketFloat", 1): ST.PhysicalFreq, - ("AngFreq", "NodeSocketFloat", 1): ST.PhysicalAngFreq, - + ('Time', 'NodeSocketFloat', 1): ST.PhysicalTime, + ('Angle', 'NodeSocketFloat', 1): ST.PhysicalAngle, + ('SolidAngle', 'NodeSocketFloat', 1): ST.PhysicalSolidAngle, + ('Rotation', 'NodeSocketVector', 2): ST.PhysicalRot2D, + ('Rotation', 'NodeSocketVector', 3): ST.PhysicalRot3D, + ('Freq', 'NodeSocketFloat', 1): ST.PhysicalFreq, + ('AngFreq', 'NodeSocketFloat', 1): ST.PhysicalAngFreq, ## Cartesian - ("Length", "NodeSocketFloat", 1): ST.PhysicalLength, - ("Area", "NodeSocketFloat", 1): ST.PhysicalArea, - ("Volume", "NodeSocketFloat", 1): ST.PhysicalVolume, - - ("Disp", "NodeSocketVector", 2): ST.PhysicalDisp2D, - ("Disp", "NodeSocketVector", 3): ST.PhysicalDisp3D, - - ("Point", "NodeSocketFloat", 1): ST.PhysicalPoint1D, - ("Point", "NodeSocketVector", 2): ST.PhysicalPoint2D, - ("Point", "NodeSocketVector", 3): ST.PhysicalPoint3D, - - ("Size", "NodeSocketVector", 2): ST.PhysicalSize2D, - ("Size", "NodeSocketVector", 3): ST.PhysicalSize3D, - + ('Length', 'NodeSocketFloat', 1): ST.PhysicalLength, + ('Area', 'NodeSocketFloat', 1): ST.PhysicalArea, + ('Volume', 'NodeSocketFloat', 1): ST.PhysicalVolume, + ('Disp', 'NodeSocketVector', 2): ST.PhysicalDisp2D, + ('Disp', 'NodeSocketVector', 3): ST.PhysicalDisp3D, + ('Point', 'NodeSocketFloat', 1): ST.PhysicalPoint1D, + ('Point', 'NodeSocketVector', 2): ST.PhysicalPoint2D, + ('Point', 'NodeSocketVector', 3): ST.PhysicalPoint3D, + ('Size', 'NodeSocketVector', 2): ST.PhysicalSize2D, + ('Size', 'NodeSocketVector', 3): ST.PhysicalSize3D, ## Mechanical - ("Mass", "NodeSocketFloat", 1): ST.PhysicalMass, - - ("Speed", "NodeSocketFloat", 1): ST.PhysicalSpeed, - ("Vel", "NodeSocketVector", 2): ST.PhysicalVel2D, - ("Vel", "NodeSocketVector", 3): ST.PhysicalVel3D, - ("Accel", "NodeSocketFloat", 1): ST.PhysicalAccelScalar, - ("Accel", "NodeSocketVector", 2): ST.PhysicalAccel2D, - ("Accel", "NodeSocketVector", 3): ST.PhysicalAccel3D, - ("Force", "NodeSocketFloat", 1): ST.PhysicalForceScalar, - ("Force", "NodeSocketVector", 2): ST.PhysicalForce2D, - ("Force", "NodeSocketVector", 3): ST.PhysicalForce3D, - ("Pressure", "NodeSocketFloat", 1): ST.PhysicalPressure, - + ('Mass', 'NodeSocketFloat', 1): ST.PhysicalMass, + ('Speed', 'NodeSocketFloat', 1): ST.PhysicalSpeed, + ('Vel', 'NodeSocketVector', 2): ST.PhysicalVel2D, + ('Vel', 'NodeSocketVector', 3): ST.PhysicalVel3D, + ('Accel', 'NodeSocketFloat', 1): ST.PhysicalAccelScalar, + ('Accel', 'NodeSocketVector', 2): ST.PhysicalAccel2D, + ('Accel', 'NodeSocketVector', 3): ST.PhysicalAccel3D, + ('Force', 'NodeSocketFloat', 1): ST.PhysicalForceScalar, + ('Force', 'NodeSocketVector', 2): ST.PhysicalForce2D, + ('Force', 'NodeSocketVector', 3): ST.PhysicalForce3D, + ('Pressure', 'NodeSocketFloat', 1): ST.PhysicalPressure, ## Energetic - ("Energy", "NodeSocketFloat", 1): ST.PhysicalEnergy, - ("Power", "NodeSocketFloat", 1): ST.PhysicalPower, - ("Temp", "NodeSocketFloat", 1): ST.PhysicalTemp, - + ('Energy', 'NodeSocketFloat', 1): ST.PhysicalEnergy, + ('Power', 'NodeSocketFloat', 1): ST.PhysicalPower, + ('Temp', 'NodeSocketFloat', 1): ST.PhysicalTemp, ## ELectrodynamical - ("Curr", "NodeSocketFloat", 1): ST.PhysicalCurr, - ("CurrDens", "NodeSocketVector", 2): ST.PhysicalCurrDens2D, - ("CurrDens", "NodeSocketVector", 3): ST.PhysicalCurrDens3D, - - ("Charge", "NodeSocketFloat", 1): ST.PhysicalCharge, - ("Voltage", "NodeSocketFloat", 1): ST.PhysicalVoltage, - ("Capacitance", "NodeSocketFloat", 1): ST.PhysicalCapacitance, - ("Resistance", "NodeSocketFloat", 1): ST.PhysicalResistance, - ("Conductance", "NodeSocketFloat", 1): ST.PhysicalConductance, - - ("MagFlux", "NodeSocketFloat", 1): ST.PhysicalMagFlux, - ("MagFluxDens", "NodeSocketFloat", 1): ST.PhysicalMagFluxDens, - ("Inductance", "NodeSocketFloat", 1): ST.PhysicalInductance, - - ("EField", "NodeSocketFloat", 2): ST.PhysicalEField3D, - ("EField", "NodeSocketFloat", 3): ST.PhysicalEField2D, - ("HField", "NodeSocketFloat", 2): ST.PhysicalHField3D, - ("HField", "NodeSocketFloat", 3): ST.PhysicalHField2D, - + ('Curr', 'NodeSocketFloat', 1): ST.PhysicalCurr, + ('CurrDens', 'NodeSocketVector', 2): ST.PhysicalCurrDens2D, + ('CurrDens', 'NodeSocketVector', 3): ST.PhysicalCurrDens3D, + ('Charge', 'NodeSocketFloat', 1): ST.PhysicalCharge, + ('Voltage', 'NodeSocketFloat', 1): ST.PhysicalVoltage, + ('Capacitance', 'NodeSocketFloat', 1): ST.PhysicalCapacitance, + ('Resistance', 'NodeSocketFloat', 1): ST.PhysicalResistance, + ('Conductance', 'NodeSocketFloat', 1): ST.PhysicalConductance, + ('MagFlux', 'NodeSocketFloat', 1): ST.PhysicalMagFlux, + ('MagFluxDens', 'NodeSocketFloat', 1): ST.PhysicalMagFluxDens, + ('Inductance', 'NodeSocketFloat', 1): ST.PhysicalInductance, + ('EField', 'NodeSocketFloat', 2): ST.PhysicalEField3D, + ('EField', 'NodeSocketFloat', 3): ST.PhysicalEField2D, + ('HField', 'NodeSocketFloat', 2): ST.PhysicalHField3D, + ('HField', 'NodeSocketFloat', 3): ST.PhysicalHField2D, ## Luminal - ("LumIntensity", "NodeSocketFloat", 1): ST.PhysicalLumIntensity, - ("LumFlux", "NodeSocketFloat", 1): ST.PhysicalLumFlux, - ("Illuminance", "NodeSocketFloat", 1): ST.PhysicalIlluminance, - + ('LumIntensity', 'NodeSocketFloat', 1): ST.PhysicalLumIntensity, + ('LumFlux', 'NodeSocketFloat', 1): ST.PhysicalLumFlux, + ('Illuminance', 'NodeSocketFloat', 1): ST.PhysicalIlluminance, ## Optical - ("PolJones", "NodeSocketFloat", 2): ST.PhysicalPolJones, - ("Pol", "NodeSocketFloat", 4): ST.PhysicalPol, + ('PolJones', 'NodeSocketFloat', 2): ST.PhysicalPolJones, + ('Pol', 'NodeSocketFloat', 4): ST.PhysicalPol, } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_from_bl_direct.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_from_bl_direct.py index d48be3a..239f4a6 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_from_bl_direct.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_from_bl_direct.py @@ -1,36 +1,33 @@ from .socket_types import SocketType as ST BL_SOCKET_DIRECT_TYPE_MAP = { - ("NodeSocketString", 1): ST.String, - ("NodeSocketBool", 1): ST.Bool, - ("NodeSocketCollection", 1): ST.BlenderCollection, - ("NodeSocketImage", 1): ST.BlenderImage, - ("NodeSocketObject", 1): ST.BlenderObject, - - ("NodeSocketFloat", 1): ST.RealNumber, - #("NodeSocketFloatAngle", 1): ST.PhysicalAngle, - #("NodeSocketFloatDistance", 1): ST.PhysicalLength, - ("NodeSocketFloatFactor", 1): ST.RealNumber, - ("NodeSocketFloatPercentage", 1): ST.RealNumber, - #("NodeSocketFloatTime", 1): ST.PhysicalTime, - #("NodeSocketFloatTimeAbsolute", 1): ST.PhysicalTime, - - ("NodeSocketInt", 1): ST.IntegerNumber, - ("NodeSocketIntFactor", 1): ST.IntegerNumber, - ("NodeSocketIntPercentage", 1): ST.IntegerNumber, - ("NodeSocketIntUnsigned", 1): ST.IntegerNumber, - - ("NodeSocketRotation", 2): ST.PhysicalRot2D, - ("NodeSocketColor", 3): ST.Color, - ("NodeSocketVector", 2): ST.Real2DVector, - ("NodeSocketVector", 3): ST.Real3DVector, - #("NodeSocketVectorAcceleration", 2): ST.PhysicalAccel2D, - #("NodeSocketVectorAcceleration", 3): ST.PhysicalAccel3D, - #("NodeSocketVectorDirection", 2): ST.Real2DVectorDir, - #("NodeSocketVectorDirection", 3): ST.Real3DVectorDir, - ("NodeSocketVectorEuler", 2): ST.PhysicalRot2D, - ("NodeSocketVectorEuler", 3): ST.PhysicalRot3D, - #("NodeSocketVectorTranslation", 3): ST.PhysicalDisp3D, - #("NodeSocketVectorVelocity", 3): ST.PhysicalVel3D, - #("NodeSocketVectorXYZ", 3): ST.PhysicalPoint3D, + ('NodeSocketString', 1): ST.String, + ('NodeSocketBool', 1): ST.Bool, + ('NodeSocketCollection', 1): ST.BlenderCollection, + ('NodeSocketImage', 1): ST.BlenderImage, + ('NodeSocketObject', 1): ST.BlenderObject, + ('NodeSocketFloat', 1): ST.RealNumber, + # ("NodeSocketFloatAngle", 1): ST.PhysicalAngle, + # ("NodeSocketFloatDistance", 1): ST.PhysicalLength, + ('NodeSocketFloatFactor', 1): ST.RealNumber, + ('NodeSocketFloatPercentage', 1): ST.RealNumber, + # ("NodeSocketFloatTime", 1): ST.PhysicalTime, + # ("NodeSocketFloatTimeAbsolute", 1): ST.PhysicalTime, + ('NodeSocketInt', 1): ST.IntegerNumber, + ('NodeSocketIntFactor', 1): ST.IntegerNumber, + ('NodeSocketIntPercentage', 1): ST.IntegerNumber, + ('NodeSocketIntUnsigned', 1): ST.IntegerNumber, + ('NodeSocketRotation', 2): ST.PhysicalRot2D, + ('NodeSocketColor', 3): ST.Color, + ('NodeSocketVector', 2): ST.Real2DVector, + ('NodeSocketVector', 3): ST.Real3DVector, + # ("NodeSocketVectorAcceleration", 2): ST.PhysicalAccel2D, + # ("NodeSocketVectorAcceleration", 3): ST.PhysicalAccel3D, + # ("NodeSocketVectorDirection", 2): ST.Real2DVectorDir, + # ("NodeSocketVectorDirection", 3): ST.Real3DVectorDir, + ('NodeSocketVectorEuler', 2): ST.PhysicalRot2D, + ('NodeSocketVectorEuler', 3): ST.PhysicalRot3D, + # ("NodeSocketVectorTranslation", 3): ST.PhysicalDisp3D, + # ("NodeSocketVectorVelocity", 3): ST.PhysicalVel3D, + # ("NodeSocketVectorXYZ", 3): ST.PhysicalPoint3D, } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_shapes.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_shapes.py index 6d6ba84..09b2f76 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_shapes.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_shapes.py @@ -2,67 +2,61 @@ from .socket_types import SocketType as ST SOCKET_SHAPES = { # Basic - ST.Any: "CIRCLE", - ST.Bool: "CIRCLE", - ST.String: "CIRCLE", - ST.FilePath: "CIRCLE", - + ST.Any: 'CIRCLE', + ST.Bool: 'CIRCLE', + ST.String: 'CIRCLE', + ST.FilePath: 'CIRCLE', # Number - ST.IntegerNumber: "CIRCLE", - ST.RationalNumber: "CIRCLE", - ST.RealNumber: "CIRCLE", - ST.ComplexNumber: "CIRCLE", - + ST.IntegerNumber: 'CIRCLE', + ST.RationalNumber: 'CIRCLE', + ST.RealNumber: 'CIRCLE', + ST.ComplexNumber: 'CIRCLE', # Vector - ST.Integer2DVector: "CIRCLE", - ST.Real2DVector: "CIRCLE", - ST.Complex2DVector: "CIRCLE", - ST.Integer3DVector: "CIRCLE", - ST.Real3DVector: "CIRCLE", - ST.Complex3DVector: "CIRCLE", - + ST.Integer2DVector: 'CIRCLE', + ST.Real2DVector: 'CIRCLE', + ST.Complex2DVector: 'CIRCLE', + ST.Integer3DVector: 'CIRCLE', + ST.Real3DVector: 'CIRCLE', + ST.Complex3DVector: 'CIRCLE', # Physical - ST.PhysicalUnitSystem: "CIRCLE", - ST.PhysicalTime: "CIRCLE", - ST.PhysicalAngle: "CIRCLE", - ST.PhysicalLength: "CIRCLE", - ST.PhysicalArea: "CIRCLE", - ST.PhysicalVolume: "CIRCLE", - ST.PhysicalPoint2D: "CIRCLE", - ST.PhysicalPoint3D: "CIRCLE", - ST.PhysicalSize2D: "CIRCLE", - ST.PhysicalSize3D: "CIRCLE", - ST.PhysicalMass: "CIRCLE", - ST.PhysicalSpeed: "CIRCLE", - ST.PhysicalAccelScalar: "CIRCLE", - ST.PhysicalForceScalar: "CIRCLE", - ST.PhysicalAccel3D: "CIRCLE", - ST.PhysicalForce3D: "CIRCLE", - ST.PhysicalPol: "CIRCLE", - ST.PhysicalFreq: "CIRCLE", - + ST.PhysicalUnitSystem: 'CIRCLE', + ST.PhysicalTime: 'CIRCLE', + ST.PhysicalAngle: 'CIRCLE', + ST.PhysicalLength: 'CIRCLE', + ST.PhysicalArea: 'CIRCLE', + ST.PhysicalVolume: 'CIRCLE', + ST.PhysicalPoint2D: 'CIRCLE', + ST.PhysicalPoint3D: 'CIRCLE', + ST.PhysicalSize2D: 'CIRCLE', + ST.PhysicalSize3D: 'CIRCLE', + ST.PhysicalMass: 'CIRCLE', + ST.PhysicalSpeed: 'CIRCLE', + ST.PhysicalAccelScalar: 'CIRCLE', + ST.PhysicalForceScalar: 'CIRCLE', + ST.PhysicalAccel3D: 'CIRCLE', + ST.PhysicalForce3D: 'CIRCLE', + ST.PhysicalPol: 'CIRCLE', + ST.PhysicalFreq: 'CIRCLE', # Blender - ST.BlenderObject: "DIAMOND", - ST.BlenderCollection: "DIAMOND", - ST.BlenderImage: "DIAMOND", - ST.BlenderGeoNodes: "DIAMOND", - ST.BlenderText: "DIAMOND", - + ST.BlenderObject: 'DIAMOND', + ST.BlenderCollection: 'DIAMOND', + ST.BlenderImage: 'DIAMOND', + ST.BlenderGeoNodes: 'DIAMOND', + ST.BlenderText: 'DIAMOND', # Maxwell - ST.MaxwellSource: "CIRCLE", - ST.MaxwellTemporalShape: "CIRCLE", - ST.MaxwellMedium: "CIRCLE", - ST.MaxwellMediumNonLinearity: "CIRCLE", - ST.MaxwellStructure: "CIRCLE", - ST.MaxwellBoundConds: "CIRCLE", - ST.MaxwellBoundCond: "CIRCLE", - ST.MaxwellMonitor: "CIRCLE", - ST.MaxwellFDTDSim: "CIRCLE", - ST.MaxwellFDTDSimData: "CIRCLE", - ST.MaxwellSimGrid: "CIRCLE", - ST.MaxwellSimGridAxis: "CIRCLE", - ST.MaxwellSimDomain: "CIRCLE", - + ST.MaxwellSource: 'CIRCLE', + ST.MaxwellTemporalShape: 'CIRCLE', + ST.MaxwellMedium: 'CIRCLE', + ST.MaxwellMediumNonLinearity: 'CIRCLE', + ST.MaxwellStructure: 'CIRCLE', + ST.MaxwellBoundConds: 'CIRCLE', + ST.MaxwellBoundCond: 'CIRCLE', + ST.MaxwellMonitor: 'CIRCLE', + ST.MaxwellFDTDSim: 'CIRCLE', + ST.MaxwellFDTDSimData: 'CIRCLE', + ST.MaxwellSimGrid: 'CIRCLE', + ST.MaxwellSimGridAxis: 'CIRCLE', + ST.MaxwellSimDomain: 'CIRCLE', # Tidy3D - ST.Tidy3DCloudTask: "DIAMOND", + ST.Tidy3DCloudTask: 'DIAMOND', } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_types.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_types.py index 79c9598..ce894c6 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_types.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_types.py @@ -1,9 +1,12 @@ import enum from ....utils.blender_type_enum import ( - BlenderTypeEnum, append_cls_name_to_values, wrap_values_in_MT + BlenderTypeEnum, + append_cls_name_to_values, + wrap_values_in_MT, ) + @append_cls_name_to_values class SocketType(BlenderTypeEnum): # Base @@ -12,43 +15,43 @@ class SocketType(BlenderTypeEnum): String = enum.auto() FilePath = enum.auto() Color = enum.auto() - + # Number IntegerNumber = enum.auto() RationalNumber = enum.auto() RealNumber = enum.auto() ComplexNumber = enum.auto() - + # Vector Integer2DVector = enum.auto() Real2DVector = enum.auto() Real2DVectorDir = enum.auto() Complex2DVector = enum.auto() - + Integer3DVector = enum.auto() Real3DVector = enum.auto() Real3DVectorDir = enum.auto() Complex3DVector = enum.auto() - + # Blender BlenderObject = enum.auto() BlenderCollection = enum.auto() - + BlenderImage = enum.auto() - + BlenderGeoNodes = enum.auto() BlenderText = enum.auto() - + # Maxwell MaxwellBoundConds = enum.auto() MaxwellBoundCond = enum.auto() - + MaxwellMedium = enum.auto() MaxwellMediumNonLinearity = enum.auto() - + MaxwellSource = enum.auto() MaxwellTemporalShape = enum.auto() - + MaxwellStructure = enum.auto() MaxwellMonitor = enum.auto() @@ -57,42 +60,42 @@ class SocketType(BlenderTypeEnum): MaxwellSimDomain = enum.auto() MaxwellSimGrid = enum.auto() MaxwellSimGridAxis = enum.auto() - + # Tidy3D Tidy3DCloudTask = enum.auto() - + # Physical PhysicalUnitSystem = enum.auto() - + PhysicalTime = enum.auto() - + PhysicalAngle = enum.auto() PhysicalSolidAngle = enum.auto() - + PhysicalRot2D = enum.auto() PhysicalRot3D = enum.auto() - + PhysicalFreq = enum.auto() PhysicalAngFreq = enum.auto() - + ## Cartesian PhysicalLength = enum.auto() PhysicalArea = enum.auto() PhysicalVolume = enum.auto() - + PhysicalDisp2D = enum.auto() PhysicalDisp3D = enum.auto() - + PhysicalPoint1D = enum.auto() PhysicalPoint2D = enum.auto() PhysicalPoint3D = enum.auto() - + PhysicalSize2D = enum.auto() PhysicalSize3D = enum.auto() - + ## Mechanical PhysicalMass = enum.auto() - + PhysicalSpeed = enum.auto() PhysicalVel2D = enum.auto() PhysicalVel3D = enum.auto() @@ -103,37 +106,37 @@ class SocketType(BlenderTypeEnum): PhysicalForce2D = enum.auto() PhysicalForce3D = enum.auto() PhysicalPressure = enum.auto() - + ## Energetic PhysicalEnergy = enum.auto() PhysicalPower = enum.auto() PhysicalTemp = enum.auto() - + ## Electrodynamical PhysicalCurr = enum.auto() PhysicalCurrDens2D = enum.auto() PhysicalCurrDens3D = enum.auto() - + PhysicalCharge = enum.auto() PhysicalVoltage = enum.auto() PhysicalCapacitance = enum.auto() PhysicalResistance = enum.auto() PhysicalConductance = enum.auto() - + PhysicalMagFlux = enum.auto() PhysicalMagFluxDens = enum.auto() PhysicalInductance = enum.auto() - + PhysicalEField2D = enum.auto() PhysicalEField3D = enum.auto() PhysicalHField2D = enum.auto() PhysicalHField3D = enum.auto() - + ## Luminal PhysicalLumIntensity = enum.auto() PhysicalLumFlux = enum.auto() PhysicalIlluminance = enum.auto() - + ## Optical PhysicalPolJones = enum.auto() PhysicalPol = enum.auto() diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_units.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_units.py index cf58462..156dec0 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_units.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/socket_units.py @@ -5,262 +5,255 @@ from .socket_types import SocketType as ST SOCKET_UNITS = { ST.PhysicalTime: { - "default": "PS", - "values": { - "FS": spux.femtosecond, - "PS": spu.picosecond, - "NS": spu.nanosecond, - "MS": spu.microsecond, - "MLSEC": spu.millisecond, - "SEC": spu.second, - "MIN": spu.minute, - "HOUR": spu.hour, - "DAY": spu.day, + 'default': 'PS', + 'values': { + 'FS': spux.femtosecond, + 'PS': spu.picosecond, + 'NS': spu.nanosecond, + 'MS': spu.microsecond, + 'MLSEC': spu.millisecond, + 'SEC': spu.second, + 'MIN': spu.minute, + 'HOUR': spu.hour, + 'DAY': spu.day, }, }, - ST.PhysicalAngle: { - "default": "RADIAN", - "values": { - "RADIAN": spu.radian, - "DEGREE": spu.degree, - "STERAD": spu.steradian, - "ANGMIL": spu.angular_mil, + 'default': 'RADIAN', + 'values': { + 'RADIAN': spu.radian, + 'DEGREE': spu.degree, + 'STERAD': spu.steradian, + 'ANGMIL': spu.angular_mil, }, }, - ST.PhysicalLength: { - "default": "UM", - "values": { - "PM": spu.picometer, - "A": spu.angstrom, - "NM": spu.nanometer, - "UM": spu.micrometer, - "MM": spu.millimeter, - "CM": spu.centimeter, - "M": spu.meter, - "INCH": spu.inch, - "FOOT": spu.foot, - "YARD": spu.yard, - "MILE": spu.mile, + 'default': 'UM', + 'values': { + 'PM': spu.picometer, + 'A': spu.angstrom, + 'NM': spu.nanometer, + 'UM': spu.micrometer, + 'MM': spu.millimeter, + 'CM': spu.centimeter, + 'M': spu.meter, + 'INCH': spu.inch, + 'FOOT': spu.foot, + 'YARD': spu.yard, + 'MILE': spu.mile, }, }, ST.PhysicalArea: { - "default": "UM_SQ", - "values": { - "PM_SQ": spu.picometer**2, - "A_SQ": spu.angstrom**2, - "NM_SQ": spu.nanometer**2, - "UM_SQ": spu.micrometer**2, - "MM_SQ": spu.millimeter**2, - "CM_SQ": spu.centimeter**2, - "M_SQ": spu.meter**2, - "INCH_SQ": spu.inch**2, - "FOOT_SQ": spu.foot**2, - "YARD_SQ": spu.yard**2, - "MILE_SQ": spu.mile**2, + 'default': 'UM_SQ', + 'values': { + 'PM_SQ': spu.picometer**2, + 'A_SQ': spu.angstrom**2, + 'NM_SQ': spu.nanometer**2, + 'UM_SQ': spu.micrometer**2, + 'MM_SQ': spu.millimeter**2, + 'CM_SQ': spu.centimeter**2, + 'M_SQ': spu.meter**2, + 'INCH_SQ': spu.inch**2, + 'FOOT_SQ': spu.foot**2, + 'YARD_SQ': spu.yard**2, + 'MILE_SQ': spu.mile**2, }, }, ST.PhysicalVolume: { - "default": "UM_CB", - "values": { - "PM_CB": spu.picometer**3, - "A_CB": spu.angstrom**3, - "NM_CB": spu.nanometer**3, - "UM_CB": spu.micrometer**3, - "MM_CB": spu.millimeter**3, - "CM_CB": spu.centimeter**3, - "M_CB": spu.meter**3, - "ML": spu.milliliter, - "L": spu.liter, - "INCH_CB": spu.inch**3, - "FOOT_CB": spu.foot**3, - "YARD_CB": spu.yard**3, - "MILE_CB": spu.mile**3, + 'default': 'UM_CB', + 'values': { + 'PM_CB': spu.picometer**3, + 'A_CB': spu.angstrom**3, + 'NM_CB': spu.nanometer**3, + 'UM_CB': spu.micrometer**3, + 'MM_CB': spu.millimeter**3, + 'CM_CB': spu.centimeter**3, + 'M_CB': spu.meter**3, + 'ML': spu.milliliter, + 'L': spu.liter, + 'INCH_CB': spu.inch**3, + 'FOOT_CB': spu.foot**3, + 'YARD_CB': spu.yard**3, + 'MILE_CB': spu.mile**3, }, }, - ST.PhysicalPoint2D: { - "default": "UM", - "values": { - "PM": spu.picometer, - "A": spu.angstrom, - "NM": spu.nanometer, - "UM": spu.micrometer, - "MM": spu.millimeter, - "CM": spu.centimeter, - "M": spu.meter, - "INCH": spu.inch, - "FOOT": spu.foot, - "YARD": spu.yard, - "MILE": spu.mile, + 'default': 'UM', + 'values': { + 'PM': spu.picometer, + 'A': spu.angstrom, + 'NM': spu.nanometer, + 'UM': spu.micrometer, + 'MM': spu.millimeter, + 'CM': spu.centimeter, + 'M': spu.meter, + 'INCH': spu.inch, + 'FOOT': spu.foot, + 'YARD': spu.yard, + 'MILE': spu.mile, }, }, ST.PhysicalPoint3D: { - "default": "UM", - "values": { - "PM": spu.picometer, - "A": spu.angstrom, - "NM": spu.nanometer, - "UM": spu.micrometer, - "MM": spu.millimeter, - "CM": spu.centimeter, - "M": spu.meter, - "INCH": spu.inch, - "FOOT": spu.foot, - "YARD": spu.yard, - "MILE": spu.mile, + 'default': 'UM', + 'values': { + 'PM': spu.picometer, + 'A': spu.angstrom, + 'NM': spu.nanometer, + 'UM': spu.micrometer, + 'MM': spu.millimeter, + 'CM': spu.centimeter, + 'M': spu.meter, + 'INCH': spu.inch, + 'FOOT': spu.foot, + 'YARD': spu.yard, + 'MILE': spu.mile, }, }, - ST.PhysicalSize2D: { - "default": "UM", - "values": { - "PM": spu.picometer, - "A": spu.angstrom, - "NM": spu.nanometer, - "UM": spu.micrometer, - "MM": spu.millimeter, - "CM": spu.centimeter, - "M": spu.meter, - "INCH": spu.inch, - "FOOT": spu.foot, - "YARD": spu.yard, - "MILE": spu.mile, + 'default': 'UM', + 'values': { + 'PM': spu.picometer, + 'A': spu.angstrom, + 'NM': spu.nanometer, + 'UM': spu.micrometer, + 'MM': spu.millimeter, + 'CM': spu.centimeter, + 'M': spu.meter, + 'INCH': spu.inch, + 'FOOT': spu.foot, + 'YARD': spu.yard, + 'MILE': spu.mile, }, }, ST.PhysicalSize3D: { - "default": "UM", - "values": { - "PM": spu.picometer, - "A": spu.angstrom, - "NM": spu.nanometer, - "UM": spu.micrometer, - "MM": spu.millimeter, - "CM": spu.centimeter, - "M": spu.meter, - "INCH": spu.inch, - "FOOT": spu.foot, - "YARD": spu.yard, - "MILE": spu.mile, + 'default': 'UM', + 'values': { + 'PM': spu.picometer, + 'A': spu.angstrom, + 'NM': spu.nanometer, + 'UM': spu.micrometer, + 'MM': spu.millimeter, + 'CM': spu.centimeter, + 'M': spu.meter, + 'INCH': spu.inch, + 'FOOT': spu.foot, + 'YARD': spu.yard, + 'MILE': spu.mile, }, }, - ST.PhysicalMass: { - "default": "UG", - "values": { - "E_REST": spu.electron_rest_mass, - "DAL": spu.dalton, - "UG": spu.microgram, - "MG": spu.milligram, - "G": spu.gram, - "KG": spu.kilogram, - "TON": spu.metric_ton, + 'default': 'UG', + 'values': { + 'E_REST': spu.electron_rest_mass, + 'DAL': spu.dalton, + 'UG': spu.microgram, + 'MG': spu.milligram, + 'G': spu.gram, + 'KG': spu.kilogram, + 'TON': spu.metric_ton, }, }, - ST.PhysicalSpeed: { - "default": "UM_S", - "values": { - "PM_S": spu.picometer / spu.second, - "NM_S": spu.nanometer / spu.second, - "UM_S": spu.micrometer / spu.second, - "MM_S": spu.millimeter / spu.second, - "M_S": spu.meter / spu.second, - "KM_S": spu.kilometer / spu.second, - "KM_H": spu.kilometer / spu.hour, - "FT_S": spu.feet / spu.second, - "MI_H": spu.mile / spu.hour, + 'default': 'UM_S', + 'values': { + 'PM_S': spu.picometer / spu.second, + 'NM_S': spu.nanometer / spu.second, + 'UM_S': spu.micrometer / spu.second, + 'MM_S': spu.millimeter / spu.second, + 'M_S': spu.meter / spu.second, + 'KM_S': spu.kilometer / spu.second, + 'KM_H': spu.kilometer / spu.hour, + 'FT_S': spu.feet / spu.second, + 'MI_H': spu.mile / spu.hour, }, }, ST.PhysicalAccelScalar: { - "default": "UM_S_SQ", - "values": { - "PM_S_SQ": spu.picometer / spu.second**2, - "NM_S_SQ": spu.nanometer / spu.second**2, - "UM_S_SQ": spu.micrometer / spu.second**2, - "MM_S_SQ": spu.millimeter / spu.second**2, - "M_S_SQ": spu.meter / spu.second**2, - "KM_S_SQ": spu.kilometer / spu.second**2, - "FT_S_SQ": spu.feet / spu.second**2, + 'default': 'UM_S_SQ', + 'values': { + 'PM_S_SQ': spu.picometer / spu.second**2, + 'NM_S_SQ': spu.nanometer / spu.second**2, + 'UM_S_SQ': spu.micrometer / spu.second**2, + 'MM_S_SQ': spu.millimeter / spu.second**2, + 'M_S_SQ': spu.meter / spu.second**2, + 'KM_S_SQ': spu.kilometer / spu.second**2, + 'FT_S_SQ': spu.feet / spu.second**2, }, }, ST.PhysicalForceScalar: { - "default": "UNEWT", - "values": { - "KG_M_S_SQ": spu.kg * spu.m/spu.second**2, - "NNEWT": spux.nanonewton, - "UNEWT": spux.micronewton, - "MNEWT": spux.millinewton, - "NEWT": spu.newton, + 'default': 'UNEWT', + 'values': { + 'KG_M_S_SQ': spu.kg * spu.m / spu.second**2, + 'NNEWT': spux.nanonewton, + 'UNEWT': spux.micronewton, + 'MNEWT': spux.millinewton, + 'NEWT': spu.newton, }, }, ST.PhysicalAccel3D: { - "default": "UM_S_SQ", - "values": { - "PM_S_SQ": spu.picometer / spu.second**2, - "NM_S_SQ": spu.nanometer / spu.second**2, - "UM_S_SQ": spu.micrometer / spu.second**2, - "MM_S_SQ": spu.millimeter / spu.second**2, - "M_S_SQ": spu.meter / spu.second**2, - "KM_S_SQ": spu.kilometer / spu.second**2, - "FT_S_SQ": spu.feet / spu.second**2, + 'default': 'UM_S_SQ', + 'values': { + 'PM_S_SQ': spu.picometer / spu.second**2, + 'NM_S_SQ': spu.nanometer / spu.second**2, + 'UM_S_SQ': spu.micrometer / spu.second**2, + 'MM_S_SQ': spu.millimeter / spu.second**2, + 'M_S_SQ': spu.meter / spu.second**2, + 'KM_S_SQ': spu.kilometer / spu.second**2, + 'FT_S_SQ': spu.feet / spu.second**2, }, }, ST.PhysicalForce3D: { - "default": "UNEWT", - "values": { - "KG_M_S_SQ": spu.kg * spu.m/spu.second**2, - "NNEWT": spux.nanonewton, - "UNEWT": spux.micronewton, - "MNEWT": spux.millinewton, - "NEWT": spu.newton, + 'default': 'UNEWT', + 'values': { + 'KG_M_S_SQ': spu.kg * spu.m / spu.second**2, + 'NNEWT': spux.nanonewton, + 'UNEWT': spux.micronewton, + 'MNEWT': spux.millinewton, + 'NEWT': spu.newton, }, }, - ST.PhysicalFreq: { - "default": "THZ", - "values": { - "HZ": spu.hertz, - "KHZ": spux.kilohertz, - "MHZ": spux.megahertz, - "GHZ": spux.gigahertz, - "THZ": spux.terahertz, - "PHZ": spux.petahertz, - "EHZ": spux.exahertz, + 'default': 'THZ', + 'values': { + 'HZ': spu.hertz, + 'KHZ': spux.kilohertz, + 'MHZ': spux.megahertz, + 'GHZ': spux.gigahertz, + 'THZ': spux.terahertz, + 'PHZ': spux.petahertz, + 'EHZ': spux.exahertz, }, }, ST.PhysicalPol: { - "default": "RADIAN", - "values": { - "RADIAN": spu.radian, - "DEGREE": spu.degree, - "STERAD": spu.steradian, - "ANGMIL": spu.angular_mil, + 'default': 'RADIAN', + 'values': { + 'RADIAN': spu.radian, + 'DEGREE': spu.degree, + 'STERAD': spu.steradian, + 'ANGMIL': spu.angular_mil, }, }, ST.MaxwellMedium: { - "default": "NM", - "values": { - "PM": spu.picometer, ## c(vac) = wl*freq - "A": spu.angstrom, - "NM": spu.nanometer, - "UM": spu.micrometer, - "MM": spu.millimeter, - "CM": spu.centimeter, - "M": spu.meter, + 'default': 'NM', + 'values': { + 'PM': spu.picometer, ## c(vac) = wl*freq + 'A': spu.angstrom, + 'NM': spu.nanometer, + 'UM': spu.micrometer, + 'MM': spu.millimeter, + 'CM': spu.centimeter, + 'M': spu.meter, }, }, ST.MaxwellMonitor: { - "default": "NM", - "values": { - "PM": spu.picometer, ## c(vac) = wl*freq - "A": spu.angstrom, - "NM": spu.nanometer, - "UM": spu.micrometer, - "MM": spu.millimeter, - "CM": spu.centimeter, - "M": spu.meter, + 'default': 'NM', + 'values': { + 'PM': spu.picometer, ## c(vac) = wl*freq + 'A': spu.angstrom, + 'NM': spu.nanometer, + 'UM': spu.micrometer, + 'MM': spu.millimeter, + 'CM': spu.centimeter, + 'M': spu.meter, }, }, } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/trees.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/trees.py index 3ec9833..1e972ec 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/trees.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/contracts/trees.py @@ -1,9 +1,11 @@ import enum from ....utils.blender_type_enum import ( - BlenderTypeEnum, append_cls_name_to_values + BlenderTypeEnum, + append_cls_name_to_values, ) + @append_cls_name_to_values class TreeType(BlenderTypeEnum): MaxwellSim = enum.auto() diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_image.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_image.py index ec36aa8..b274f65 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_image.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_image.py @@ -10,48 +10,49 @@ import bpy from .. import contracts as ct -AREA_TYPE = "IMAGE_EDITOR" -SPACE_TYPE = "IMAGE_EDITOR" +AREA_TYPE = 'IMAGE_EDITOR' +SPACE_TYPE = 'IMAGE_EDITOR' + class ManagedBLImage(ct.schemas.ManagedObj): managed_obj_type = ct.ManagedObjType.ManagedBLImage _bl_image_name: str - + def __init__(self, name: str): ## TODO: Check that blender doesn't have any other images by the same name. self._bl_image_name = name - + @property def name(self): return self._bl_image_name - + @name.setter def name(self, value: str): - # Image Doesn't Exist + # Image Doesn't Exist if not (bl_image := bpy.data.images.get(self._bl_image_name)): # ...AND Desired Image Name is Not Taken if not bpy.data.objects.get(value): self._bl_image_name = value return - + # ...AND Desired Image Name is Taken else: - msg = f"Desired name {value} for BL image is taken" + msg = f'Desired name {value} for BL image is taken' raise ValueError(msg) - + # Object DOES Exist bl_image.name = value self._bl_image_name = bl_image.name ## - When name exists, Blender adds .### to prevent overlap. ## - `set_name` is allowed to change the name; nodes account for this. - + def free(self): if not (bl_image := bpy.data.images.get(self.name)): msg = "Can't free BL image that doesn't exist" raise ValueError(msg) - + bpy.data.images.remove(bl_image) - + #################### # - Managed Object Management #################### @@ -59,27 +60,24 @@ class ManagedBLImage(ct.schemas.ManagedObj): self, width_px: int, height_px: int, - color_model: typx.Literal["RGB", "RGBA"], - dtype: typx.Literal["uint8", "float32"], + color_model: typx.Literal['RGB', 'RGBA'], + dtype: typx.Literal['uint8', 'float32'], ): """Returns the managed blender image. - + If the requested image properties are different from the image's, then delete the old image make a new image with correct geometry. """ - channels = 4 if color_model == "RGBA" else 3 - + channels = 4 if color_model == 'RGBA' else 3 + # Remove Image (if mismatch) - if ( - (bl_image := bpy.data.images.get(self.name)) - and ( - bl_image.size[0] != width_px - or bl_image.size[1] != height_px - or bl_image.channels != channels - or bl_image.is_float ^ (dtype == "float32") - ) + if (bl_image := bpy.data.images.get(self.name)) and ( + bl_image.size[0] != width_px + or bl_image.size[1] != height_px + or bl_image.channels != channels + or bl_image.is_float ^ (dtype == 'float32') ): self.free() - + # Create Image w/Geometry (if none exists) if not (bl_image := bpy.data.images.get(self.name)): bl_image = bpy.data.images.new( @@ -87,9 +85,9 @@ class ManagedBLImage(ct.schemas.ManagedObj): width=width_px, height=height_px, ) - + return bl_image - + #################### # - Editor UX Manipulation #################### @@ -99,25 +97,23 @@ class ManagedBLImage(ct.schemas.ManagedObj): If none are valid, return None. """ valid_areas = [ - area - for area in bpy.context.screen.areas - if area.type == AREA_TYPE + area for area in bpy.context.screen.areas if area.type == AREA_TYPE ] if valid_areas: return valid_areas[0] - + @property def preview_space(self) -> bpy.types.SpaceProperties: """Returns the visible preview space in the visible preview area of the Blender UI """ - if (preview_area := self.preview_area): + if preview_area := self.preview_area: return next( space for space in preview_area.spaces if space.type == SPACE_TYPE ) - + #################### # - Actions #################### @@ -125,9 +121,9 @@ class ManagedBLImage(ct.schemas.ManagedObj): """Synchronizes the managed object to the preview, by manipulating relevant editors. """ - if (bl_image := bpy.data.images.get(self.name)): + if bl_image := bpy.data.images.get(self.name): self.preview_space.image = bl_image - + #################### # - Special Methods #################### @@ -140,37 +136,37 @@ class ManagedBLImage(ct.schemas.ManagedObj): bl_select: bool = False, ): import matplotlib.pyplot as plt - + # Compute Image Geometry - if (preview_area := self.preview_area): + if preview_area := self.preview_area: # Retrieve DPI from Blender Preferences _dpi = bpy.context.preferences.system.dpi - + # Retrieve Image Geometry from Area width_px = preview_area.width height_px = preview_area.height - + # Compute Inches _width_inches = width_px / _dpi _height_inches = height_px / _dpi - + elif width_inches and height_inches and dpi: # Copy Parameters _dpi = dpi _width_inches = height_inches _height_inches = height_inches - + # Compute Pixel Geometry width_px = int(_width_inches * _dpi) height_px = int(_height_inches * _dpi) - + else: - msg = f"There must either be a preview area, or defined `width_inches`, `height_inches`, and `dpi`" + msg = f'There must either be a preview area, or defined `width_inches`, `height_inches`, and `dpi`' raise ValueError(msg) - + # Compute Plot Dimensions aspect_ratio = _width_inches / _height_inches - + # Create MPL Figure, Axes, and Compute Figure Geometry fig, ax = plt.subplots( figsize=[_width_inches, _height_inches], @@ -179,10 +175,10 @@ class ManagedBLImage(ct.schemas.ManagedObj): ax.set_aspect(aspect_ratio) cmp_width_px, cmp_height_px = fig.canvas.get_width_height() ## Use computed pixel w/h to preempt off-by-one size errors. - + # Plot w/User Parameter func_plotter(ax) - + # Save Figure to BytesIO with io.BytesIO() as buff: fig.savefig(buff, format='raw', dpi=dpi) @@ -191,15 +187,14 @@ class ManagedBLImage(ct.schemas.ManagedObj): buff.getvalue(), dtype=np.uint8, ).reshape([cmp_height_px, cmp_width_px, -1]) - + image_data = np.flipud(image_data).astype(np.float32) / 255 plt.close(fig) - + # Optimized Write to Blender Image - bl_image = self.bl_image(cmp_width_px, cmp_height_px, "RGBA", "uint8") + bl_image = self.bl_image(cmp_width_px, cmp_height_px, 'RGBA', 'uint8') bl_image.pixels.foreach_set(image_data.ravel()) bl_image.update() - + if bl_select: self.bl_select() - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_object.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_object.py index 35c234e..72dd4e3 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_object.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/managed_objs/managed_bl_object.py @@ -13,13 +13,14 @@ import bmesh from .. import contracts as ct -ModifierType = typx.Literal["NODES", "ARRAY"] +ModifierType = typx.Literal['NODES', 'ARRAY'] MODIFIER_NAMES = { - "NODES": "BLMaxwell_GeoNodes", - "ARRAY": "BLMaxwell_Array", + 'NODES': 'BLMaxwell_GeoNodes', + 'ARRAY': 'BLMaxwell_Array', } -MANAGED_COLLECTION_NAME = "BLMaxwell" -PREVIEW_COLLECTION_NAME = "BLMaxwell Visible" +MANAGED_COLLECTION_NAME = 'BLMaxwell' +PREVIEW_COLLECTION_NAME = 'BLMaxwell Visible' + def bl_collection( collection_name: str, view_layer_exclude: bool @@ -27,113 +28,133 @@ def bl_collection( # Init the "Managed Collection" # Ensure Collection exists (and is in the Scene collection) if collection_name not in bpy.data.collections: - collection = bpy.data.collections.new(collection_name) + collection = bpy.data.collections.new(collection_name) bpy.context.scene.collection.children.link(collection) else: collection = bpy.data.collections[collection_name] - + ## Ensure synced View Layer exclusion - if (layer_collection := bpy.context.view_layer.layer_collection.children[ - collection_name - ]).exclude != view_layer_exclude: + if ( + layer_collection := bpy.context.view_layer.layer_collection.children[ + collection_name + ] + ).exclude != view_layer_exclude: layer_collection.exclude = view_layer_exclude - + return collection + class ManagedBLObject(ct.schemas.ManagedObj): managed_obj_type = ct.ManagedObjType.ManagedBLObject _bl_object_name: str - + def __init__(self, name: str): self._bl_object_name = name - + # Object Name @property def name(self): return self._bl_object_name - + @name.setter def set_name(self, value: str) -> None: - # Object Doesn't Exist + # Object Doesn't Exist if not (bl_object := bpy.data.objects.get(self._bl_object_name)): # ...AND Desired Object Name is Not Taken if not bpy.data.objects.get(value): self._bl_object_name = value return - + # ...AND Desired Object Name is Taken else: - msg = f"Desired name {value} for BL object is taken" + msg = f'Desired name {value} for BL object is taken' raise ValueError(msg) - + # Object DOES Exist bl_object.name = value self._bl_object_name = bl_object.name ## - When name exists, Blender adds .### to prevent overlap. ## - `set_name` is allowed to change the name; nodes account for this. - + # Object Datablock Name @property def bl_mesh_name(self): return self.name - + @property def bl_volume_name(self): return self.name - + # Deallocation def free(self): if not (bl_object := bpy.data.objects.get(self.name)): return ## Nothing to do - + # Delete the Underlying Datablock ## This automatically deletes the object too - if bl_object.type == "MESH": + if bl_object.type == 'MESH': bpy.data.meshes.remove(bl_object.data) - elif bl_object.type == "EMPTY": + elif bl_object.type == 'EMPTY': bpy.data.meshes.remove(bl_object.data) - elif bl_object.type == "VOLUME": + elif bl_object.type == 'VOLUME': bpy.data.volumes.remove(bl_object.data) else: - msg = f"Type of to-delete `bl_object`, {bl_object.type}, is not valid" + msg = f'Type of to-delete `bl_object`, {bl_object.type}, is not valid' raise ValueError(msg) - + #################### # - Actions #################### def show_preview( self, - kind: typx.Literal["MESH", "EMPTY", "VOLUME"], + kind: typx.Literal['MESH', 'EMPTY', 'VOLUME'], empty_display_type: typx.Literal[ - "PLAIN_AXES", "ARROWS", "SINGLE_ARROW", "CIRCLE", "CUBE", - "SPHERE", "CONE", "IMAGE", - ] | None = None, + 'PLAIN_AXES', + 'ARROWS', + 'SINGLE_ARROW', + 'CIRCLE', + 'CUBE', + 'SPHERE', + 'CONE', + 'IMAGE', + ] + | None = None, ) -> None: """Moves the managed Blender object to the preview collection. - + If it's already included, do nothing. """ bl_object = self.bl_object(kind) - if bl_object.name not in (preview_collection := bl_collection( - PREVIEW_COLLECTION_NAME, view_layer_exclude=False - )).objects: + if ( + bl_object.name + not in ( + preview_collection := bl_collection( + PREVIEW_COLLECTION_NAME, view_layer_exclude=False + ) + ).objects + ): preview_collection.objects.link(bl_object) - - if kind == "EMPTY" and empty_display_type is not None: + + if kind == 'EMPTY' and empty_display_type is not None: bl_object.empty_display_type = empty_display_type - + def hide_preview( self, - kind: typx.Literal["MESH", "EMPTY", "VOLUME"], + kind: typx.Literal['MESH', 'EMPTY', 'VOLUME'], ) -> None: """Removes the managed Blender object from the preview collection. - + If it's already removed, do nothing. """ bl_object = self.bl_object(kind) - if bl_object.name not in (preview_collection := bl_collection( - PREVIEW_COLLECTION_NAME, view_layer_exclude=False - )).objects: + if ( + bl_object.name + not in ( + preview_collection := bl_collection( + PREVIEW_COLLECTION_NAME, view_layer_exclude=False + ) + ).objects + ): preview_collection.objects.unlink(bl_object) def bl_select(self) -> None: @@ -141,68 +162,68 @@ class ManagedBLObject(ct.schemas.ManagedObj): outlined in the 3D viewport. """ if not (bl_object := bpy.data.objects.get(self.name)): - msg = "Managed BLObject does not exist" + msg = 'Managed BLObject does not exist' raise ValueError(msg) - + bpy.ops.object.select_all(action='DESELECT') bl_object.select_set(True) - + #################### # - Managed Object Management #################### def bl_object( self, - kind: typx.Literal["MESH", "EMPTY", "VOLUME"], + kind: typx.Literal['MESH', 'EMPTY', 'VOLUME'], ): """Returns the managed blender object. - + If the requested object data type is different, then delete the old object and recreate. """ # Remove Object (if mismatch) if ( - (bl_object := bpy.data.objects.get(self.name)) - and bl_object.type != kind - ): + bl_object := bpy.data.objects.get(self.name) + ) and bl_object.type != kind: self.free() - + # Create Object w/Appropriate Data Block if not (bl_object := bpy.data.objects.get(self.name)): - if kind == "MESH": + if kind == 'MESH': bl_data = bpy.data.meshes.new(self.bl_mesh_name) - elif kind == "EMPTY": + elif kind == 'EMPTY': bl_data = None - elif kind == "VOLUME": + elif kind == 'VOLUME': raise NotImplementedError else: - msg = f"Requested `bl_object` type {bl_object.type} is not valid" + msg = ( + f'Requested `bl_object` type {bl_object.type} is not valid' + ) raise ValueError(msg) - + bl_object = bpy.data.objects.new(self.name, bl_data) bl_collection( MANAGED_COLLECTION_NAME, view_layer_exclude=True ).objects.link(bl_object) - + return bl_object - + #################### # - Mesh Data Properties #################### @property def raw_mesh(self) -> bpy.types.Mesh: """Returns the object's raw mesh data. - + Raises an error if the object has no mesh data. """ if ( - (bl_object := bpy.data.objects.get(self.name)) - and bl_object.type == "MESH" - ): + bl_object := bpy.data.objects.get(self.name) + ) and bl_object.type == 'MESH': return bl_object.data - - msg = f"Requested MESH data from `bl_object` of type {bl_object.type}" + + msg = f'Requested MESH data from `bl_object` of type {bl_object.type}' raise ValueError(msg) - + @contextlib.contextmanager def mesh_as_bmesh( self, @@ -210,9 +231,8 @@ class ManagedBLObject(ct.schemas.ManagedObj): triangulate: bool = False, ) -> bpy.types.Mesh: if ( - (bl_object := bpy.data.objects.get(self.name)) - and bl_object.type == "MESH" - ): + bl_object := bpy.data.objects.get(self.name) + ) and bl_object.type == 'MESH': bmesh_mesh = None try: bmesh_mesh = bmesh.new() @@ -223,52 +243,53 @@ class ManagedBLObject(ct.schemas.ManagedObj): ) else: bmesh_mesh.from_object(bl_object) - + if triangulate: bmesh.ops.triangulate(bmesh_mesh, faces=bmesh_mesh.faces) - + yield bmesh_mesh - + finally: - if bmesh_mesh: bmesh_mesh.free() - + if bmesh_mesh: + bmesh_mesh.free() + else: - msg = f"Requested BMesh from `bl_object` of type {bl_object.type}" + msg = f'Requested BMesh from `bl_object` of type {bl_object.type}' raise ValueError(msg) - + @property def mesh_as_arrays(self) -> dict: ## TODO: Cached - + # Ensure Updated Geometry bpy.context.view_layer.update() ## TODO: Must we? - + # Compute Evaluted + Triangulated Mesh - _mesh = bpy.data.meshes.new(name="TemporaryMesh") + _mesh = bpy.data.meshes.new(name='TemporaryMesh') with self.mesh_as_bmesh(evaluate=True, triangulate=True) as bmesh_mesh: bmesh_mesh.to_mesh(_mesh) - + # Optimized Vertex Copy ## See - verts = np.zeros(3 * len(_mesh.vertices), dtype=np.float64) - _mesh.vertices.foreach_get('co', verts) + verts = np.zeros(3 * len(_mesh.vertices), dtype=np.float64) + _mesh.vertices.foreach_get('co', verts) verts.shape = (-1, 3) - + # Optimized Triangle Copy ## To understand, read it, **carefully**. - faces = np.zeros(3 * len(_mesh.polygons), dtype=np.uint64) - _mesh.polygons.foreach_get('vertices', faces) + faces = np.zeros(3 * len(_mesh.polygons), dtype=np.uint64) + _mesh.polygons.foreach_get('vertices', faces) faces.shape = (-1, 3) - + # Remove Temporary Mesh bpy.data.meshes.remove(_mesh) - + return { - "verts": verts, - "faces": faces, + 'verts': verts, + 'faces': faces, } - + #################### # - Modifier Methods #################### @@ -277,13 +298,13 @@ class ManagedBLObject(ct.schemas.ManagedObj): modifier_type: ModifierType, ): """Creates a new modifier for the current `bl_object`. - + For all Blender modifier type names, see: """ if not (bl_object := bpy.data.objects.get(self.name)): msg = "Can't add modifier to BL object that doesn't exist" raise ValueError(msg) - + # (Create and) Return Modifier bl_modifier_name = MODIFIER_NAMES[modifier_type] if bl_modifier_name not in bl_object.modifiers: @@ -292,33 +313,33 @@ class ManagedBLObject(ct.schemas.ManagedObj): type=modifier_type, ) return bl_object.modifiers[bl_modifier_name] - + def modifier_attrs(self, modifier_type: ModifierType) -> dict: """Based on the modifier type, retrieve a representative dictionary of modifier attributes. The attributes can then easily be set using `setattr`. """ bl_modifier = self.bl_modifier(modifier_type) - - if modifier_type == "NODES": + + if modifier_type == 'NODES': return { - "node_group": bl_modifier.node_group, + 'node_group': bl_modifier.node_group, } - elif modifier_type == "ARRAY": + elif modifier_type == 'ARRAY': raise NotImplementedError - + def s_modifier_attrs( self, modifier_type: ModifierType, modifier_attrs: dict, ): bl_modifier = self.bl_modifier(modifier_type) - - if modifier_type == "NODES": - if bl_modifier.node_group != modifier_attrs["node_group"]: - bl_modifier.node_group = modifier_attrs["node_group"] - elif modifier_type == "ARRAY": + + if modifier_type == 'NODES': + if bl_modifier.node_group != modifier_attrs['node_group']: + bl_modifier.node_group = modifier_attrs['node_group'] + elif modifier_type == 'ARRAY': raise NotImplementedError - + #################### # - GeoNodes Modifier #################### @@ -328,65 +349,64 @@ class ManagedBLObject(ct.schemas.ManagedObj): geonodes_identifier_to_value: dict, ): """Push the given GeoNodes Interface values to a GeoNodes modifier attached to a managed MESH object. - + The values must be compatible with the `default_value`s of the interface sockets. - + If there is no object, it is created. If the object isn't a MESH object, it is made so. If the GeoNodes modifier doesn't exist, it is created. If the GeoNodes node group doesn't match, it is changed. Only differing interface values are actually changed. """ - bl_object = self.bl_object("MESH") - + bl_object = self.bl_object('MESH') + # Get (/make) a GeoModes Modifier - bl_modifier = self.bl_modifier("NODES") - + bl_modifier = self.bl_modifier('NODES') + # Set GeoNodes Modifier Attributes (specifically, the 'node_group') - self.s_modifier_attrs("NODES", {"node_group": geonodes_node_group}) - + self.s_modifier_attrs('NODES', {'node_group': geonodes_node_group}) + # Set GeoNodes Values modifier_altered = False - for interface_identifier, value in ( - geonodes_identifier_to_value.items() - ): + for ( + interface_identifier, + value, + ) in geonodes_identifier_to_value.items(): if bl_modifier[interface_identifier] != value: # Quickly Determine if IDPropertyArray is Equal - if hasattr( - bl_modifier[interface_identifier], - "to_list" - ) and tuple( - bl_modifier[interface_identifier].to_list() - ) == value: + if ( + hasattr(bl_modifier[interface_identifier], 'to_list') + and tuple(bl_modifier[interface_identifier].to_list()) + == value + ): continue - + # Quickly Determine int/float Mismatch if isinstance( bl_modifier[interface_identifier], float, ) and isinstance(value, int): value = float(value) - + bl_modifier[interface_identifier] = value - + modifier_altered = True - + # Update DepGraph (if anything changed) if modifier_altered: bl_object.data.update() - - - #@property - #def volume(self) -> bpy.types.Volume: - # """Returns the object's volume data. - # - # Raises an error if the object has no volume data. - # """ - # if ( - # (bl_object := bpy.data.objects.get(self.bl_object_name)) - # and bl_object.type == "VOLUME" - # ): - # return bl_object.data + + # @property + # def volume(self) -> bpy.types.Volume: + # """Returns the object's volume data. # - # msg = f"Requested VOLUME data from `bl_object` of type {bl_object.type}" - # raise ValueError(msg) + # Raises an error if the object has no volume data. + # """ + # if ( + # (bl_object := bpy.data.objects.get(self.bl_object_name)) + # and bl_object.type == "VOLUME" + # ): + # return bl_object.data + # + # msg = f"Requested VOLUME data from `bl_object` of type {bl_object.type}" + # raise ValueError(msg) diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/__init__.py index b6b44ed..11dbee7 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/__init__.py @@ -1,37 +1,38 @@ -#from . import kitchen_sink +# from . import kitchen_sink from . import inputs from . import outputs from . import sources from . import mediums from . import structures -#from . import bounds + +# from . import bounds from . import monitors from . import simulations from . import utilities from . import viz BL_REGISTER = [ - #*kitchen_sink.BL_REGISTER, + # *kitchen_sink.BL_REGISTER, *inputs.BL_REGISTER, *outputs.BL_REGISTER, *sources.BL_REGISTER, *mediums.BL_REGISTER, *structures.BL_REGISTER, -# *bounds.BL_REGISTER, + # *bounds.BL_REGISTER, *monitors.BL_REGISTER, *simulations.BL_REGISTER, *utilities.BL_REGISTER, *viz.BL_REGISTER, ] BL_NODES = { - #**kitchen_sink.BL_NODES, + # **kitchen_sink.BL_NODES, **inputs.BL_NODES, **outputs.BL_NODES, **sources.BL_NODES, **mediums.BL_NODES, **structures.BL_NODES, -# **bounds.BL_NODES, + # **bounds.BL_NODES, **monitors.BL_NODES, **simulations.BL_NODES, **utilities.BL_NODES, diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/base.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/base.py index 9066877..66727ca 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/base.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/base.py @@ -13,11 +13,14 @@ from .. import sockets CACHE: dict[str, typ.Any] = {} ## By Instance UUID ## NOTE: CACHE does not persist between file loads. -_DEFAULT_LOOSE_SOCKET_SER = json.dumps({ - "socket_names": [], - "socket_def_names": [], - "models": [], -}) +_DEFAULT_LOOSE_SOCKET_SER = json.dumps( + { + 'socket_names': [], + 'socket_def_names': [], + 'models': [], + } +) + class MaxwellSimNode(bpy.types.Node): # Fundamentals @@ -25,133 +28,122 @@ class MaxwellSimNode(bpy.types.Node): bl_idname: str use_sim_node_name: bool = False bl_label: str - #draw_label(self) -> str: pass - + # draw_label(self) -> str: pass + # Style - bl_description: str = "" - - #bl_width_default: float = 0.0 - #bl_width_min: float = 0.0 - #bl_width_max: float = 0.0 - + bl_description: str = '' + + # bl_width_default: float = 0.0 + # bl_width_min: float = 0.0 + # bl_width_max: float = 0.0 + # Sockets _output_socket_methods: dict - + input_sockets: dict[str, ct.schemas.SocketDef] = {} output_sockets: dict[str, ct.schemas.SocketDef] = {} input_socket_sets: dict[str, dict[str, ct.schemas.SocketDef]] = {} output_socket_sets: dict[str, dict[str, ct.schemas.SocketDef]] = {} - + # Presets presets = {} - + # Managed Objects managed_obj_defs: dict[ct.ManagedObjName, ct.schemas.ManagedObjDef] = {} - + #################### # - Initialization #################### def __init_subclass__(cls, **kwargs: typ.Any): super().__init_subclass__(**kwargs) - + # Setup Blender ID for Node - if not hasattr(cls, "node_type"): + if not hasattr(cls, 'node_type'): msg = f"Node class {cls} does not define 'node_type', or it is does not have the type {ct.NodeType}" raise ValueError(msg) cls.bl_idname = str(cls.node_type.value) - + # Setup Instance ID for Node - cls.__annotations__["instance_id"] = bpy.props.StringProperty( - name="Instance ID", - description="The instance ID of a particular MaxwellSimNode instance, used to index caches", - default="", + cls.__annotations__['instance_id'] = bpy.props.StringProperty( + name='Instance ID', + description='The instance ID of a particular MaxwellSimNode instance, used to index caches', + default='', ) - + # Setup Name Property for Node - cls.__annotations__["sim_node_name"] = bpy.props.StringProperty( - name="Sim Node Name", - description="The name of a particular MaxwellSimNode node, which can be used to help identify data managed by the node", - default="", - update=(lambda self, context: self.sync_sim_node_name(context)) + cls.__annotations__['sim_node_name'] = bpy.props.StringProperty( + name='Sim Node Name', + description='The name of a particular MaxwellSimNode node, which can be used to help identify data managed by the node', + default='', + update=(lambda self, context: self.sync_sim_node_name(context)), ) - + # Setup Locked Property for Node - cls.__annotations__["locked"] = bpy.props.BoolProperty( - name="Locked State", + cls.__annotations__['locked'] = bpy.props.BoolProperty( + name='Locked State', description="The lock-state of a particular MaxwellSimNode instance, which determines the node's user editability", default=False, ) - + # Setup Blender Label for Node - if not hasattr(cls, "bl_label"): + if not hasattr(cls, 'bl_label'): msg = f"Node class {cls} does not define 'bl_label'" raise ValueError(msg) - + # Setup Callback Methods cls._output_socket_methods = { method._index_by: method for attr_name in dir(cls) - if hasattr( - method := getattr(cls, attr_name), - "_callback_type" - ) and method._callback_type == "computes_output_socket" + if hasattr(method := getattr(cls, attr_name), '_callback_type') + and method._callback_type == 'computes_output_socket' } cls._on_value_changed_methods = { method for attr_name in dir(cls) - if hasattr( - method := getattr(cls, attr_name), - "_callback_type" - ) and method._callback_type == "on_value_changed" + if hasattr(method := getattr(cls, attr_name), '_callback_type') + and method._callback_type == 'on_value_changed' } cls._on_show_preview = { method for attr_name in dir(cls) - if hasattr( - method := getattr(cls, attr_name), - "_callback_type" - ) and method._callback_type == "on_show_preview" + if hasattr(method := getattr(cls, attr_name), '_callback_type') + and method._callback_type == 'on_show_preview' } cls._on_show_plot = { method for attr_name in dir(cls) - if hasattr( - method := getattr(cls, attr_name), - "_callback_type" - ) and method._callback_type == "on_show_plot" + if hasattr(method := getattr(cls, attr_name), '_callback_type') + and method._callback_type == 'on_show_plot' } cls._on_init = { method for attr_name in dir(cls) - if hasattr( - method := getattr(cls, attr_name), - "_callback_type" - ) and method._callback_type == "on_init" + if hasattr(method := getattr(cls, attr_name), '_callback_type') + and method._callback_type == 'on_init' } - + # Setup Socket Set Dropdown if not len(cls.input_socket_sets) + len(cls.output_socket_sets) > 0: cls.active_socket_set = None else: ## Add Active Socket Set Enum socket_set_names = ( - (_input_socket_set_names := list(cls.input_socket_sets.keys())) - + [ - output_socket_set_name - for output_socket_set_name in cls.output_socket_sets.keys() - if output_socket_set_name not in _input_socket_set_names - ] - ) + _input_socket_set_names := list(cls.input_socket_sets.keys()) + ) + [ + output_socket_set_name + for output_socket_set_name in cls.output_socket_sets.keys() + if output_socket_set_name not in _input_socket_set_names + ] socket_set_ids = [ - socket_set_name.replace(" ", "_").upper() + socket_set_name.replace(' ', '_').upper() for socket_set_name in socket_set_names ] ## TODO: Better deriv. of sock.set. ID, ex. ( is currently invalid. - + ## Add Active Socket Set Enum - cls.__annotations__["active_socket_set"] = bpy.props.EnumProperty( - name="Active Socket Set", - description="The active socket set", + cls.__annotations__['active_socket_set'] = bpy.props.EnumProperty( + name='Active Socket Set', + description='The active socket set', items=[ ( socket_set_name, @@ -164,17 +156,19 @@ class MaxwellSimNode(bpy.types.Node): ) ], default=socket_set_names[0], - update=lambda self, context: self.sync_active_socket_set(context), + update=lambda self, context: self.sync_active_socket_set( + context + ), ) - + # Setup Preset Dropdown if not cls.presets: cls.active_preset = None else: ## TODO: Check that presets are represented in a socket that is guaranteed to be always available, specifically either a static socket or ALL static socket sets. - cls.__annotations__["active_preset"] = bpy.props.EnumProperty( - name="Active Preset", - description="The active preset", + cls.__annotations__['active_preset'] = bpy.props.EnumProperty( + name='Active Preset', + description='The active preset', items=[ ( preset_name, @@ -184,33 +178,31 @@ class MaxwellSimNode(bpy.types.Node): for preset_name, preset_def in cls.presets.items() ], default=list(cls.presets.keys())[0], - update=lambda self, context: ( - self.sync_active_preset()() - ), + update=lambda self, context: (self.sync_active_preset()()), ) - + #################### # - Generic Properties #################### def sync_active_socket_set(self, context): self.sync_sockets() - self.sync_prop("active_socket_set", context) - + self.sync_prop('active_socket_set', context) + def sync_sim_node_name(self, context): - if (mobjs := CACHE[self.instance_id].get("managed_objs")) is None: + if (mobjs := CACHE[self.instance_id].get('managed_objs')) is None: return - + for mobj_id, mobj in mobjs.items(): # Retrieve Managed Obj Definition mobj_def = self.managed_obj_defs[mobj_id] - + # Set Managed Obj Name mobj.name = mobj_def.name_prefix + self.sim_node_name ## ManagedObj is allowed to alter the name when setting it. ## - This will happen whenever the name is taken. ## - If altered, set the 'sim_node_name' to the altered name. ## - This will cause recursion, but only once. - + #################### # - Managed Object Properties #################### @@ -219,191 +211,207 @@ class MaxwellSimNode(bpy.types.Node): global CACHE if not CACHE.get(self.instance_id): CACHE[self.instance_id] = {} - + # If No Managed Objects in CACHE: Initialize Managed Objects ## - This happens on every ex. file load, init(), etc. . ## - ManagedObjects MUST the same object by name. ## - We sync our 'sim_node_name' with all managed objects. ## - (There is also a class-defined 'name_prefix' to differentiate) ## - See the 'sim_node_name' w/its sync function. - if CACHE[self.instance_id].get("managed_objs") is None: + if CACHE[self.instance_id].get('managed_objs') is None: # Initialize the Managed Object Instance Cache - CACHE[self.instance_id]["managed_objs"] = {} - - # Fill w/Managed Objects by Name Socket + CACHE[self.instance_id]['managed_objs'] = {} + + # Fill w/Managed Objects by Name Socket for mobj_id, mobj_def in self.managed_obj_defs.items(): name = mobj_def.name_prefix + self.sim_node_name - CACHE[self.instance_id]["managed_objs"][mobj_id] = ( - mobj_def.mk(name) + CACHE[self.instance_id]['managed_objs'][mobj_id] = mobj_def.mk( + name ) - - return CACHE[self.instance_id]["managed_objs"] - - return CACHE[self.instance_id]["managed_objs"] - + + return CACHE[self.instance_id]['managed_objs'] + + return CACHE[self.instance_id]['managed_objs'] + #################### # - Socket Properties #################### - def active_bl_sockets(self, direc: typx.Literal["input", "output"]): - return self.inputs if direc == "input" else self.outputs - + def active_bl_sockets(self, direc: typx.Literal['input', 'output']): + return self.inputs if direc == 'input' else self.outputs + def active_socket_set_sockets( self, - direc: typx.Literal["input", "output"], + direc: typx.Literal['input', 'output'], ) -> dict: # No Active Socket Set: Return Nothing - if not self.active_socket_set: return {} - + if not self.active_socket_set: + return {} + # Retrieve Active Socket Set Sockets socket_sets = ( self.input_socket_sets - if direc == "input" else self.output_socket_sets + if direc == 'input' + else self.output_socket_sets ) - active_socket_set_sockets = socket_sets.get( - self.active_socket_set - ) - + active_socket_set_sockets = socket_sets.get(self.active_socket_set) + # Return Active Socket Set Sockets (if any) - if not active_socket_set_sockets: return {} + if not active_socket_set_sockets: + return {} return active_socket_set_sockets - - def active_sockets(self, direc: typx.Literal["input", "output"]): + + def active_sockets(self, direc: typx.Literal['input', 'output']): static_sockets = ( - self.input_sockets - if direc == "input" - else self.output_sockets + self.input_sockets if direc == 'input' else self.output_sockets ) socket_sets = ( self.input_socket_sets - if direc == "input" + if direc == 'input' else self.output_socket_sets ) loose_sockets = ( self.loose_input_sockets - if direc == "input" + if direc == 'input' else self.loose_output_sockets ) - + return ( static_sockets | self.active_socket_set_sockets(direc=direc) | loose_sockets ) - + #################### # - Loose Sockets #################### # Loose Sockets ## Only Blender props persist as instance data ser_loose_input_sockets: bpy.props.StringProperty( - name="Serialized Loose Input Sockets", - description="JSON-serialized representation of loose input sockets.", + name='Serialized Loose Input Sockets', + description='JSON-serialized representation of loose input sockets.', default=_DEFAULT_LOOSE_SOCKET_SER, ) ser_loose_output_sockets: bpy.props.StringProperty( - name="Serialized Loose Input Sockets", - description="JSON-serialized representation of loose input sockets.", + name='Serialized Loose Input Sockets', + description='JSON-serialized representation of loose input sockets.', default=_DEFAULT_LOOSE_SOCKET_SER, ) - + ## Internal Serialization/Deserialization Methods (yuck) - def _ser_loose_sockets(self, deser: dict[str, ct.schemas.SocketDef]) -> str: - if not all(isinstance(model, pyd.BaseModel) for model in deser.values()): - msg = "Trying to deserialize loose sockets with invalid SocketDefs (they must be `pydantic` BaseModels)." + def _ser_loose_sockets( + self, deser: dict[str, ct.schemas.SocketDef] + ) -> str: + if not all( + isinstance(model, pyd.BaseModel) for model in deser.values() + ): + msg = 'Trying to deserialize loose sockets with invalid SocketDefs (they must be `pydantic` BaseModels).' raise ValueError(msg) - - return json.dumps({ - "socket_names": list(deser.keys()), - "socket_def_names": [ - model.__class__.__name__ - for model in deser.values() - ], - "models": [ - model.model_dump() - for model in deser.values() - if isinstance(model, pyd.BaseModel) - ], - }) ## Big reliance on order-preservation of dicts here.) - def _deser_loose_sockets(self, ser: str) -> dict[str, ct.schemas.SocketDef]: + + return json.dumps( + { + 'socket_names': list(deser.keys()), + 'socket_def_names': [ + model.__class__.__name__ for model in deser.values() + ], + 'models': [ + model.model_dump() + for model in deser.values() + if isinstance(model, pyd.BaseModel) + ], + } + ) ## Big reliance on order-preservation of dicts here.) + + def _deser_loose_sockets( + self, ser: str + ) -> dict[str, ct.schemas.SocketDef]: semi_deser = json.loads(ser) return { socket_name: getattr(sockets, socket_def_name)(**model_kwargs) for socket_name, socket_def_name, model_kwargs in zip( - semi_deser["socket_names"], - semi_deser["socket_def_names"], - semi_deser["models"], + semi_deser['socket_names'], + semi_deser['socket_def_names'], + semi_deser['models'], ) if hasattr(sockets, socket_def_name) } - + @property def loose_input_sockets(self) -> dict[str, ct.schemas.SocketDef]: return self._deser_loose_sockets(self.ser_loose_input_sockets) + @property def loose_output_sockets(self) -> dict[str, ct.schemas.SocketDef]: return self._deser_loose_sockets(self.ser_loose_output_sockets) + ## TODO: Some caching may play a role if this is all too slow. - + @loose_input_sockets.setter def loose_input_sockets( - self, value: dict[str, ct.schemas.SocketDef], + self, + value: dict[str, ct.schemas.SocketDef], ) -> None: - if not value: self.ser_loose_input_sockets = _DEFAULT_LOOSE_SOCKET_SER - else: self.ser_loose_input_sockets = self._ser_loose_sockets(value) - + if not value: + self.ser_loose_input_sockets = _DEFAULT_LOOSE_SOCKET_SER + else: + self.ser_loose_input_sockets = self._ser_loose_sockets(value) + # Synchronize Sockets self.sync_sockets() ## TODO: Perhaps re-init() all loose sockets anyway? - + @loose_output_sockets.setter def loose_output_sockets( - self, value: dict[str, ct.schemas.SocketDef], + self, + value: dict[str, ct.schemas.SocketDef], ) -> None: - if not value: self.ser_loose_output_sockets = _DEFAULT_LOOSE_SOCKET_SER - else: self.ser_loose_output_sockets = self._ser_loose_sockets(value) - + if not value: + self.ser_loose_output_sockets = _DEFAULT_LOOSE_SOCKET_SER + else: + self.ser_loose_output_sockets = self._ser_loose_sockets(value) + # Synchronize Sockets self.sync_sockets() ## TODO: Perhaps re-init() all loose sockets anyway? - + #################### # - Socket Management #################### def _prune_inactive_sockets(self): """Remove all inactive sockets from the node. - + **NOTE**: Socket names must be unique within direction, active socket set, and loose socket set. """ - for direc in ["input", "output"]: + for direc in ['input', 'output']: sockets = self.active_sockets(direc) bl_sockets = self.active_bl_sockets(direc) - + # Determine Sockets to Remove bl_sockets_to_remove = [ bl_socket for socket_name, bl_socket in bl_sockets.items() if socket_name not in sockets ] - + # Remove Sockets for bl_socket in bl_sockets_to_remove: bl_sockets.remove(bl_socket) - + def _add_new_active_sockets(self): """Add and initialize all non-existing active sockets to the node. - + Existing sockets within the given direction are not re-created. """ - for direc in ["input", "output"]: + for direc in ['input', 'output']: sockets = self.active_sockets(direc) bl_sockets = self.active_bl_sockets(direc) - + # Define BL Sockets created_sockets = {} for socket_name, socket_def in sockets.items(): # Skip Existing Sockets - if socket_name in bl_sockets: continue - + if socket_name in bl_sockets: + continue + # Create BL Socket from Socket bl_socket = bl_sockets.new( str(socket_def.socket_type.value), @@ -411,27 +419,27 @@ class MaxwellSimNode(bpy.types.Node): ) bl_socket.display_shape = bl_socket.socket_shape ## `display_shape` needs to be dynamically set - + # Record Created Socket created_sockets[socket_name] = socket_def - + # Initialize Just-Created BL Sockets for socket_name, socket_def in created_sockets.items(): socket_def.init(bl_sockets[socket_name]) - + def sync_sockets(self) -> None: """Synchronize the node's sockets with the active sockets. - + - Any non-existing active socket will be added and initialized. - Any existing active socket will not be changed. - Any existing inactive socket will be removed. - + Must be called after any change to socket definitions, including loose sockets. """ self._prune_inactive_sockets() self._add_new_active_sockets() - + #################### # - Preset Management #################### @@ -440,17 +448,17 @@ class MaxwellSimNode(bpy.types.Node): preset-defined input sockets. """ if not (preset_def := self.presets.get(self.active_preset)): - msg = f"Tried to apply active preset, but the active preset ({self.active_preset}) is not in presets ({self.presets})" + msg = f'Tried to apply active preset, but the active preset ({self.active_preset}) is not in presets ({self.presets})' raise RuntimeError(msg) - + for socket_name, socket_value in preset_def.values.items(): if not (bl_socket := self.inputs.get(socket_name)): - msg = f"Tried to set preset socket/value pair ({socket_name}={socket_value}), but socket is not in active input sockets ({self.inputs})" + msg = f'Tried to set preset socket/value pair ({socket_name}={socket_value}), but socket is not in active input sockets ({self.inputs})' raise ValueError(msg) - + bl_socket.value = socket_value ## TODO: Lazy-valued presets? - + #################### # - UI Methods #################### @@ -459,37 +467,46 @@ class MaxwellSimNode(bpy.types.Node): context: bpy.types.Context, layout: bpy.types.UILayout, ) -> None: - if self.locked: layout.enabled = False - + if self.locked: + layout.enabled = False + if self.active_preset: - layout.prop(self, "active_preset", text="") - + layout.prop(self, 'active_preset', text='') + if self.active_socket_set: - layout.prop(self, "active_socket_set", text="") - + layout.prop(self, 'active_socket_set', text='') + # Draw Name col = layout.column(align=False) if self.use_sim_node_name: row = col.row(align=True) - row.label(text="", icon="FILE_TEXT") - row.prop(self, "sim_node_name", text="") - + row.label(text='', icon='FILE_TEXT') + row.prop(self, 'sim_node_name', text='') + # Draw Name self.draw_props(context, col) self.draw_operators(context, col) self.draw_info(context, col) - + ## TODO: Managed Operators instead of this shit - def draw_props(self, context, layout): pass - def draw_operators(self, context, layout): pass - def draw_info(self, context, layout): pass - - def draw_buttons_ext(self, context, layout): pass + def draw_props(self, context, layout): + pass + + def draw_operators(self, context, layout): + pass + + def draw_info(self, context, layout): + pass + + def draw_buttons_ext(self, context, layout): + pass + ## TODO: Side panel buttons for fanciness. - + def draw_plot_settings(self, context, layout): - if self.locked: layout.enabled = False - + if self.locked: + layout.enabled = False + #################### # - Data Flow #################### @@ -499,7 +516,7 @@ class MaxwellSimNode(bpy.types.Node): kind: ct.DataFlowKind = ct.DataFlowKind.Value, ) -> typ.Any | None: """Computes the data of an input socket, by socket name and data flow kind, by asking the socket nicely via `bl_socket.compute_data`. - + Args: input_socket_name: The name of the input socket, as defined in `self.input_sockets`. @@ -507,25 +524,25 @@ class MaxwellSimNode(bpy.types.Node): """ if not (bl_socket := self.inputs.get(input_socket_name)): return None - #msg = f"Input socket name {input_socket_name} is not an active input sockets." - #raise ValueError(msg) - + # msg = f"Input socket name {input_socket_name} is not an active input sockets." + # raise ValueError(msg) + return bl_socket.compute_data(kind=kind) - + def compute_output( self, output_socket_name: ct.SocketName, kind: ct.DataFlowKind = ct.DataFlowKind.Value, ) -> typ.Any: """Computes the value of an output socket name, from its socket name. - + Searches methods decorated with `@computes_output_socket(output_socket_name, kind=..., ...)`, for a perfect match to the pair `socket_name..kind`. This method is run to produce the value. - + Args: output_socket_name: The name declaring the output socket, for which this method computes the output. - + Returns: The value of the output socket, as computed by the dedicated method registered using the `@computes_output_socket` decorator. @@ -535,140 +552,150 @@ class MaxwellSimNode(bpy.types.Node): (output_socket_name, kind) ) ): - msg = f"No output method for ({output_socket_name}, {str(kind.value)}" + msg = f'No output method for ({output_socket_name}, {str(kind.value)}' raise ValueError(msg) - + return output_socket_method(self) - + #################### # - Action Chain #################### def sync_prop(self, prop_name: str, context: bpy.types.Context): - """Called when a property has been updated. - """ + """Called when a property has been updated.""" if not hasattr(self, prop_name): - msg = f"Property {prop_name} not defined on socket {self}" + msg = f'Property {prop_name} not defined on socket {self}' raise RuntimeError(msg) - - self.trigger_action("value_changed", prop_name=prop_name) - + + self.trigger_action('value_changed', prop_name=prop_name) + def trigger_action( self, - action: typx.Literal["enable_lock", "disable_lock", "value_changed", "show_preview", "show_plot"], + action: typx.Literal[ + 'enable_lock', + 'disable_lock', + 'value_changed', + 'show_preview', + 'show_plot', + ], socket_name: ct.SocketName | None = None, prop_name: ct.SocketName | None = None, ) -> None: """Reports that the input socket is changed. - + Invalidates (recursively) the cache of any managed object or output socket method that implicitly depends on this input socket. """ # Forwards Chains - if action == "value_changed": + if action == 'value_changed': # Run User Callbacks ## Careful with these, they run BEFORE propagation... ## ...because later-chain methods may rely on the results of this. for method in self._on_value_changed_methods: if ( - socket_name - and socket_name in method._extra_data.get("changed_sockets") - ) or ( - prop_name - and prop_name in method._extra_data.get("changed_props") - ) or ( - socket_name - and method._extra_data["changed_loose_input"] - and socket_name in self.loose_input_sockets + ( + socket_name + and socket_name + in method._extra_data.get('changed_sockets') + ) + or ( + prop_name + and prop_name + in method._extra_data.get('changed_props') + ) + or ( + socket_name + and method._extra_data['changed_loose_input'] + and socket_name in self.loose_input_sockets + ) ): method(self) - + # Propagate via Output Sockets - for bl_socket in self.active_bl_sockets("output"): + for bl_socket in self.active_bl_sockets('output'): bl_socket.trigger_action(action) - + # Backwards Chains - elif action == "enable_lock": + elif action == 'enable_lock': self.locked = True - + ## Propagate via Input Sockets - for bl_socket in self.active_bl_sockets("input"): + for bl_socket in self.active_bl_sockets('input'): bl_socket.trigger_action(action) - - elif action == "disable_lock": + + elif action == 'disable_lock': self.locked = False - + ## Propagate via Input Sockets - for bl_socket in self.active_bl_sockets("input"): + for bl_socket in self.active_bl_sockets('input'): bl_socket.trigger_action(action) - - elif action == "show_preview": + + elif action == 'show_preview': # Run User Callbacks for method in self._on_show_preview: method(self) - + ## Propagate via Input Sockets - for bl_socket in self.active_bl_sockets("input"): + for bl_socket in self.active_bl_sockets('input'): bl_socket.trigger_action(action) - - elif action == "show_plot": + + elif action == 'show_plot': # Run User Callbacks ## These shouldn't change any data, BUT... ## ...because they can stop propagation, they should go first. for method in self._on_show_plot: method(self) - if method._extra_data["stop_propagation"]: + if method._extra_data['stop_propagation']: return - + ## Propagate via Input Sockets - for bl_socket in self.active_bl_sockets("input"): + for bl_socket in self.active_bl_sockets('input'): bl_socket.trigger_action(action) - + #################### # - Blender Node Methods #################### @classmethod def poll(cls, node_tree: bpy.types.NodeTree) -> bool: """Run (by Blender) to determine instantiability. - + Restricted to the MaxwellSimTreeType. """ - + return node_tree.bl_idname == ct.TreeType.MaxwellSim.value - + def init(self, context: bpy.types.Context): - """Run (by Blender) on node creation. - """ + """Run (by Blender) on node creation.""" global CACHE - + # Initialize Cache and Instance ID self.instance_id = str(uuid.uuid4()) CACHE[self.instance_id] = {} - + # Initialize Name self.sim_node_name = self.name ## Only shown in draw_buttons if 'self.use_sim_node_name' - + # Initialize Sockets self.sync_sockets() - + # Apply Default Preset - if self.active_preset: self.sync_active_preset() - + if self.active_preset: + self.sync_active_preset() + # Callbacks for method in self._on_init: method(self) - + def update(self) -> None: pass - + def free(self) -> None: - """Run (by Blender) when deleting the node. - """ + """Run (by Blender) when deleting the node.""" global CACHE if not CACHE.get(self.instance_id): CACHE[self.instance_id] = {} node_tree = self.id_data - + # Unlock ## This is one approach to the "deleted locked nodes" problem. ## Essentially, deleting a locked node will unlock along input chain. @@ -679,35 +706,33 @@ class MaxwellSimNode(bpy.types.Node): bl_socket.is_linked and bl_socket.locked for bl_socket in self.inputs.values() ): - self.trigger_action("disable_lock") - + self.trigger_action('disable_lock') + # Free Managed Objects for managed_obj in self.managed_objs.values(): managed_obj.free() - + # Update NodeTree Caches ## The NodeTree keeps caches to for optimized event triggering. ## However, ex. deleted nodes also deletes links, without cache update. ## By reporting that we're deleting the node, the cache stays happy. node_tree.sync_node_removed(self) - + # Finally: Free Instance Cache if self.instance_id in CACHE: del CACHE[self.instance_id] - def chain_event_decorator( callback_type: typ.Literal[ - "computes_output_socket", - "on_value_changed", - "on_show_preview", - "on_show_plot", - "on_init", + 'computes_output_socket', + 'on_value_changed', + 'on_show_preview', + 'on_show_plot', + 'on_init', ], index_by: typ.Any | None = None, extra_data: dict[str, typ.Any] | None = None, - kind: ct.DataFlowKind = ct.DataFlowKind.Value, input_sockets: set[str] = set(), ## For now, presume output_sockets: set[str] = set(), ## For now, presume @@ -715,51 +740,56 @@ def chain_event_decorator( loose_output_sockets: bool = False, props: set[str] = set(), managed_objs: set[str] = set(), - - req_params: set[str] = set() + req_params: set[str] = set(), ): def decorator(method: typ.Callable) -> typ.Callable: # Check Function Signature Validity func_sig = set(inspect.signature(method).parameters.keys()) - + ## Too Little if func_sig != req_params and func_sig.issubset(req_params): - msg = f"Decorated method {method.__name__} is missing arguments {req_params - func_sig}" - + msg = f'Decorated method {method.__name__} is missing arguments {req_params - func_sig}' + ## Too Much if func_sig != req_params and func_sig.issuperset(req_params): - msg = f"Decorated method {method.__name__} has superfluous arguments {func_sig - req_params}" + msg = f'Decorated method {method.__name__} has superfluous arguments {func_sig - req_params}' raise ValueError(msg) - + ## Just Right :) - + # TODO: Check Function Annotation Validity # - w/pydantic and/or socket capabilities - + def decorated(node: MaxwellSimNode): # Assemble Keyword Arguments method_kw_args = {} - + ## Add Input Sockets if input_sockets: _input_sockets = { - input_socket_name: node._compute_input(input_socket_name, kind) + input_socket_name: node._compute_input( + input_socket_name, kind + ) for input_socket_name in input_sockets } method_kw_args |= dict(input_sockets=_input_sockets) - + ## Add Output Sockets if output_sockets: _output_sockets = { - output_socket_name: node.compute_output(output_socket_name, kind) + output_socket_name: node.compute_output( + output_socket_name, kind + ) for output_socket_name in output_sockets } method_kw_args |= dict(output_sockets=_output_sockets) - + ## Add Loose Sockets if loose_input_sockets: _loose_input_sockets = { - input_socket_name: node._compute_input(input_socket_name, kind) + input_socket_name: node._compute_input( + input_socket_name, kind + ) for input_socket_name in node.loose_input_sockets } method_kw_args |= dict( @@ -767,21 +797,22 @@ def chain_event_decorator( ) if loose_output_sockets: _loose_output_sockets = { - output_socket_name: node.compute_output(output_socket_name, kind) + output_socket_name: node.compute_output( + output_socket_name, kind + ) for output_socket_name in node.loose_output_sockets } method_kw_args |= dict( loose_output_sockets=_loose_output_sockets ) - + ## Add Props if props: _props = { - prop_name: getattr(node, prop_name) - for prop_name in props + prop_name: getattr(node, prop_name) for prop_name in props } method_kw_args |= dict(props=_props) - + ## Add Managed Object if managed_objs: _managed_objs = { @@ -789,21 +820,22 @@ def chain_event_decorator( for managed_obj_name in managed_objs } method_kw_args |= dict(managed_objs=_managed_objs) - + # Call Method return method( node, **method_kw_args, ) - + # Set Attributes for Discovery decorated._callback_type = callback_type if index_by: decorated._index_by = index_by if extra_data: decorated._extra_data = extra_data - + return decorated + return decorator @@ -820,9 +852,9 @@ def computes_output_socket( ): """Given a socket name, defines a function-that-makes-a-function (aka. decorator) which has the name of the socket attached. - + Must be used as a decorator, ex. `@compute_output_socket("name")`. - + Args: output_socket_name: The name of the output socket to attach the decorated method to. @@ -835,22 +867,21 @@ def computes_output_socket( be returned by the decorated function. cacheable: The output of th be returned by the decorated function. - + Returns: The decorator, which takes the output-socket-computing method and returns a new output-socket-computing method, now annotated and discoverable by the `MaxwellSimTreeNode`. """ - req_params = {"self"} | ( - {"input_sockets"} if input_sockets else set() - ) | ( - {"props"} if props else set() - ) | ( - {"managed_objs"} if managed_objs else set() + req_params = ( + {'self'} + | ({'input_sockets'} if input_sockets else set()) + | ({'props'} if props else set()) + | ({'managed_objs'} if managed_objs else set()) ) - + return chain_event_decorator( - callback_type="computes_output_socket", + callback_type='computes_output_socket', index_by=(output_socket_name, kind), kind=kind, input_sockets=input_sockets, @@ -860,7 +891,6 @@ def computes_output_socket( ) - #################### # - Decorator: On Show Preview #################### @@ -868,40 +898,42 @@ def on_value_changed( socket_name: set[ct.SocketName] | ct.SocketName | None = None, prop_name: set[str] | str | None = None, any_loose_input_socket: bool = False, - kind: ct.DataFlowKind = ct.DataFlowKind.Value, input_sockets: set[str] = set(), props: set[str] = set(), managed_objs: set[str] = set(), ): - if sum([ - int(socket_name is not None), - int(prop_name is not None), - int(any_loose_input_socket), - ]) > 1: - msg = "Define only one of socket_name, prop_name or any_loose_input_socket" + if ( + sum( + [ + int(socket_name is not None), + int(prop_name is not None), + int(any_loose_input_socket), + ] + ) + > 1 + ): + msg = 'Define only one of socket_name, prop_name or any_loose_input_socket' raise ValueError(msg) - - req_params = {"self"} | ( - {"input_sockets"} if input_sockets else set() - ) | ( - {"loose_input_sockets"} if any_loose_input_socket else set() - ) | ( - {"props"} if props else set() - ) | ( - {"managed_objs"} if managed_objs else set() + + req_params = ( + {'self'} + | ({'input_sockets'} if input_sockets else set()) + | ({'loose_input_sockets'} if any_loose_input_socket else set()) + | ({'props'} if props else set()) + | ({'managed_objs'} if managed_objs else set()) ) - + return chain_event_decorator( - callback_type="on_value_changed", + callback_type='on_value_changed', extra_data={ - "changed_sockets": ( + 'changed_sockets': ( socket_name if isinstance(socket_name, set) else {socket_name} ), - "changed_props": ( + 'changed_props': ( prop_name if isinstance(prop_name, set) else {prop_name} ), - "changed_loose_input": any_loose_input_socket, + 'changed_loose_input': any_loose_input_socket, }, kind=kind, input_sockets=input_sockets, @@ -911,6 +943,7 @@ def on_value_changed( req_params=req_params, ) + def on_show_preview( kind: ct.DataFlowKind = ct.DataFlowKind.Value, input_sockets: set[str] = set(), ## For now, presume only same kind @@ -918,18 +951,16 @@ def on_show_preview( props: set[str] = set(), managed_objs: set[str] = set(), ): - req_params = {"self"} | ( - {"input_sockets"} if input_sockets else set() - ) | ( - {"output_sockets"} if output_sockets else set() - ) | ( - {"props"} if props else set() - ) | ( - {"managed_objs"} if managed_objs else set() + req_params = ( + {'self'} + | ({'input_sockets'} if input_sockets else set()) + | ({'output_sockets'} if output_sockets else set()) + | ({'props'} if props else set()) + | ({'managed_objs'} if managed_objs else set()) ) - + return chain_event_decorator( - callback_type="on_show_preview", + callback_type='on_show_preview', kind=kind, input_sockets=input_sockets, output_sockets=output_sockets, @@ -938,6 +969,7 @@ def on_show_preview( req_params=req_params, ) + def on_show_plot( kind: ct.DataFlowKind = ct.DataFlowKind.Value, input_sockets: set[str] = set(), @@ -946,20 +978,18 @@ def on_show_plot( managed_objs: set[str] = set(), stop_propagation: bool = False, ): - req_params = {"self"} | ( - {"input_sockets"} if input_sockets else set() - ) | ( - {"output_sockets"} if output_sockets else set() - ) | ( - {"props"} if props else set() - ) | ( - {"managed_objs"} if managed_objs else set() + req_params = ( + {'self'} + | ({'input_sockets'} if input_sockets else set()) + | ({'output_sockets'} if output_sockets else set()) + | ({'props'} if props else set()) + | ({'managed_objs'} if managed_objs else set()) ) - + return chain_event_decorator( - callback_type="on_show_plot", + callback_type='on_show_plot', extra_data={ - "stop_propagation": stop_propagation, + 'stop_propagation': stop_propagation, }, kind=kind, input_sockets=input_sockets, @@ -969,6 +999,7 @@ def on_show_plot( req_params=req_params, ) + def on_init( kind: ct.DataFlowKind = ct.DataFlowKind.Value, input_sockets: set[str] = set(), @@ -976,18 +1007,16 @@ def on_init( props: set[str] = set(), managed_objs: set[str] = set(), ): - req_params = {"self"} | ( - {"input_sockets"} if input_sockets else set() - ) | ( - {"output_sockets"} if output_sockets else set() - ) | ( - {"props"} if props else set() - ) | ( - {"managed_objs"} if managed_objs else set() + req_params = ( + {'self'} + | ({'input_sockets'} if input_sockets else set()) + | ({'output_sockets'} if output_sockets else set()) + | ({'props'} if props else set()) + | ({'managed_objs'} if managed_objs else set()) ) - + return chain_event_decorator( - callback_type="on_init", + callback_type='on_init', kind=kind, input_sockets=input_sockets, output_sockets=output_sockets, diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/bounds/bound_box.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/bounds/bound_box.py index bbfbed1..b377b05 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/bounds/bound_box.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/bounds/bound_box.py @@ -6,41 +6,41 @@ from ... import contracts as ct from ... import sockets from .. import base + class BoundCondsNode(base.MaxwellSimNode): node_type = ct.NodeType.BoundConds - bl_label = "Bound Box" - #bl_icon = ... - + bl_label = 'Bound Box' + # bl_icon = ... + #################### # - Sockets #################### input_sockets = { - "+X": sockets.MaxwellBoundCondSocketDef(), - "-X": sockets.MaxwellBoundCondSocketDef(), - "+Y": sockets.MaxwellBoundCondSocketDef(), - "-Y": sockets.MaxwellBoundCondSocketDef(), - "+Z": sockets.MaxwellBoundCondSocketDef(), - "-Z": sockets.MaxwellBoundCondSocketDef(), + '+X': sockets.MaxwellBoundCondSocketDef(), + '-X': sockets.MaxwellBoundCondSocketDef(), + '+Y': sockets.MaxwellBoundCondSocketDef(), + '-Y': sockets.MaxwellBoundCondSocketDef(), + '+Z': sockets.MaxwellBoundCondSocketDef(), + '-Z': sockets.MaxwellBoundCondSocketDef(), } output_sockets = { - "BCs": sockets.MaxwellBoundCondsSocketDef(), + 'BCs': sockets.MaxwellBoundCondsSocketDef(), } - + #################### # - Output Socket Computation #################### @base.computes_output_socket( - "BCs", - input_sockets={"+X", "-X", "+Y", "-Y", "+Z", "-Z"} + 'BCs', input_sockets={'+X', '-X', '+Y', '-Y', '+Z', '-Z'} ) def compute_simulation(self, input_sockets) -> td.BoundarySpec: - x_pos = input_sockets["+X"] - x_neg = input_sockets["-X"] - y_pos = input_sockets["+Y"] - y_neg = input_sockets["-Y"] - z_pos = input_sockets["+Z"] - z_neg = input_sockets["-Z"] - + x_pos = input_sockets['+X'] + x_neg = input_sockets['-X'] + y_pos = input_sockets['+Y'] + y_neg = input_sockets['-Y'] + z_pos = input_sockets['+Z'] + z_neg = input_sockets['-Z'] + return td.BoundarySpec( x=td.Boundary( plus=x_pos, @@ -57,15 +57,10 @@ class BoundCondsNode(base.MaxwellSimNode): ) - #################### # - Blender Registration #################### BL_REGISTER = [ BoundCondsNode, ] -BL_NODES = { - ct.NodeType.BoundConds: ( - ct.NodeCategory.MAXWELLSIM_BOUNDS - ) -} +BL_NODES = {ct.NodeType.BoundConds: (ct.NodeCategory.MAXWELLSIM_BOUNDS)} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/bounds/bound_faces/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/bounds/bound_faces/__init__.py index 281da54..52233ca 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/bounds/bound_faces/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/bounds/bound_faces/__init__.py @@ -10,7 +10,6 @@ BL_REGISTER = [ *pml_bound_face.BL_REGISTER, *pec_bound_face.BL_REGISTER, *pmc_bound_face.BL_REGISTER, - *bloch_bound_face.BL_REGISTER, *periodic_bound_face.BL_REGISTER, *absorbing_bound_face.BL_REGISTER, @@ -19,7 +18,6 @@ BL_NODES = { **pml_bound_face.BL_NODES, **pec_bound_face.BL_NODES, **pmc_bound_face.BL_NODES, - **bloch_bound_face.BL_NODES, **periodic_bound_face.BL_NODES, **absorbing_bound_face.BL_NODES, diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/__init__.py index 383022d..0244f95 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/__init__.py @@ -1,26 +1,22 @@ from . import wave_constant -#from . import unit_system +# from . import unit_system from . import constants from . import web_importers -#from . import file_importers +# from . import file_importers BL_REGISTER = [ *wave_constant.BL_REGISTER, -# *unit_system.BL_REGISTER, - + # *unit_system.BL_REGISTER, *constants.BL_REGISTER, - *web_importers.BL_REGISTER, -# *file_importers.BL_REGISTER, + # *file_importers.BL_REGISTER, ] BL_NODES = { **wave_constant.BL_NODES, -# **unit_system.BL_NODES, - + # **unit_system.BL_NODES, **constants.BL_NODES, - **web_importers.BL_NODES, -# *file_importers.BL_REGISTER, + # *file_importers.BL_REGISTER, } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/__init__.py index c731c46..821f186 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/__init__.py @@ -1,17 +1,18 @@ -#from . import scientific_constant +# from . import scientific_constant from . import number_constant -#from . import physical_constant + +# from . import physical_constant from . import blender_constant BL_REGISTER = [ -# *scientific_constant.BL_REGISTER, + # *scientific_constant.BL_REGISTER, *number_constant.BL_REGISTER, -# *physical_constant.BL_REGISTER, + # *physical_constant.BL_REGISTER, *blender_constant.BL_REGISTER, ] BL_NODES = { -# **scientific_constant.BL_NODES, + # **scientific_constant.BL_NODES, **number_constant.BL_NODES, -# **physical_constant.BL_NODES, + # **physical_constant.BL_NODES, **blender_constant.BL_NODES, } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/blender_constant.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/blender_constant.py index e6bdd10..17a5a7c 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/blender_constant.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/blender_constant.py @@ -4,39 +4,36 @@ from .... import contracts as ct from .... import sockets from ... import base + class BlenderConstantNode(base.MaxwellSimNode): node_type = ct.NodeType.BlenderConstant - bl_label = "Blender Constant" - + bl_label = 'Blender Constant' + input_socket_sets = { - "Object": { - "Value": sockets.BlenderObjectSocketDef(), + 'Object': { + 'Value': sockets.BlenderObjectSocketDef(), }, - "Collection": { - "Value": sockets.BlenderCollectionSocketDef(), + 'Collection': { + 'Value': sockets.BlenderCollectionSocketDef(), }, - "Text": { - "Value": sockets.BlenderTextSocketDef(), + 'Text': { + 'Value': sockets.BlenderTextSocketDef(), }, - "Image": { - "Value": sockets.BlenderImageSocketDef(), + 'Image': { + 'Value': sockets.BlenderImageSocketDef(), }, - "GeoNode Tree": { - "Value": sockets.BlenderGeoNodesSocketDef(), + 'GeoNode Tree': { + 'Value': sockets.BlenderGeoNodesSocketDef(), }, } output_socket_sets = input_socket_sets - + #################### # - Callbacks #################### - @base.computes_output_socket( - "Value", - input_sockets={"Value"} - ) + @base.computes_output_socket('Value', input_sockets={'Value'}) def compute_value(self, input_sockets) -> typ.Any: - return input_sockets["Value"] - + return input_sockets['Value'] #################### @@ -46,7 +43,5 @@ BL_REGISTER = [ BlenderConstantNode, ] BL_NODES = { - ct.NodeType.BlenderConstant: ( - ct.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS - ) + ct.NodeType.BlenderConstant: (ct.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS) } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/number_constant.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/number_constant.py index 1bff953..bc6e706 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/number_constant.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/number_constant.py @@ -7,36 +7,33 @@ from .... import contracts as ct from .... import sockets from ... import base + class NumberConstantNode(base.MaxwellSimNode): node_type = ct.NodeType.NumberConstant - bl_label = "Numerical Constant" - + bl_label = 'Numerical Constant' + input_socket_sets = { - "Integer": { - "Value": sockets.IntegerNumberSocketDef(), + 'Integer': { + 'Value': sockets.IntegerNumberSocketDef(), }, - "Rational": { - "Value": sockets.RationalNumberSocketDef(), + 'Rational': { + 'Value': sockets.RationalNumberSocketDef(), }, - "Real": { - "Value": sockets.RealNumberSocketDef(), + 'Real': { + 'Value': sockets.RealNumberSocketDef(), }, - "Complex": { - "Value": sockets.ComplexNumberSocketDef(), + 'Complex': { + 'Value': sockets.ComplexNumberSocketDef(), }, } output_socket_sets = input_socket_sets - + #################### # - Callbacks #################### - @base.computes_output_socket( - "Value", - input_sockets={"Value"} - ) + @base.computes_output_socket('Value', input_sockets={'Value'}) def compute_value(self, input_sockets) -> typ.Any: - return input_sockets["Value"] - + return input_sockets['Value'] #################### @@ -46,7 +43,5 @@ BL_REGISTER = [ NumberConstantNode, ] BL_NODES = { - ct.NodeType.NumberConstant: ( - ct.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS - ) + ct.NodeType.NumberConstant: (ct.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS) } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/physical_constant.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/physical_constant.py index 1503b45..6284e89 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/physical_constant.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/physical_constant.py @@ -5,61 +5,61 @@ from .... import contracts from .... import sockets from ... import base + class PhysicalConstantNode(base.MaxwellSimTreeNode): node_type = contracts.NodeType.PhysicalConstant - - bl_label = "Physical Constant" - #bl_icon = constants.ICON_SIM_INPUT - + + bl_label = 'Physical Constant' + # bl_icon = constants.ICON_SIM_INPUT + input_sockets = {} input_socket_sets = { - "time": { - "value": sockets.PhysicalTimeSocketDef( - label="Time", + 'time': { + 'value': sockets.PhysicalTimeSocketDef( + label='Time', ), }, - "angle": { - "value": sockets.PhysicalAngleSocketDef( - label="Angle", + 'angle': { + 'value': sockets.PhysicalAngleSocketDef( + label='Angle', ), }, - "length": { - "value": sockets.PhysicalLengthSocketDef( - label="Length", + 'length': { + 'value': sockets.PhysicalLengthSocketDef( + label='Length', ), }, - "area": { - "value": sockets.PhysicalAreaSocketDef( - label="Area", + 'area': { + 'value': sockets.PhysicalAreaSocketDef( + label='Area', ), }, - "volume": { - "value": sockets.PhysicalVolumeSocketDef( - label="Volume", + 'volume': { + 'value': sockets.PhysicalVolumeSocketDef( + label='Volume', ), }, - "point_3d": { - "value": sockets.PhysicalPoint3DSocketDef( - label="3D Point", + 'point_3d': { + 'value': sockets.PhysicalPoint3DSocketDef( + label='3D Point', ), }, - "size_3d": { - "value": sockets.PhysicalSize3DSocketDef( - label="3D Size", + 'size_3d': { + 'value': sockets.PhysicalSize3DSocketDef( + label='3D Size', ), }, ## I got bored so maybe the rest later } output_sockets = {} output_socket_sets = input_socket_sets - + #################### # - Callbacks #################### - @base.computes_output_socket("value") + @base.computes_output_socket('value') def compute_value(self: contracts.NodeTypeProtocol) -> sp.Expr: - return self.compute_input("value") - + return self.compute_input('value') #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/unit_system.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/unit_system.py index c26d1e9..b8a0b38 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/unit_system.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/unit_system.py @@ -5,29 +5,29 @@ from ... import contracts as ct from ... import sockets from .. import base + class PhysicalUnitSystemNode(base.MaxwellSimNode): node_type = ct.NodeType.UnitSystem - bl_label = "Unit System" - + bl_label = 'Unit System' + input_sockets = { - "Unit System": sockets.PhysicalUnitSystemSocketDef( + 'Unit System': sockets.PhysicalUnitSystemSocketDef( show_by_default=True, ), } output_sockets = { - "Unit System": sockets.PhysicalUnitSystemSocketDef(), + 'Unit System': sockets.PhysicalUnitSystemSocketDef(), } - + #################### # - Callbacks #################### @base.computes_output_socket( - "Unit System", - input_sockets = {"Unit System"}, + 'Unit System', + input_sockets={'Unit System'}, ) def compute_value(self, input_sockets) -> dict: - return input_sockets["Unit System"] - + return input_sockets['Unit System'] #################### @@ -36,8 +36,4 @@ class PhysicalUnitSystemNode(base.MaxwellSimNode): BL_REGISTER = [ PhysicalUnitSystemNode, ] -BL_NODES = { - ct.NodeType.UnitSystem: ( - ct.NodeCategory.MAXWELLSIM_INPUTS - ) -} +BL_NODES = {ct.NodeType.UnitSystem: (ct.NodeCategory.MAXWELLSIM_INPUTS)} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/wave_constant.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/wave_constant.py index d4c7849..482aa0a 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/wave_constant.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/wave_constant.py @@ -8,89 +8,86 @@ from ... import contracts as ct from ... import sockets from .. import base -VAC_SPEED_OF_LIGHT = ( - sc.constants.speed_of_light - * spu.meter/spu.second -) +VAC_SPEED_OF_LIGHT = sc.constants.speed_of_light * spu.meter / spu.second + class WaveConstantNode(base.MaxwellSimNode): node_type = ct.NodeType.WaveConstant - - bl_label = "Wave Constant" - + + bl_label = 'Wave Constant' + input_socket_sets = { # Single - "Vacuum WL": { - "WL": sockets.PhysicalLengthSocketDef( - default_value=500*spu.nm, + 'Vacuum WL': { + 'WL': sockets.PhysicalLengthSocketDef( + default_value=500 * spu.nm, default_unit=spu.nm, ), }, - "Frequency": { - "Freq": sockets.PhysicalFreqSocketDef( - default_value=500*spux.THz, + 'Frequency': { + 'Freq': sockets.PhysicalFreqSocketDef( + default_value=500 * spux.THz, default_unit=spux.THz, ), }, - # Listy - "Vacuum WLs": { - "WLs": sockets.PhysicalLengthSocketDef( + 'Vacuum WLs': { + 'WLs': sockets.PhysicalLengthSocketDef( is_list=True, ), }, - "Frequencies": { - "Freqs": sockets.PhysicalFreqSocketDef( + 'Frequencies': { + 'Freqs': sockets.PhysicalFreqSocketDef( is_list=True, ), }, } - + #################### # - Callbacks #################### @base.computes_output_socket( - "WL", - input_sockets={"WL", "Freq"}, + 'WL', + input_sockets={'WL', 'Freq'}, ) def compute_vac_wl(self, input_sockets: dict) -> sp.Expr: - if (vac_wl := input_sockets["WL"]) is not None: + if (vac_wl := input_sockets['WL']) is not None: return vac_wl - - elif (freq := input_sockets["Freq"]) is not None: + + elif (freq := input_sockets['Freq']) is not None: return spu.convert_to( VAC_SPEED_OF_LIGHT / freq, spu.meter, ) - - raise RuntimeError("Vac WL and Freq are both None") - + + raise RuntimeError('Vac WL and Freq are both None') + @base.computes_output_socket( - "Freq", - input_sockets={"WL", "Freq"}, + 'Freq', + input_sockets={'WL', 'Freq'}, ) def compute_freq(self, input_sockets: dict) -> sp.Expr: - if (vac_wl := input_sockets["WL"]) is not None: + if (vac_wl := input_sockets['WL']) is not None: return spu.convert_to( VAC_SPEED_OF_LIGHT / vac_wl, spu.hertz, ) - elif (freq := input_sockets["Freq"]) is not None: + elif (freq := input_sockets['Freq']) is not None: return freq - - raise RuntimeError("Vac WL and Freq are both None") - + + raise RuntimeError('Vac WL and Freq are both None') + #################### # - Listy Callbacks #################### @base.computes_output_socket( - "WLs", - input_sockets={"WLs", "Freqs"}, + 'WLs', + input_sockets={'WLs', 'Freqs'}, ) def compute_vac_wls(self, input_sockets: dict) -> sp.Expr: - if (vac_wls := input_sockets["WLs"]) is not None: + if (vac_wls := input_sockets['WLs']) is not None: return vac_wls - elif (freqs := input_sockets["Freqs"]) is not None: + elif (freqs := input_sockets['Freqs']) is not None: return [ spu.convert_to( VAC_SPEED_OF_LIGHT / freq, @@ -98,15 +95,15 @@ class WaveConstantNode(base.MaxwellSimNode): ) for freq in freqs ][::-1] - - raise RuntimeError("Vac WLs and Freqs are both None") - + + raise RuntimeError('Vac WLs and Freqs are both None') + @base.computes_output_socket( - "Freqs", - input_sockets={"WLs", "Freqs"}, + 'Freqs', + input_sockets={'WLs', 'Freqs'}, ) def compute_freqs(self, input_sockets: dict) -> sp.Expr: - if (vac_wls := input_sockets["WLs"]) is not None: + if (vac_wls := input_sockets['WLs']) is not None: return [ spu.convert_to( VAC_SPEED_OF_LIGHT / vac_wl, @@ -114,43 +111,42 @@ class WaveConstantNode(base.MaxwellSimNode): ) for vac_wl in vac_wls ][::-1] - elif (freqs := input_sockets["Freqs"]) is not None: + elif (freqs := input_sockets['Freqs']) is not None: return freqs - - raise RuntimeError("Vac WLs and Freqs are both None") - + + raise RuntimeError('Vac WLs and Freqs are both None') + #################### # - Callbacks #################### @base.on_value_changed( - prop_name="active_socket_set", - props={"active_socket_set"} + prop_name='active_socket_set', props={'active_socket_set'} ) def on_value_changed__active_socket_set(self, props: dict): # Singular: Normal Output Sockets - if props["active_socket_set"] in {"Vacuum WL", "Frequency"}: + if props['active_socket_set'] in {'Vacuum WL', 'Frequency'}: self.loose_output_sockets = {} self.loose_output_sockets = { - "Freq": sockets.PhysicalFreqSocketDef(), - "WL": sockets.PhysicalLengthSocketDef(), + 'Freq': sockets.PhysicalFreqSocketDef(), + 'WL': sockets.PhysicalLengthSocketDef(), } - + # Plural: Listy Output Sockets - elif props["active_socket_set"] in {"Vacuum WLs", "Frequencies"}: + elif props['active_socket_set'] in {'Vacuum WLs', 'Frequencies'}: self.loose_output_sockets = {} self.loose_output_sockets = { - "Freqs": sockets.PhysicalFreqSocketDef(is_list=True), - "WLs": sockets.PhysicalLengthSocketDef(is_list=True), + 'Freqs': sockets.PhysicalFreqSocketDef(is_list=True), + 'WLs': sockets.PhysicalLengthSocketDef(is_list=True), } - + else: msg = f"Active socket set invalid for wave constant: {props['active_socket_set']}" raise RuntimeError(msg) - + @base.on_init() def on_init(self): self.on_value_changed__active_socket_set() - + #################### # - Blender Registration @@ -158,8 +154,4 @@ class WaveConstantNode(base.MaxwellSimNode): BL_REGISTER = [ WaveConstantNode, ] -BL_NODES = { - ct.NodeType.WaveConstant: ( - ct.NodeCategory.MAXWELLSIM_INPUTS - ) -} +BL_NODES = {ct.NodeType.WaveConstant: (ct.NodeCategory.MAXWELLSIM_INPUTS)} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/web_importers/tidy_3d_web_importer.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/web_importers/tidy_3d_web_importer.py index ea3fb55..62cfbee 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/web_importers/tidy_3d_web_importer.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/web_importers/tidy_3d_web_importer.py @@ -17,109 +17,108 @@ from ... import base CACHE = {} + #################### # - Node #################### class Tidy3DWebImporterNode(base.MaxwellSimNode): node_type = ct.NodeType.Tidy3DWebImporter - bl_label = "Tidy3DWebImporter" - + bl_label = 'Tidy3DWebImporter' + input_sockets = { - "Cloud Task": sockets.Tidy3DCloudTaskSocketDef( + 'Cloud Task': sockets.Tidy3DCloudTaskSocketDef( should_exist=True, ), - "Cache Path": sockets.FilePathSocketDef( - default_path=Path("loaded_simulation.hdf5") - ) + 'Cache Path': sockets.FilePathSocketDef( + default_path=Path('loaded_simulation.hdf5') + ), } - + #################### # - Output Methods #################### @base.computes_output_socket( - "FDTD Sim Data", - input_sockets={"Cloud Task", "Cache Path"}, + 'FDTD Sim Data', + input_sockets={'Cloud Task', 'Cache Path'}, ) def compute_fdtd_sim_data(self, input_sockets: dict) -> str: global CACHE if not CACHE.get(self.instance_id): - CACHE[self.instance_id] = {"fdtd_sim_data": None} - - if CACHE[self.instance_id]["fdtd_sim_data"] is not None: - return CACHE[self.instance_id]["fdtd_sim_data"] - + CACHE[self.instance_id] = {'fdtd_sim_data': None} + + if CACHE[self.instance_id]['fdtd_sim_data'] is not None: + return CACHE[self.instance_id]['fdtd_sim_data'] + if not ( - (cloud_task := input_sockets["Cloud Task"]) is not None + (cloud_task := input_sockets['Cloud Task']) is not None and isinstance(cloud_task, tdcloud.CloudTask) - and cloud_task.status == "success" + and cloud_task.status == 'success' ): - msg ="Won't attempt getting SimData" + msg = "Won't attempt getting SimData" raise RuntimeError(msg) - + # Load the Simulation - cache_path = input_sockets["Cache Path"] - if cache_path is None: - print("CACHE PATH IS NONE WHY") - return ## I guess? + cache_path = input_sockets['Cache Path'] + if cache_path is None: + print('CACHE PATH IS NONE WHY') + return ## I guess? if cache_path.is_file(): sim_data = td.SimulationData.from_file(str(cache_path)) - + else: sim_data = td_web.api.webapi.load( cloud_task.task_id, path=str(cache_path), ) - - CACHE[self.instance_id]["fdtd_sim_data"] = sim_data + + CACHE[self.instance_id]['fdtd_sim_data'] = sim_data return sim_data - + @base.computes_output_socket( - "FDTD Sim", - input_sockets={"Cloud Task"}, + 'FDTD Sim', + input_sockets={'Cloud Task'}, ) def compute_fdtd_sim(self, input_sockets: dict) -> str: if not isinstance( - cloud_task := input_sockets["Cloud Task"], - tdcloud.CloudTask + cloud_task := input_sockets['Cloud Task'], tdcloud.CloudTask ): - msg ="Input cloud task does not exist" + msg = 'Input cloud task does not exist' raise RuntimeError(msg) - + # Load the Simulation with tempfile.NamedTemporaryFile(delete=False) as f: _path_tmp = Path(f.name) - _path_tmp.rename(f.name + ".json") - path_tmp = Path(f.name + ".json") - + _path_tmp.rename(f.name + '.json') + path_tmp = Path(f.name + '.json') + sim = td_web.api.webapi.load_simulation( cloud_task.task_id, path=str(path_tmp), ) ## TODO: Don't use td_web directly. Only through tdcloud Path(path_tmp).unlink() - + return sim - + #################### # - Update #################### @base.on_value_changed( - socket_name="Cloud Task", - input_sockets={"Cloud Task"} + socket_name='Cloud Task', input_sockets={'Cloud Task'} ) def on_value_changed__cloud_task(self, input_sockets: dict): if ( - (cloud_task := input_sockets["Cloud Task"]) is not None + (cloud_task := input_sockets['Cloud Task']) is not None and isinstance(cloud_task, tdcloud.CloudTask) - and cloud_task.status == "success" + and cloud_task.status == 'success' ): self.loose_output_sockets = { - "FDTD Sim Data": sockets.MaxwellFDTDSimDataSocketDef(), - "FDTD Sim": sockets.MaxwellFDTDSimSocketDef(), + 'FDTD Sim Data': sockets.MaxwellFDTDSimDataSocketDef(), + 'FDTD Sim': sockets.MaxwellFDTDSimSocketDef(), } return - + self.loose_output_sockets = {} - + @base.on_init() def on_init(self): self.on_value_changed__cloud_task() diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/kitchen_sink.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/kitchen_sink.py index 9885e85..5b3069f 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/kitchen_sink.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/kitchen_sink.py @@ -8,95 +8,91 @@ from .. import contracts as ct from .. import sockets from . import base + class KitchenSinkNode(base.MaxwellSimNode): node_type = ct.NodeType.KitchenSink - - bl_label = "Kitchen Sink" - #bl_icon = ... - + + bl_label = 'Kitchen Sink' + # bl_icon = ... + #################### # - Sockets #################### input_sockets = { - "Static Data": sockets.AnySocketDef(), + 'Static Data': sockets.AnySocketDef(), } input_socket_sets = { - "Basic": { - "Any": sockets.AnySocketDef(), - "Bool": sockets.BoolSocketDef(), - "FilePath": sockets.FilePathSocketDef(), - "Text": sockets.TextSocketDef(), + 'Basic': { + 'Any': sockets.AnySocketDef(), + 'Bool': sockets.BoolSocketDef(), + 'FilePath': sockets.FilePathSocketDef(), + 'Text': sockets.TextSocketDef(), }, - "Number": { - "Integer": sockets.IntegerNumberSocketDef(), - "Rational": sockets.RationalNumberSocketDef(), - "Real": sockets.RealNumberSocketDef(), - "Complex": sockets.ComplexNumberSocketDef(), + 'Number': { + 'Integer': sockets.IntegerNumberSocketDef(), + 'Rational': sockets.RationalNumberSocketDef(), + 'Real': sockets.RealNumberSocketDef(), + 'Complex': sockets.ComplexNumberSocketDef(), }, - "Vector": { - "Real 2D": sockets.Real2DVectorSocketDef(), - "Real 3D": sockets.Real3DVectorSocketDef( + 'Vector': { + 'Real 2D': sockets.Real2DVectorSocketDef(), + 'Real 3D': sockets.Real3DVectorSocketDef( default_value=sp.Matrix([0.0, 0.0, 0.0]) ), - "Complex 2D": sockets.Complex2DVectorSocketDef(), - "Complex 3D": sockets.Complex3DVectorSocketDef(), + 'Complex 2D': sockets.Complex2DVectorSocketDef(), + 'Complex 3D': sockets.Complex3DVectorSocketDef(), }, - "Physical": { - "Time": sockets.PhysicalTimeSocketDef(), - #"physical_point_2d": sockets.PhysicalPoint2DSocketDef(), - "Angle": sockets.PhysicalAngleSocketDef(), - "Length": sockets.PhysicalLengthSocketDef(), - "Area": sockets.PhysicalAreaSocketDef(), - "Volume": sockets.PhysicalVolumeSocketDef(), - "Point 3D": sockets.PhysicalPoint3DSocketDef(), + 'Physical': { + 'Time': sockets.PhysicalTimeSocketDef(), + # "physical_point_2d": sockets.PhysicalPoint2DSocketDef(), + 'Angle': sockets.PhysicalAngleSocketDef(), + 'Length': sockets.PhysicalLengthSocketDef(), + 'Area': sockets.PhysicalAreaSocketDef(), + 'Volume': sockets.PhysicalVolumeSocketDef(), + 'Point 3D': sockets.PhysicalPoint3DSocketDef(), ##"physical_size_2d": sockets.PhysicalSize2DSocketDef(), - "Size 3D": sockets.PhysicalSize3DSocketDef(), - "Mass": sockets.PhysicalMassSocketDef(), - "Speed": sockets.PhysicalSpeedSocketDef(), - "Accel Scalar": sockets.PhysicalAccelScalarSocketDef(), - "Force Scalar": sockets.PhysicalForceScalarSocketDef(), - #"physical_accel_3dvector": sockets.PhysicalAccel3DVectorSocketDef(), + 'Size 3D': sockets.PhysicalSize3DSocketDef(), + 'Mass': sockets.PhysicalMassSocketDef(), + 'Speed': sockets.PhysicalSpeedSocketDef(), + 'Accel Scalar': sockets.PhysicalAccelScalarSocketDef(), + 'Force Scalar': sockets.PhysicalForceScalarSocketDef(), + # "physical_accel_3dvector": sockets.PhysicalAccel3DVectorSocketDef(), ##"physical_force_3dvector": sockets.PhysicalForce3DVectorSocketDef(), - "Pol": sockets.PhysicalPolSocketDef(), - "Freq": sockets.PhysicalFreqSocketDef(), + 'Pol': sockets.PhysicalPolSocketDef(), + 'Freq': sockets.PhysicalFreqSocketDef(), }, - "Blender": { - "Object": sockets.BlenderObjectSocketDef(), - "Collection": sockets.BlenderCollectionSocketDef(), - "Image": sockets.BlenderImageSocketDef(), - "GeoNodes": sockets.BlenderGeoNodesSocketDef(), - "Text": sockets.BlenderTextSocketDef(), + 'Blender': { + 'Object': sockets.BlenderObjectSocketDef(), + 'Collection': sockets.BlenderCollectionSocketDef(), + 'Image': sockets.BlenderImageSocketDef(), + 'GeoNodes': sockets.BlenderGeoNodesSocketDef(), + 'Text': sockets.BlenderTextSocketDef(), }, - "Maxwell": { - "Source": sockets.MaxwellSourceSocketDef(), - "Temporal Shape": sockets.MaxwellTemporalShapeSocketDef(), - "Medium": sockets.MaxwellMediumSocketDef(), - "Medium Non-Linearity": sockets.MaxwellMediumNonLinearitySocketDef(), - "Structure": sockets.MaxwellStructureSocketDef(), - "Bound Box": sockets.MaxwellBoundBoxSocketDef(), - "Bound Face": sockets.MaxwellBoundFaceSocketDef(), - "Monitor": sockets.MaxwellMonitorSocketDef(), - "FDTD Sim": sockets.MaxwellFDTDSimSocketDef(), - "Sim Grid": sockets.MaxwellSimGridSocketDef(), - "Sim Grid Axis": sockets.MaxwellSimGridAxisSocketDef(), + 'Maxwell': { + 'Source': sockets.MaxwellSourceSocketDef(), + 'Temporal Shape': sockets.MaxwellTemporalShapeSocketDef(), + 'Medium': sockets.MaxwellMediumSocketDef(), + 'Medium Non-Linearity': sockets.MaxwellMediumNonLinearitySocketDef(), + 'Structure': sockets.MaxwellStructureSocketDef(), + 'Bound Box': sockets.MaxwellBoundBoxSocketDef(), + 'Bound Face': sockets.MaxwellBoundFaceSocketDef(), + 'Monitor': sockets.MaxwellMonitorSocketDef(), + 'FDTD Sim': sockets.MaxwellFDTDSimSocketDef(), + 'Sim Grid': sockets.MaxwellSimGridSocketDef(), + 'Sim Grid Axis': sockets.MaxwellSimGridAxisSocketDef(), }, } - + output_sockets = { - "Static Data": sockets.AnySocketDef(), + 'Static Data': sockets.AnySocketDef(), } output_socket_sets = input_socket_sets - #################### # - Blender Registration #################### BL_REGISTER = [ KitchenSinkNode, ] -BL_NODES = { - ct.NodeType.KitchenSink: ( - ct.NodeCategory.MAXWELLSIM_INPUTS - ) -} +BL_NODES = {ct.NodeType.KitchenSink: (ct.NodeCategory.MAXWELLSIM_INPUTS)} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/__init__.py index a4d181e..7ebf174 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/__init__.py @@ -1,47 +1,45 @@ from . import library_medium -#from . import pec_medium -#from . import isotropic_medium -#from . import anisotropic_medium +# from . import pec_medium +# from . import isotropic_medium +# from . import anisotropic_medium # -#from . import triple_sellmeier_medium -#from . import sellmeier_medium -#from . import pole_residue_medium -#from . import drude_medium -#from . import drude_lorentz_medium -#from . import debye_medium +# from . import triple_sellmeier_medium +# from . import sellmeier_medium +# from . import pole_residue_medium +# from . import drude_medium +# from . import drude_lorentz_medium +# from . import debye_medium # -#from . import non_linearities +# from . import non_linearities BL_REGISTER = [ *library_medium.BL_REGISTER, - -# *pec_medium.BL_REGISTER, -# *isotropic_medium.BL_REGISTER, -# *anisotropic_medium.BL_REGISTER, -# -# *triple_sellmeier_medium.BL_REGISTER, -# *sellmeier_medium.BL_REGISTER, -# *pole_residue_medium.BL_REGISTER, -# *drude_medium.BL_REGISTER, -# *drude_lorentz_medium.BL_REGISTER, -# *debye_medium.BL_REGISTER, -# -# *non_linearities.BL_REGISTER, + # *pec_medium.BL_REGISTER, + # *isotropic_medium.BL_REGISTER, + # *anisotropic_medium.BL_REGISTER, + # + # *triple_sellmeier_medium.BL_REGISTER, + # *sellmeier_medium.BL_REGISTER, + # *pole_residue_medium.BL_REGISTER, + # *drude_medium.BL_REGISTER, + # *drude_lorentz_medium.BL_REGISTER, + # *debye_medium.BL_REGISTER, + # + # *non_linearities.BL_REGISTER, ] BL_NODES = { **library_medium.BL_NODES, - -# **pec_medium.BL_NODES, -# **isotropic_medium.BL_NODES, -# **anisotropic_medium.BL_NODES, -# -# **triple_sellmeier_medium.BL_NODES, -# **sellmeier_medium.BL_NODES, -# **pole_residue_medium.BL_NODES, -# **drude_medium.BL_NODES, -# **drude_lorentz_medium.BL_NODES, -# **debye_medium.BL_NODES, -# -# **non_linearities.BL_NODES, + # **pec_medium.BL_NODES, + # **isotropic_medium.BL_NODES, + # **anisotropic_medium.BL_NODES, + # + # **triple_sellmeier_medium.BL_NODES, + # **sellmeier_medium.BL_NODES, + # **pole_residue_medium.BL_NODES, + # **drude_medium.BL_NODES, + # **drude_lorentz_medium.BL_NODES, + # **debye_medium.BL_NODES, + # + # **non_linearities.BL_NODES, } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/debye_medium.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/debye_medium.py index 8f4a665..41fac16 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/debye_medium.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/debye_medium.py @@ -3,4 +3,3 @@ #################### BL_REGISTER = [] BL_NODES = {} - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/drude_lorentz_medium.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/drude_lorentz_medium.py index 9d0fc25..db6029e 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/drude_lorentz_medium.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/drude_lorentz_medium.py @@ -6,67 +6,72 @@ from ... import contracts from ... import sockets from .. import base + class DrudeLorentzMediumNode(base.MaxwellSimTreeNode): node_type = contracts.NodeType.DrudeLorentzMedium - - bl_label = "Drude-Lorentz Medium" - #bl_icon = ... - + + bl_label = 'Drude-Lorentz Medium' + # bl_icon = ... + #################### # - Sockets #################### - input_sockets = { - "eps_inf": sockets.RealNumberSocketDef( - label=f"εr_∞", - ), - } | { - f"del_eps{i}": sockets.RealNumberSocketDef( - label=f"Δεr_{i}", - ) - for i in [1, 2, 3] - } | { - f"f{i}": sockets.PhysicalFreqSocketDef( - label=f"f_{i}", - ) - for i in [1, 2, 3] - } | { - f"delta{i}": sockets.PhysicalFreqSocketDef( - label=f"δ_{i}", - ) - for i in [1, 2, 3] - } + input_sockets = ( + { + 'eps_inf': sockets.RealNumberSocketDef( + label=f'εr_∞', + ), + } + | { + f'del_eps{i}': sockets.RealNumberSocketDef( + label=f'Δεr_{i}', + ) + for i in [1, 2, 3] + } + | { + f'f{i}': sockets.PhysicalFreqSocketDef( + label=f'f_{i}', + ) + for i in [1, 2, 3] + } + | { + f'delta{i}': sockets.PhysicalFreqSocketDef( + label=f'δ_{i}', + ) + for i in [1, 2, 3] + } + ) output_sockets = { - "medium": sockets.MaxwellMediumSocketDef( - label="Medium" - ), + 'medium': sockets.MaxwellMediumSocketDef(label='Medium'), } - + #################### # - Output Socket Computation #################### - @base.computes_output_socket("medium") + @base.computes_output_socket('medium') def compute_medium(self: contracts.NodeTypeProtocol) -> td.Sellmeier: ## Retrieval return td.Lorentz( - eps_inf=self.compute_input(f"eps_inf"), - coeffs = [ - ( - self.compute_input(f"del_eps{i}"), - spu.convert_to( - self.compute_input(f"f{i}"), - spu.hertz, - ) / spu.hertz, - spu.convert_to( - self.compute_input(f"delta{i}"), - spu.hertz, - ) / spu.hertz, - ) - for i in [1, 2, 3] - ] + eps_inf=self.compute_input(f'eps_inf'), + coeffs=[ + ( + self.compute_input(f'del_eps{i}'), + spu.convert_to( + self.compute_input(f'f{i}'), + spu.hertz, + ) + / spu.hertz, + spu.convert_to( + self.compute_input(f'delta{i}'), + spu.hertz, + ) + / spu.hertz, + ) + for i in [1, 2, 3] + ], ) - #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/isotropic_medium.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/isotropic_medium.py index 8f4a665..41fac16 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/isotropic_medium.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/isotropic_medium.py @@ -3,4 +3,3 @@ #################### BL_REGISTER = [] BL_NODES = {} - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/library_medium.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/library_medium.py index 7d30f18..58a0fab 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/library_medium.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/library_medium.py @@ -14,55 +14,55 @@ from ... import sockets from ... import managed_objs from .. import base -VAC_SPEED_OF_LIGHT = ( - sc.constants.speed_of_light - * spu.meter/spu.second -) +VAC_SPEED_OF_LIGHT = sc.constants.speed_of_light * spu.meter / spu.second + class LibraryMediumNode(base.MaxwellSimNode): node_type = ct.NodeType.LibraryMedium - bl_label = "Library Medium" - + bl_label = 'Library Medium' + #################### # - Sockets #################### input_sockets = {} output_sockets = { - "Medium": sockets.MaxwellMediumSocketDef(), + 'Medium': sockets.MaxwellMediumSocketDef(), } - + managed_obj_defs = { - "nk_plot": ct.schemas.ManagedObjDef( + 'nk_plot': ct.schemas.ManagedObjDef( mk=lambda name: managed_objs.ManagedBLImage(name), - name_prefix="", + name_prefix='', ) } - + #################### # - Properties #################### material: bpy.props.EnumProperty( - name="", - description="", - #icon="NODE_MATERIAL", + name='', + description='', + # icon="NODE_MATERIAL", items=[ ( mat_key, td.material_library[mat_key].name, - ", ".join([ - ref.journal - for ref in td.material_library[mat_key].variants[ - td.material_library[mat_key].default - ].reference - ]) + ', '.join( + [ + ref.journal + for ref in td.material_library[mat_key] + .variants[td.material_library[mat_key].default] + .reference + ] + ), ) for mat_key in td.material_library - if mat_key != "graphene" ## For some reason, it's unique... + if mat_key != 'graphene' ## For some reason, it's unique... ], - default="Au", - update=(lambda self, context: self.sync_prop("material", context)), + default='Au', + update=(lambda self, context: self.sync_prop('material', context)), ) - + @property def freq_range_str(self) -> tuple[sp.Expr, sp.Expr]: ## TODO: Cache (node instances don't seem able to keep data outside of properties, not even cached_property) @@ -71,14 +71,14 @@ class LibraryMediumNode(base.MaxwellSimNode): spu.convert_to( val * spu.hertz, spuex.terahertz, - ) / spuex.terahertz + ) + / spuex.terahertz for val in mat.medium.frequency_range ] return sp.pretty( - [freq_range[0].n(4), freq_range[1].n(4)], - use_unicode=True + [freq_range[0].n(4), freq_range[1].n(4)], use_unicode=True ) - + @property def nm_range_str(self) -> str: ## TODO: Cache (node instances don't seem able to keep data outside of properties, not even cached_property) @@ -87,47 +87,47 @@ class LibraryMediumNode(base.MaxwellSimNode): spu.convert_to( VAC_SPEED_OF_LIGHT / (val * spu.hertz), spu.nanometer, - ) / spu.nanometer + ) + / spu.nanometer for val in reversed(mat.medium.frequency_range) ] return sp.pretty( - [nm_range[0].n(4), nm_range[1].n(4)], - use_unicode=True + [nm_range[0].n(4), nm_range[1].n(4)], use_unicode=True ) - + #################### # - UI #################### def draw_props(self, context, layout): - layout.prop(self, "material", text="") - + layout.prop(self, 'material', text='') + def draw_info(self, context, col): # UI Drawing split = col.split(factor=0.23, align=True) - + _col = split.column(align=True) - _col.alignment = "LEFT" - _col.label(text="nm") - _col.label(text="THz") - + _col.alignment = 'LEFT' + _col.label(text='nm') + _col.label(text='THz') + _col = split.column(align=True) - _col.alignment = "RIGHT" + _col.alignment = 'RIGHT' _col.label(text=self.nm_range_str) _col.label(text=self.freq_range_str) - + #################### # - Output Sockets #################### - @base.computes_output_socket("Medium") + @base.computes_output_socket('Medium') def compute_vac_wl(self) -> sp.Expr: return td.material_library[self.material].medium - + #################### # - Event Callbacks #################### @base.on_show_plot( - managed_objs={"nk_plot"}, - props={"material"}, + managed_objs={'nk_plot'}, + props={'material'}, stop_propagation=True, ## Plot only the first plottable node ) def on_show_plot( @@ -135,28 +135,26 @@ class LibraryMediumNode(base.MaxwellSimNode): managed_objs: dict[str, ct.schemas.ManagedObj], props: dict[str, typ.Any], ): - medium = td.material_library[props["material"]].medium + medium = td.material_library[props['material']].medium freq_range = [ spu.convert_to( val * spu.hertz, spuex.terahertz, - ) / spu.hertz + ) + / spu.hertz for val in medium.frequency_range ] - - managed_objs["nk_plot"].mpl_plot_to_image( + + managed_objs['nk_plot'].mpl_plot_to_image( lambda ax: medium.plot(medium.frequency_range, ax=ax), bl_select=True, ) + #################### # - Blender Registration #################### BL_REGISTER = [ LibraryMediumNode, ] -BL_NODES = { - ct.NodeType.LibraryMedium: ( - ct.NodeCategory.MAXWELLSIM_MEDIUMS - ) -} +BL_NODES = {ct.NodeType.LibraryMedium: (ct.NodeCategory.MAXWELLSIM_MEDIUMS)} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/pec_medium.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/pec_medium.py index 8f4a665..41fac16 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/pec_medium.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/pec_medium.py @@ -3,4 +3,3 @@ #################### BL_REGISTER = [] BL_NODES = {} - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/pole_residue_medium.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/pole_residue_medium.py index 8f4a665..41fac16 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/pole_residue_medium.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/pole_residue_medium.py @@ -3,4 +3,3 @@ #################### BL_REGISTER = [] BL_NODES = {} - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/sellmeier_medium.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/sellmeier_medium.py index 8f4a665..41fac16 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/sellmeier_medium.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/sellmeier_medium.py @@ -3,4 +3,3 @@ #################### BL_REGISTER = [] BL_NODES = {} - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/triple_sellmeier_medium.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/triple_sellmeier_medium.py index 843ac48..8c3e368 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/triple_sellmeier_medium.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/triple_sellmeier_medium.py @@ -6,86 +6,86 @@ from ... import contracts from ... import sockets from .. import base + class TripleSellmeierMediumNode(base.MaxwellSimTreeNode): node_type = contracts.NodeType.TripleSellmeierMedium - - bl_label = "Three-Parameter Sellmeier Medium" - #bl_icon = ... - + + bl_label = 'Three-Parameter Sellmeier Medium' + # bl_icon = ... + #################### # - Sockets #################### input_sockets = { - f"B{i}": sockets.RealNumberSocketDef( - label=f"B{i}", + f'B{i}': sockets.RealNumberSocketDef( + label=f'B{i}', ) for i in [1, 2, 3] } | { - f"C{i}": sockets.PhysicalAreaSocketDef( - label=f"C{i}", - default_unit=spu.um**2 + f'C{i}': sockets.PhysicalAreaSocketDef( + label=f'C{i}', default_unit=spu.um**2 ) for i in [1, 2, 3] } output_sockets = { - "medium": sockets.MaxwellMediumSocketDef( - label="Medium" - ), + 'medium': sockets.MaxwellMediumSocketDef(label='Medium'), } - + #################### # - Presets #################### presets = { - "BK7": contracts.PresetDef( - label="BK7 Glass", - description="Borosilicate crown glass (known as BK7)", + 'BK7': contracts.PresetDef( + label='BK7 Glass', + description='Borosilicate crown glass (known as BK7)', values={ - "B1": 1.03961212, - "B2": 0.231792344, - "B3": 1.01046945, - "C1": 6.00069867e-3 * spu.um**2, - "C2": 2.00179144e-2 * spu.um**2, - "C3": 103.560653 * spu.um**2, - } + 'B1': 1.03961212, + 'B2': 0.231792344, + 'B3': 1.01046945, + 'C1': 6.00069867e-3 * spu.um**2, + 'C2': 2.00179144e-2 * spu.um**2, + 'C3': 103.560653 * spu.um**2, + }, ), - "FUSED_SILICA": contracts.PresetDef( - label="Fused Silica", - description="Fused silica aka. SiO2", + 'FUSED_SILICA': contracts.PresetDef( + label='Fused Silica', + description='Fused silica aka. SiO2', values={ - "B1": 0.696166300, - "B2": 0.407942600, - "B3": 0.897479400, - "C1": 4.67914826e-3 * spu.um**2, - "C2": 1.35120631e-2 * spu.um**2, - "C3": 97.9340025 * spu.um**2, - } + 'B1': 0.696166300, + 'B2': 0.407942600, + 'B3': 0.897479400, + 'C1': 4.67914826e-3 * spu.um**2, + 'C2': 1.35120631e-2 * spu.um**2, + 'C3': 97.9340025 * spu.um**2, + }, ), } - + #################### # - Output Socket Computation #################### - @base.computes_output_socket("medium") + @base.computes_output_socket('medium') def compute_medium(self: contracts.NodeTypeProtocol) -> td.Sellmeier: ## Retrieval - #B1 = self.compute_input("B1") - #C1_with_units = self.compute_input("C1") + # B1 = self.compute_input("B1") + # C1_with_units = self.compute_input("C1") # ## Processing - #C1 = spu.convert_to(C1_with_units, spu.um**2) / spu.um**2 - - return td.Sellmeier(coeffs = [ - ( - self.compute_input(f"B{i}"), - spu.convert_to( - self.compute_input(f"C{i}"), - spu.um**2, - ) / spu.um**2 - ) - for i in [1, 2, 3] - ]) + # C1 = spu.convert_to(C1_with_units, spu.um**2) / spu.um**2 + return td.Sellmeier( + coeffs=[ + ( + self.compute_input(f'B{i}'), + spu.convert_to( + self.compute_input(f'C{i}'), + spu.um**2, + ) + / spu.um**2, + ) + for i in [1, 2, 3] + ] + ) #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/__init__.py index 2070702..5a658d3 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/__init__.py @@ -1,17 +1,17 @@ from . import eh_field_monitor from . import field_power_flux_monitor -#from . import epsilon_tensor_monitor -#from . import diffraction_monitor +# from . import epsilon_tensor_monitor +# from . import diffraction_monitor BL_REGISTER = [ *eh_field_monitor.BL_REGISTER, *field_power_flux_monitor.BL_REGISTER, -# *epsilon_tensor_monitor.BL_REGISTER, -# *diffraction_monitor.BL_REGISTER, + # *epsilon_tensor_monitor.BL_REGISTER, + # *diffraction_monitor.BL_REGISTER, ] BL_NODES = { **eh_field_monitor.BL_NODES, **field_power_flux_monitor.BL_NODES, -# **epsilon_tensor_monitor.BL_NODES, -# **diffraction_monitor.BL_NODES, + # **epsilon_tensor_monitor.BL_NODES, + # **diffraction_monitor.BL_NODES, } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/diffraction_monitor.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/diffraction_monitor.py index 8f4a665..41fac16 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/diffraction_monitor.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/diffraction_monitor.py @@ -3,4 +3,3 @@ #################### BL_REGISTER = [] BL_NODES = {} - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/eh_field_monitor.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/eh_field_monitor.py index e87597b..543b0de 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/eh_field_monitor.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/eh_field_monitor.py @@ -15,90 +15,98 @@ from ... import sockets from ... import managed_objs from .. import base -GEONODES_MONITOR_BOX = "monitor_box" +GEONODES_MONITOR_BOX = 'monitor_box' + class EHFieldMonitorNode(base.MaxwellSimNode): node_type = ct.NodeType.EHFieldMonitor - bl_label = "E/H Field Monitor" + bl_label = 'E/H Field Monitor' use_sim_node_name = True - + #################### # - Sockets #################### input_sockets = { - "Center": sockets.PhysicalPoint3DSocketDef(), - "Size": sockets.PhysicalSize3DSocketDef(), - "Samples/Space": sockets.Integer3DVectorSocketDef( + 'Center': sockets.PhysicalPoint3DSocketDef(), + 'Size': sockets.PhysicalSize3DSocketDef(), + 'Samples/Space': sockets.Integer3DVectorSocketDef( default_value=sp.Matrix([10, 10, 10]) ), } input_socket_sets = { - "Freq Domain": { - "Freqs": sockets.PhysicalFreqSocketDef( + 'Freq Domain': { + 'Freqs': sockets.PhysicalFreqSocketDef( is_list=True, ), }, - "Time Domain": { - "Rec Start": sockets.PhysicalTimeSocketDef(), - "Rec Stop": sockets.PhysicalTimeSocketDef( - default_value=200*spux.fs + 'Time Domain': { + 'Rec Start': sockets.PhysicalTimeSocketDef(), + 'Rec Stop': sockets.PhysicalTimeSocketDef( + default_value=200 * spux.fs ), - "Samples/Time": sockets.IntegerNumberSocketDef( + 'Samples/Time': sockets.IntegerNumberSocketDef( default_value=100, ), }, } output_sockets = { - "Monitor": sockets.MaxwellMonitorSocketDef(), + 'Monitor': sockets.MaxwellMonitorSocketDef(), } - + managed_obj_defs = { - "monitor_box": ct.schemas.ManagedObjDef( + 'monitor_box': ct.schemas.ManagedObjDef( mk=lambda name: managed_objs.ManagedBLObject(name), - name_prefix="", + name_prefix='', ) } - + #################### # - Properties #################### - + #################### # - UI #################### def draw_props(self, context, layout): pass - + def draw_info(self, context, col): pass - + #################### # - Output Sockets #################### @base.computes_output_socket( - "Monitor", + 'Monitor', input_sockets={ - "Rec Start", "Rec Stop", "Center", "Size", "Samples/Space", - "Samples/Time", "Freqs", + 'Rec Start', + 'Rec Stop', + 'Center', + 'Size', + 'Samples/Space', + 'Samples/Time', + 'Freqs', }, - props={"active_socket_set", "sim_node_name"} + props={'active_socket_set', 'sim_node_name'}, ) - def compute_monitor(self, input_sockets: dict, props: dict) -> td.FieldTimeMonitor: - _center = input_sockets["Center"] - _size = input_sockets["Size"] - _samples_space = input_sockets["Samples/Space"] - + def compute_monitor( + self, input_sockets: dict, props: dict + ) -> td.FieldTimeMonitor: + _center = input_sockets['Center'] + _size = input_sockets['Size'] + _samples_space = input_sockets['Samples/Space'] + center = tuple(spu.convert_to(_center, spu.um) / spu.um) size = tuple(spu.convert_to(_size, spu.um) / spu.um) samples_space = tuple(_samples_space) - - if props["active_socket_set"] == "Freq Domain": - freqs = input_sockets["Freqs"] - + + if props['active_socket_set'] == 'Freq Domain': + freqs = input_sockets['Freqs'] + return td.FieldMonitor( center=center, size=size, - name=props["sim_node_name"], + name=props['sim_node_name'], interval_space=samples_space, freqs=[ float(spu.convert_to(freq, spu.hertz) / spu.hertz) @@ -106,91 +114,86 @@ class EHFieldMonitorNode(base.MaxwellSimNode): ], ) else: ## Time Domain - _rec_start = input_sockets["Rec Start"] - _rec_stop = input_sockets["Rec Stop"] - samples_time = input_sockets["Samples/Time"] - + _rec_start = input_sockets['Rec Start'] + _rec_stop = input_sockets['Rec Stop'] + samples_time = input_sockets['Samples/Time'] + rec_start = spu.convert_to(_rec_start, spu.second) / spu.second rec_stop = spu.convert_to(_rec_stop, spu.second) / spu.second - + return td.FieldTimeMonitor( center=center, size=size, - name=props["sim_node_name"], + name=props['sim_node_name'], start=rec_start, stop=rec_stop, interval=samples_time, interval_space=samples_space, ) - + #################### # - Preview - Changes to Input Sockets #################### @base.on_value_changed( - socket_name={"Center", "Size"}, - input_sockets={"Center", "Size"}, - managed_objs={"monitor_box"}, + socket_name={'Center', 'Size'}, + input_sockets={'Center', 'Size'}, + managed_objs={'monitor_box'}, ) def on_value_changed__center_size( self, input_sockets: dict, managed_objs: dict[str, ct.schemas.ManagedObj], ): - _center = input_sockets["Center"] - center = tuple([ - float(el) - for el in spu.convert_to(_center, spu.um) / spu.um - ]) - - _size = input_sockets["Size"] - size = tuple([ - float(el) - for el in spu.convert_to(_size, spu.um) / spu.um - ]) + _center = input_sockets['Center'] + center = tuple( + [float(el) for el in spu.convert_to(_center, spu.um) / spu.um] + ) + + _size = input_sockets['Size'] + size = tuple( + [float(el) for el in spu.convert_to(_size, spu.um) / spu.um] + ) ## TODO: Preview unit system?? Presume um for now - + # Retrieve Hard-Coded GeoNodes and Analyze Input geo_nodes = bpy.data.node_groups[GEONODES_MONITOR_BOX] geonodes_interface = analyze_geonodes.interface( - geo_nodes, direc="INPUT" + geo_nodes, direc='INPUT' ) - + # Sync Modifier Inputs - managed_objs["monitor_box"].sync_geonodes_modifier( + managed_objs['monitor_box'].sync_geonodes_modifier( geonodes_node_group=geo_nodes, geonodes_identifier_to_value={ - geonodes_interface["Size"].identifier: size, + geonodes_interface['Size'].identifier: size, ## TODO: Use 'bl_socket_map.value_to_bl`! ## - This accounts for auto-conversion, unit systems, etc. . ## - We could keep it in the node base class... ## - ...But it needs aligning with Blender, too. Hmm. - } + }, ) - + # Sync Object Position - managed_objs["monitor_box"].bl_object("MESH").location = center - + managed_objs['monitor_box'].bl_object('MESH').location = center + #################### # - Preview - Show Preview #################### @base.on_show_preview( - managed_objs={"monitor_box"}, + managed_objs={'monitor_box'}, ) def on_show_preview( self, managed_objs: dict[str, ct.schemas.ManagedObj], ): - managed_objs["monitor_box"].show_preview("MESH") + managed_objs['monitor_box'].show_preview('MESH') self.on_value_changed__center_size() + #################### # - Blender Registration #################### BL_REGISTER = [ EHFieldMonitorNode, ] -BL_NODES = { - ct.NodeType.EHFieldMonitor: ( - ct.NodeCategory.MAXWELLSIM_MONITORS - ) -} +BL_NODES = {ct.NodeType.EHFieldMonitor: (ct.NodeCategory.MAXWELLSIM_MONITORS)} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/epsilon_tensor_monitor.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/epsilon_tensor_monitor.py index 8f4a665..41fac16 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/epsilon_tensor_monitor.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/epsilon_tensor_monitor.py @@ -3,4 +3,3 @@ #################### BL_REGISTER = [] BL_NODES = {} - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/field_power_flux_monitor.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/field_power_flux_monitor.py index b9f32bc..54e76b8 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/field_power_flux_monitor.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/monitors/field_power_flux_monitor.py @@ -15,93 +15,102 @@ from ... import sockets from ... import managed_objs from .. import base -GEONODES_MONITOR_BOX = "monitor_flux_box" +GEONODES_MONITOR_BOX = 'monitor_flux_box' + class FieldPowerFluxMonitorNode(base.MaxwellSimNode): node_type = ct.NodeType.FieldPowerFluxMonitor - bl_label = "Field Power Flux Monitor" + bl_label = 'Field Power Flux Monitor' use_sim_node_name = True - + #################### # - Sockets #################### input_sockets = { - "Center": sockets.PhysicalPoint3DSocketDef(), - "Size": sockets.PhysicalSize3DSocketDef(), - "Samples/Space": sockets.Integer3DVectorSocketDef( + 'Center': sockets.PhysicalPoint3DSocketDef(), + 'Size': sockets.PhysicalSize3DSocketDef(), + 'Samples/Space': sockets.Integer3DVectorSocketDef( default_value=sp.Matrix([10, 10, 10]) ), - "Direction": sockets.BoolSocketDef(), + 'Direction': sockets.BoolSocketDef(), } input_socket_sets = { - "Freq Domain": { - "Freqs": sockets.PhysicalFreqSocketDef( + 'Freq Domain': { + 'Freqs': sockets.PhysicalFreqSocketDef( is_list=True, ), }, - "Time Domain": { - "Rec Start": sockets.PhysicalTimeSocketDef(), - "Rec Stop": sockets.PhysicalTimeSocketDef( - default_value=200*spux.fs + 'Time Domain': { + 'Rec Start': sockets.PhysicalTimeSocketDef(), + 'Rec Stop': sockets.PhysicalTimeSocketDef( + default_value=200 * spux.fs ), - "Samples/Time": sockets.IntegerNumberSocketDef( + 'Samples/Time': sockets.IntegerNumberSocketDef( default_value=100, ), }, } output_sockets = { - "Monitor": sockets.MaxwellMonitorSocketDef(), + 'Monitor': sockets.MaxwellMonitorSocketDef(), } - + managed_obj_defs = { - "monitor_box": ct.schemas.ManagedObjDef( + 'monitor_box': ct.schemas.ManagedObjDef( mk=lambda name: managed_objs.ManagedBLObject(name), - name_prefix="", + name_prefix='', ) } - + #################### # - Properties #################### - + #################### # - UI #################### def draw_props(self, context, layout): pass - + def draw_info(self, context, col): pass - + #################### # - Output Sockets #################### @base.computes_output_socket( - "Monitor", + 'Monitor', input_sockets={ - "Rec Start", "Rec Stop", "Center", "Size", "Samples/Space", - "Samples/Time", "Freqs", "Direction", + 'Rec Start', + 'Rec Stop', + 'Center', + 'Size', + 'Samples/Space', + 'Samples/Time', + 'Freqs', + 'Direction', }, - props={"active_socket_set", "sim_node_name"} + props={'active_socket_set', 'sim_node_name'}, ) - def compute_monitor(self, input_sockets: dict, props: dict) -> td.FieldTimeMonitor: - _center = input_sockets["Center"] - _size = input_sockets["Size"] - _samples_space = input_sockets["Samples/Space"] - + def compute_monitor( + self, input_sockets: dict, props: dict + ) -> td.FieldTimeMonitor: + _center = input_sockets['Center'] + _size = input_sockets['Size'] + _samples_space = input_sockets['Samples/Space'] + center = tuple(spu.convert_to(_center, spu.um) / spu.um) size = tuple(spu.convert_to(_size, spu.um) / spu.um) samples_space = tuple(_samples_space) - - direction = "+" if input_sockets["Direction"] else "-" - - if props["active_socket_set"] == "Freq Domain": - freqs = input_sockets["Freqs"] - + + direction = '+' if input_sockets['Direction'] else '-' + + if props['active_socket_set'] == 'Freq Domain': + freqs = input_sockets['Freqs'] + return td.FluxMonitor( center=center, size=size, - name=props["sim_node_name"], + name=props['sim_node_name'], interval_space=samples_space, freqs=[ float(spu.convert_to(freq, spu.hertz) / spu.hertz) @@ -110,84 +119,85 @@ class FieldPowerFluxMonitorNode(base.MaxwellSimNode): normal_dir=direction, ) else: ## Time Domain - _rec_start = input_sockets["Rec Start"] - _rec_stop = input_sockets["Rec Stop"] - samples_time = input_sockets["Samples/Time"] - + _rec_start = input_sockets['Rec Start'] + _rec_stop = input_sockets['Rec Stop'] + samples_time = input_sockets['Samples/Time'] + rec_start = spu.convert_to(_rec_start, spu.second) / spu.second rec_stop = spu.convert_to(_rec_stop, spu.second) / spu.second - + return td.FieldTimeMonitor( center=center, size=size, - name=props["sim_node_name"], + name=props['sim_node_name'], start=rec_start, stop=rec_stop, interval=samples_time, interval_space=samples_space, ) - + #################### # - Preview - Changes to Input Sockets #################### @base.on_value_changed( - socket_name={"Center", "Size"}, - input_sockets={"Center", "Size", "Direction"}, - managed_objs={"monitor_box"}, + socket_name={'Center', 'Size'}, + input_sockets={'Center', 'Size', 'Direction'}, + managed_objs={'monitor_box'}, ) def on_value_changed__center_size( self, input_sockets: dict, managed_objs: dict[str, ct.schemas.ManagedObj], ): - _center = input_sockets["Center"] - center = tuple([ - float(el) - for el in spu.convert_to(_center, spu.um) / spu.um - ]) - - _size = input_sockets["Size"] - size = tuple([ - float(el) - for el in spu.convert_to(_size, spu.um) / spu.um - ]) + _center = input_sockets['Center'] + center = tuple( + [float(el) for el in spu.convert_to(_center, spu.um) / spu.um] + ) + + _size = input_sockets['Size'] + size = tuple( + [float(el) for el in spu.convert_to(_size, spu.um) / spu.um] + ) ## TODO: Preview unit system?? Presume um for now - + # Retrieve Hard-Coded GeoNodes and Analyze Input geo_nodes = bpy.data.node_groups[GEONODES_MONITOR_BOX] geonodes_interface = analyze_geonodes.interface( - geo_nodes, direc="INPUT" + geo_nodes, direc='INPUT' ) - + # Sync Modifier Inputs - managed_objs["monitor_box"].sync_geonodes_modifier( + managed_objs['monitor_box'].sync_geonodes_modifier( geonodes_node_group=geo_nodes, geonodes_identifier_to_value={ - geonodes_interface["Size"].identifier: size, - geonodes_interface["Direction"].identifier: input_sockets["Direction"], + geonodes_interface['Size'].identifier: size, + geonodes_interface['Direction'].identifier: input_sockets[ + 'Direction' + ], ## TODO: Use 'bl_socket_map.value_to_bl`! ## - This accounts for auto-conversion, unit systems, etc. . ## - We could keep it in the node base class... ## - ...But it needs aligning with Blender, too. Hmm. - } + }, ) - + # Sync Object Position - managed_objs["monitor_box"].bl_object("MESH").location = center - + managed_objs['monitor_box'].bl_object('MESH').location = center + #################### # - Preview - Show Preview #################### @base.on_show_preview( - managed_objs={"monitor_box"}, + managed_objs={'monitor_box'}, ) def on_show_preview( self, managed_objs: dict[str, ct.schemas.ManagedObj], ): - managed_objs["monitor_box"].show_preview("MESH") + managed_objs['monitor_box'].show_preview('MESH') self.on_value_changed__center_size() + #################### # - Blender Registration #################### @@ -195,7 +205,5 @@ BL_REGISTER = [ FieldPowerFluxMonitorNode, ] BL_NODES = { - ct.NodeType.FieldPowerFluxMonitor: ( - ct.NodeCategory.MAXWELLSIM_MONITORS - ) + ct.NodeType.FieldPowerFluxMonitor: (ct.NodeCategory.MAXWELLSIM_MONITORS) } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/json_file_exporter.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/json_file_exporter.py index de97103..16b7b66 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/json_file_exporter.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/json_file_exporter.py @@ -11,11 +11,12 @@ from .... import contracts as ct from .... import sockets from ... import base + #################### # - Operators #################### class JSONFileExporterSaveJSON(bpy.types.Operator): - bl_idname = "blender_maxwell.json_file_exporter_save_json" + bl_idname = 'blender_maxwell.json_file_exporter_save_json' bl_label = "Save the JSON of what's linked into a JSONFileExporterNode." @classmethod @@ -27,28 +28,29 @@ class JSONFileExporterSaveJSON(bpy.types.Operator): node.export_data_as_json() return {'FINISHED'} + #################### # - Node #################### class JSONFileExporterNode(base.MaxwellSimNode): node_type = ct.NodeType.JSONFileExporter - - bl_label = "JSON File Exporter" - #bl_icon = constants.ICON_SIM_INPUT - + + bl_label = 'JSON File Exporter' + # bl_icon = constants.ICON_SIM_INPUT + input_sockets = { - "Data": sockets.AnySocketDef(), - "JSON Path": sockets.FilePathSocketDef( - default_path=Path("simulation.json") + 'Data': sockets.AnySocketDef(), + 'JSON Path': sockets.FilePathSocketDef( + default_path=Path('simulation.json') ), - "JSON Indent": sockets.IntegerNumberSocketDef( + 'JSON Indent': sockets.IntegerNumberSocketDef( default_value=4, ), } output_sockets = { - "JSON String": sockets.StringSocketDef(), + 'JSON String': sockets.StringSocketDef(), } - + #################### # - UI Layout #################### @@ -57,37 +59,39 @@ class JSONFileExporterNode(base.MaxwellSimNode): context: bpy.types.Context, layout: bpy.types.UILayout, ) -> None: - layout.operator(JSONFileExporterSaveJSON.bl_idname, text="Save JSON") + layout.operator(JSONFileExporterSaveJSON.bl_idname, text='Save JSON') #################### # - Methods #################### def export_data_as_json(self) -> None: - if (json_str := self.compute_output("JSON String")): + if json_str := self.compute_output('JSON String'): data_dict = json.loads(json_str) - with self._compute_input("JSON Path").open("w") as f: - indent = self._compute_input("JSON Indent") + with self._compute_input('JSON Path').open('w') as f: + indent = self._compute_input('JSON Indent') json.dump(data_dict, f, ensure_ascii=False, indent=indent) - + #################### # - Output Sockets #################### @base.computes_output_socket( - "JSON String", - input_sockets={"Data"}, + 'JSON String', + input_sockets={'Data'}, ) - def compute_json_string(self, input_sockets: dict[str, typ.Any]) -> str | None: - if not (data := input_sockets["Data"]): + def compute_json_string( + self, input_sockets: dict[str, typ.Any] + ) -> str | None: + if not (data := input_sockets['Data']): return None - + # Tidy3D Objects: Call .json() - if hasattr(data, "json"): + if hasattr(data, 'json'): return data.json() - + # Pydantic Models: Call .model_dump_json() elif isinstance(data, pyd.BaseModel): return data.model_dump_json() - + else: json.dumps(data) diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/tidy3d_web_exporter.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/tidy3d_web_exporter.py index aa0bd49..ad70f59 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/tidy3d_web_exporter.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/tidy3d_web_exporter.py @@ -16,25 +16,25 @@ from .... import contracts as ct from .... import sockets from ... import base + #################### # - Web Uploader / Loader / Runner / Releaser #################### class UploadSimulation(bpy.types.Operator): - bl_idname = "blender_maxwell.nodes__upload_simulation" - bl_label = "Upload Tidy3D Simulation" - bl_description = "Upload the attached (locked) simulation, such that it is ready to run on the Tidy3D cloud" + bl_idname = 'blender_maxwell.nodes__upload_simulation' + bl_label = 'Upload Tidy3D Simulation' + bl_description = 'Upload the attached (locked) simulation, such that it is ready to run on the Tidy3D cloud' @classmethod def poll(cls, context): return ( - hasattr(context, "node") - and hasattr(context.node, "node_type") + hasattr(context, 'node') + and hasattr(context.node, 'node_type') and context.node.node_type == ct.NodeType.Tidy3DWebExporter - and context.node.lock_tree and tdcloud.IS_AUTHENTICATED and not context.node.tracked_task_id - and context.node.inputs["FDTD Sim"].is_linked + and context.node.inputs['FDTD Sim'].is_linked ) def execute(self, context): @@ -42,24 +42,27 @@ class UploadSimulation(bpy.types.Operator): node.upload_sim() return {'FINISHED'} + class RunSimulation(bpy.types.Operator): - bl_idname = "blender_maxwell.nodes__run_simulation" - bl_label = "Run Tracked Tidy3D Sim" - bl_description = "Run the currently tracked simulation task" + bl_idname = 'blender_maxwell.nodes__run_simulation' + bl_label = 'Run Tracked Tidy3D Sim' + bl_description = 'Run the currently tracked simulation task' @classmethod def poll(cls, context): return ( - hasattr(context, "node") - and hasattr(context.node, "node_type") + hasattr(context, 'node') + and hasattr(context.node, 'node_type') and context.node.node_type == ct.NodeType.Tidy3DWebExporter - and tdcloud.IS_AUTHENTICATED and context.node.tracked_task_id - and (task_info := tdcloud.TidyCloudTasks.task_info( - context.node.tracked_task_id - )) is not None - and task_info.status == "draft" + and ( + task_info := tdcloud.TidyCloudTasks.task_info( + context.node.tracked_task_id + ) + ) + is not None + and task_info.status == 'draft' ) def execute(self, context): @@ -67,18 +70,18 @@ class RunSimulation(bpy.types.Operator): node.run_tracked_task() return {'FINISHED'} + class ReloadTrackedTask(bpy.types.Operator): - bl_idname = "blender_maxwell.nodes__reload_tracked_task" - bl_label = "Reload Tracked Tidy3D Cloud Task" - bl_description = "Reload the currently tracked simulation task" + bl_idname = 'blender_maxwell.nodes__reload_tracked_task' + bl_label = 'Reload Tracked Tidy3D Cloud Task' + bl_description = 'Reload the currently tracked simulation task' @classmethod def poll(cls, context): return ( - hasattr(context, "node") - and hasattr(context.node, "node_type") + hasattr(context, 'node') + and hasattr(context.node, 'node_type') and context.node.node_type == ct.NodeType.Tidy3DWebExporter - and tdcloud.IS_AUTHENTICATED and context.node.tracked_task_id ) @@ -90,22 +93,22 @@ class ReloadTrackedTask(bpy.types.Operator): ) is None: msg = "Tried to reload tracked task, but it doesn't exist" raise RuntimeError(msg) - + cloud_task = tdcloud.TidyCloudTasks.update_task(cloud_task) return {'FINISHED'} + class EstCostTrackedTask(bpy.types.Operator): - bl_idname = "blender_maxwell.nodes__est_cost_tracked_task" - bl_label = "Est Cost of Tracked Tidy3D Cloud Task" - bl_description = "Reload the currently tracked simulation task" + bl_idname = 'blender_maxwell.nodes__est_cost_tracked_task' + bl_label = 'Est Cost of Tracked Tidy3D Cloud Task' + bl_description = 'Reload the currently tracked simulation task' @classmethod def poll(cls, context): return ( - hasattr(context, "node") - and hasattr(context.node, "node_type") + hasattr(context, 'node') + and hasattr(context.node, 'node_type') and context.node.node_type == ct.NodeType.Tidy3DWebExporter - and tdcloud.IS_AUTHENTICATED and context.node.tracked_task_id ) @@ -113,142 +116,145 @@ class EstCostTrackedTask(bpy.types.Operator): def execute(self, context): node = context.node if ( - task_info := tdcloud.TidyCloudTasks.task_info(context.node.tracked_task_id) + task_info := tdcloud.TidyCloudTasks.task_info( + context.node.tracked_task_id + ) ) is None: - msg = "Tried to estimate cost of tracked task, but it doesn't exist" + msg = ( + "Tried to estimate cost of tracked task, but it doesn't exist" + ) raise RuntimeError(msg) - + node.cache_est_cost = task_info.cost_est() return {'FINISHED'} + class ReleaseTrackedTask(bpy.types.Operator): - bl_idname = "blender_maxwell.nodes__release_tracked_task" - bl_label = "Release Tracked Tidy3D Cloud Task" - bl_description = "Release the currently tracked simulation task" + bl_idname = 'blender_maxwell.nodes__release_tracked_task' + bl_label = 'Release Tracked Tidy3D Cloud Task' + bl_description = 'Release the currently tracked simulation task' @classmethod def poll(cls, context): return ( - hasattr(context, "node") - and hasattr(context.node, "node_type") + hasattr(context, 'node') + and hasattr(context.node, 'node_type') and context.node.node_type == ct.NodeType.Tidy3DWebExporter - - #and tdcloud.IS_AUTHENTICATED + # and tdcloud.IS_AUTHENTICATED and context.node.tracked_task_id ) def execute(self, context): node = context.node - node.tracked_task_id = "" + node.tracked_task_id = '' return {'FINISHED'} - #################### # - Node #################### class Tidy3DWebExporterNode(base.MaxwellSimNode): node_type = ct.NodeType.Tidy3DWebExporter - bl_label = "Tidy3D Web Exporter" - + bl_label = 'Tidy3D Web Exporter' + input_sockets = { - "FDTD Sim": sockets.MaxwellFDTDSimSocketDef(), - "Cloud Task": sockets.Tidy3DCloudTaskSocketDef( + 'FDTD Sim': sockets.MaxwellFDTDSimSocketDef(), + 'Cloud Task': sockets.Tidy3DCloudTaskSocketDef( should_exist=False, ), } - + #################### # - Properties #################### lock_tree: bpy.props.BoolProperty( - name="Whether to lock the attached tree", - description="Whether or not to lock the attached tree", + name='Whether to lock the attached tree', + description='Whether or not to lock the attached tree', default=False, update=lambda self, context: self.sync_lock_tree(context), ) tracked_task_id: bpy.props.StringProperty( - name="Tracked Task ID", - description="The currently tracked task ID", - default="", + name='Tracked Task ID', + description='The currently tracked task ID', + default='', update=lambda self, context: self.sync_tracked_task_id(context), ) - + # Cache cache_total_monitor_data: bpy.props.FloatProperty( - name="(Cached) Total Monitor Data", - description="Required storage space by all monitors", + name='(Cached) Total Monitor Data', + description='Required storage space by all monitors', default=0.0, ) cache_est_cost: bpy.props.FloatProperty( - name="(Cached) Estimated Total Cost", - description="Est. Cost in FlexCompute units", + name='(Cached) Estimated Total Cost', + description='Est. Cost in FlexCompute units', default=-1.0, ) - + #################### # - Sync Methods #################### def sync_lock_tree(self, context): if self.lock_tree: - self.trigger_action("enable_lock") + self.trigger_action('enable_lock') self.locked = False for bl_socket in self.inputs: - if bl_socket.name == "FDTD Sim": continue + if bl_socket.name == 'FDTD Sim': + continue bl_socket.locked = False - + else: - self.trigger_action("disable_lock") - - self.sync_prop("lock_tree", context) + self.trigger_action('disable_lock') + + self.sync_prop('lock_tree', context) def sync_tracked_task_id(self, context): # Select Tracked Task if self.tracked_task_id: cloud_task = tdcloud.TidyCloudTasks.task(self.tracked_task_id) task_info = tdcloud.TidyCloudTasks.task_info(self.tracked_task_id) - + self.loose_output_sockets = { - "Cloud Task": sockets.Tidy3DCloudTaskSocketDef( + 'Cloud Task': sockets.Tidy3DCloudTaskSocketDef( should_exist=True, ), } - self.inputs["Cloud Task"].locked = True - + self.inputs['Cloud Task'].locked = True + # Release Tracked Task else: self.cache_est_cost = -1.0 self.loose_output_sockets = {} - self.inputs["Cloud Task"].sync_prepare_new_task() - self.inputs["Cloud Task"].locked = False - - self.sync_prop("tracked_task_id", context) - + self.inputs['Cloud Task'].sync_prepare_new_task() + self.inputs['Cloud Task'].locked = False + + self.sync_prop('tracked_task_id', context) + #################### # - Output Socket Callbacks #################### def validate_sim(self): - if (sim := self._compute_input("FDTD Sim")) is None: - msg = "Tried to validate simulation, but none is attached" + if (sim := self._compute_input('FDTD Sim')) is None: + msg = 'Tried to validate simulation, but none is attached' raise ValueError(msg) - - sim.validate_pre_upload(source_required = True) - + + sim.validate_pre_upload(source_required=True) + def upload_sim(self): - if (sim := self._compute_input("FDTD Sim")) is None: - msg = "Tried to upload simulation, but none is attached" + if (sim := self._compute_input('FDTD Sim')) is None: + msg = 'Tried to upload simulation, but none is attached' raise ValueError(msg) - + if ( - (new_task := self._compute_input("Cloud Task")) is None - or isinstance( - new_task, - tdcloud.CloudTask, - ) + new_task := self._compute_input('Cloud Task') + ) is None or isinstance( + new_task, + tdcloud.CloudTask, ): - msg = "Tried to upload simulation to new task, but existing task was selected" + msg = 'Tried to upload simulation to new task, but existing task was selected' raise ValueError(msg) - + # Create Cloud Task cloud_task = tdcloud.TidyCloudTasks.mk_task( task_name=new_task[0], @@ -257,25 +263,27 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode): upload_progress_cb=lambda uploaded_bytes: None, ## TODO: Use! verbose=True, ) - + # Declare to Cloud Task that it Exists Now ## This will change the UI to not allow free-text input. ## If the socket is linked, this errors. - self.inputs["Cloud Task"].sync_created_new_task(cloud_task) - + self.inputs['Cloud Task'].sync_created_new_task(cloud_task) + # Track the Newly Uploaded Task ID self.tracked_task_id = cloud_task.task_id - + def run_tracked_task(self): if ( cloud_task := tdcloud.TidyCloudTasks.task(self.tracked_task_id) ) is None: msg = "Tried to run tracked task, but it doesn't exist" raise RuntimeError(msg) - + cloud_task.submit() - tdcloud.TidyCloudTasks.update_task(cloud_task) ## TODO: Check that status is actually immediately updated. - + tdcloud.TidyCloudTasks.update_task( + cloud_task + ) ## TODO: Check that status is actually immediately updated. + #################### # - UI #################### @@ -284,146 +292,156 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode): row = layout.row(align=True) row.operator( UploadSimulation.bl_idname, - text="Upload", + text='Upload', ) - tree_lock_icon = "LOCKED" if self.lock_tree else "UNLOCKED" - row.prop(self, "lock_tree", toggle=True, icon=tree_lock_icon, text="") - + tree_lock_icon = 'LOCKED' if self.lock_tree else 'UNLOCKED' + row.prop(self, 'lock_tree', toggle=True, icon=tree_lock_icon, text='') + # Row: Run Sim Buttons row = layout.row(align=True) row.operator( RunSimulation.bl_idname, - text="Run", + text='Run', ) if self.tracked_task_id: - tree_lock_icon = "LOOP_BACK" + tree_lock_icon = 'LOOP_BACK' row.operator( ReleaseTrackedTask.bl_idname, - icon="LOOP_BACK", - text="", + icon='LOOP_BACK', + text='', ) - + def draw_info(self, context, layout): # Connection Info - auth_icon = "CHECKBOX_HLT" if tdcloud.IS_AUTHENTICATED else "CHECKBOX_DEHLT" - conn_icon = "CHECKBOX_HLT" if tdcloud.IS_ONLINE else "CHECKBOX_DEHLT" - + auth_icon = ( + 'CHECKBOX_HLT' if tdcloud.IS_AUTHENTICATED else 'CHECKBOX_DEHLT' + ) + conn_icon = 'CHECKBOX_HLT' if tdcloud.IS_ONLINE else 'CHECKBOX_DEHLT' + row = layout.row() - row.alignment = "CENTER" - row.label(text="Cloud Status") + row.alignment = 'CENTER' + row.label(text='Cloud Status') box = layout.box() split = box.split(factor=0.85) - + ## Split: Left Column col = split.column(align=False) - col.label(text="Authed") - col.label(text="Connected") - + col.label(text='Authed') + col.label(text='Connected') + ## Split: Right Column col = split.column(align=False) col.label(icon=auth_icon) col.label(icon=conn_icon) - - + # Simulation Info - if self.inputs["FDTD Sim"].is_linked: + if self.inputs['FDTD Sim'].is_linked: row = layout.row() - row.alignment = "CENTER" - row.label(text="Sim Info") + row.alignment = 'CENTER' + row.label(text='Sim Info') box = layout.box() split = box.split(factor=0.4) - + ## Split: Left Column col = split.column(align=False) - col.label(text="𝝨 Output") - + col.label(text='𝝨 Output') + ## Split: Right Column col = split.column(align=False) - col.alignment = "RIGHT" - col.label(text=f"{self.cache_total_monitor_data / 1_000_000:.2f}MB") - - + col.alignment = 'RIGHT' + col.label( + text=f'{self.cache_total_monitor_data / 1_000_000:.2f}MB' + ) + # Cloud Task Info if self.tracked_task_id and tdcloud.IS_AUTHENTICATED: - task_info = tdcloud.TidyCloudTasks.task_info( - self.tracked_task_id - ) - if task_info is None: return - + task_info = tdcloud.TidyCloudTasks.task_info(self.tracked_task_id) + if task_info is None: + return + ## Header row = layout.row() - row.alignment = "CENTER" - row.label(text="Task Info") - + row.alignment = 'CENTER' + row.label(text='Task Info') + ## Progress Bar row = layout.row(align=True) row.progress( factor=0.0, - type="BAR", - text=f"Status: {task_info.status.capitalize()}", + type='BAR', + text=f'Status: {task_info.status.capitalize()}', ) row.operator( ReloadTrackedTask.bl_idname, - text="", - icon="FILE_REFRESH", + text='', + icon='FILE_REFRESH', ) row.operator( EstCostTrackedTask.bl_idname, - text="", - icon="SORTTIME", + text='', + icon='SORTTIME', ) - + ## Information box = layout.box() split = box.split(factor=0.4) - + ## Split: Left Column col = split.column(align=False) - col.label(text="Status") - col.label(text="Est. Cost") - col.label(text="Real Cost") - + col.label(text='Status') + col.label(text='Est. Cost') + col.label(text='Real Cost') + ## Split: Right Column - cost_est = f"{self.cache_est_cost:.2f}" if self.cache_est_cost >= 0 else "TBD" - cost_real = f"{task_info.cost_real:.2f}" if task_info.cost_real is not None else "TBD" - + cost_est = ( + f'{self.cache_est_cost:.2f}' + if self.cache_est_cost >= 0 + else 'TBD' + ) + cost_real = ( + f'{task_info.cost_real:.2f}' + if task_info.cost_real is not None + else 'TBD' + ) + col = split.column(align=False) - col.alignment = "RIGHT" + col.alignment = 'RIGHT' col.label(text=task_info.status.capitalize()) - col.label(text=f"{cost_est} creds") - col.label(text=f"{cost_real} creds") - + col.label(text=f'{cost_est} creds') + col.label(text=f'{cost_real} creds') + # Connection Information - + #################### # - Output Methods #################### @base.computes_output_socket( - "Cloud Task", - input_sockets={"Cloud Task"}, + 'Cloud Task', + input_sockets={'Cloud Task'}, ) - def compute_cloud_task(self, input_sockets: dict) -> tdcloud.CloudTask | None: + def compute_cloud_task( + self, input_sockets: dict + ) -> tdcloud.CloudTask | None: if isinstance( - cloud_task := input_sockets["Cloud Task"], - tdcloud.CloudTask + cloud_task := input_sockets['Cloud Task'], tdcloud.CloudTask ): return cloud_task - + return None - + #################### # - Output Methods #################### @base.on_value_changed( - socket_name="FDTD Sim", - input_sockets={"FDTD Sim"}, + socket_name='FDTD Sim', + input_sockets={'FDTD Sim'}, ) def on_value_changed__fdtd_sim(self, input_sockets): - if (sim := self._compute_input("FDTD Sim")) is None: + if (sim := self._compute_input('FDTD Sim')) is None: self.cache_total_monitor_data = 0 return - - sim.validate_pre_upload(source_required = True) + + sim.validate_pre_upload(source_required=True) self.cache_total_monitor_data = sum(sim.monitors_data_size.values()) diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/viewer.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/viewer.py index 444bb43..8c3aacc 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/viewer.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/viewer.py @@ -15,8 +15,8 @@ from ...managed_objs import managed_bl_object class ConsoleViewOperator(bpy.types.Operator): - bl_idname = "blender_maxwell.console_view_operator" - bl_label = "View Plots" + bl_idname = 'blender_maxwell.console_view_operator' + bl_label = 'View Plots' @classmethod def poll(cls, context): @@ -27,9 +27,10 @@ class ConsoleViewOperator(bpy.types.Operator): node.print_data_to_console() return {'FINISHED'} + class RefreshPlotViewOperator(bpy.types.Operator): - bl_idname = "blender_maxwell.refresh_plot_view_operator" - bl_label = "Refresh Plots" + bl_idname = 'blender_maxwell.refresh_plot_view_operator' + bl_label = 'Refresh Plots' @classmethod def poll(cls, context): @@ -37,93 +38,96 @@ class RefreshPlotViewOperator(bpy.types.Operator): def execute(self, context): node = context.node - node.trigger_action("value_changed", "Data") + node.trigger_action('value_changed', 'Data') return {'FINISHED'} + #################### # - Node #################### class ViewerNode(base.MaxwellSimNode): node_type = ct.NodeType.Viewer - bl_label = "Viewer" - + bl_label = 'Viewer' + input_sockets = { - "Data": sockets.AnySocketDef(), + 'Data': sockets.AnySocketDef(), } - + #################### # - Properties #################### auto_plot: bpy.props.BoolProperty( - name="Auto-Plot", - description="Whether to auto-plot anything plugged into the viewer node", + name='Auto-Plot', + description='Whether to auto-plot anything plugged into the viewer node', default=False, - update=lambda self, context: self.sync_prop("auto_plot", context), + update=lambda self, context: self.sync_prop('auto_plot', context), ) - + auto_3d_preview: bpy.props.BoolProperty( - name="Auto 3D Preview", + name='Auto 3D Preview', description="Whether to auto-preview anything 3D, that's plugged into the viewer node", default=False, - update=lambda self, context: self.sync_prop("auto_3d_preview", context), + update=lambda self, context: self.sync_prop( + 'auto_3d_preview', context + ), ) - + #################### # - UI #################### def draw_operators(self, context, layout): split = layout.split(factor=0.4) - + # Split LHS col = split.column(align=False) - col.label(text="Console") - col.label(text="Plot") - col.label(text="3D") - + col.label(text='Console') + col.label(text='Plot') + col.label(text='3D') + # Split RHS col = split.column(align=False) - + ## Console Options - col.operator(ConsoleViewOperator.bl_idname, text="Print") - + col.operator(ConsoleViewOperator.bl_idname, text='Print') + ## Plot Options row = col.row(align=True) - row.prop(self, "auto_plot", text="Plot", toggle=True) + row.prop(self, 'auto_plot', text='Plot', toggle=True) row.operator( RefreshPlotViewOperator.bl_idname, - text="", - icon="FILE_REFRESH", + text='', + icon='FILE_REFRESH', ) - + ## 3D Preview Options row = col.row(align=True) - row.prop(self, "auto_3d_preview", text="3D Preview", toggle=True) - + row.prop(self, 'auto_3d_preview', text='3D Preview', toggle=True) + #################### # - Methods #################### def print_data_to_console(self): - if not (data := self._compute_input("Data")): + if not (data := self._compute_input('Data')): return - + if isinstance(data, sp.Basic): sp.pprint(data, use_unicode=True) - + print(str(data)) - + #################### # - Updates #################### @base.on_value_changed( - socket_name="Data", - props={"auto_3d_preview"}, + socket_name='Data', + props={'auto_3d_preview'}, ) def on_value_changed__data(self, props): # Show Plot ## Don't have to un-show other plots. if self.auto_plot: - self.trigger_action("show_plot") - + self.trigger_action('show_plot') + # Remove Anything Previewed preview_collection = managed_bl_object.bl_collection( managed_bl_object.PREVIEW_COLLECTION_NAME, @@ -131,14 +135,14 @@ class ViewerNode(base.MaxwellSimNode): ) for bl_object in preview_collection.objects.values(): preview_collection.objects.unlink(bl_object) - + # Preview Anything that Should be Previewed (maybe) - if props["auto_3d_preview"]: - self.trigger_action("show_preview") - + if props['auto_3d_preview']: + self.trigger_action('show_preview') + @base.on_value_changed( - prop_name="auto_3d_preview", - props={"auto_3d_preview"}, + prop_name='auto_3d_preview', + props={'auto_3d_preview'}, ) def on_value_changed__auto_3d_preview(self, props): # Remove Anything Previewed @@ -148,10 +152,10 @@ class ViewerNode(base.MaxwellSimNode): ) for bl_object in preview_collection.objects.values(): preview_collection.objects.unlink(bl_object) - + # Preview Anything that Should be Previewed (maybe) - if props["auto_3d_preview"]: - self.trigger_action("show_preview") + if props['auto_3d_preview']: + self.trigger_action('show_preview') #################### @@ -162,8 +166,4 @@ BL_REGISTER = [ RefreshPlotViewOperator, ViewerNode, ] -BL_NODES = { - ct.NodeType.Viewer: ( - ct.NodeCategory.MAXWELLSIM_OUTPUTS - ) -} +BL_NODES = {ct.NodeType.Viewer: (ct.NodeCategory.MAXWELLSIM_OUTPUTS)} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/__init__.py index fbb823c..95eae45 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/__init__.py @@ -1,19 +1,19 @@ from . import sim_domain -#from . import sim_grid -#from . import sim_grid_axes +# from . import sim_grid +# from . import sim_grid_axes from . import fdtd_sim BL_REGISTER = [ *sim_domain.BL_REGISTER, -# *sim_grid.BL_REGISTER, -# *sim_grid_axes.BL_REGISTER, + # *sim_grid.BL_REGISTER, + # *sim_grid_axes.BL_REGISTER, *fdtd_sim.BL_REGISTER, ] BL_NODES = { **sim_domain.BL_NODES, -# **sim_grid.BL_NODES, -# **sim_grid_axes.BL_NODES, + # **sim_grid.BL_NODES, + # **sim_grid_axes.BL_NODES, **fdtd_sim.BL_NODES, } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/fdtd_sim.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/fdtd_sim.py index e80790a..d3dd140 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/fdtd_sim.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/fdtd_sim.py @@ -6,54 +6,53 @@ from ... import contracts as ct from ... import sockets from .. import base + class FDTDSimNode(base.MaxwellSimNode): node_type = ct.NodeType.FDTDSim - bl_label = "FDTD Simulation" - + bl_label = 'FDTD Simulation' + #################### # - Sockets #################### input_sockets = { - "Domain": sockets.MaxwellSimDomainSocketDef(), - "BCs": sockets.MaxwellBoundCondsSocketDef(), - "Sources": sockets.MaxwellSourceSocketDef( + 'Domain': sockets.MaxwellSimDomainSocketDef(), + 'BCs': sockets.MaxwellBoundCondsSocketDef(), + 'Sources': sockets.MaxwellSourceSocketDef( is_list=True, ), - "Structures": sockets.MaxwellStructureSocketDef( + 'Structures': sockets.MaxwellStructureSocketDef( is_list=True, ), - "Monitors": sockets.MaxwellMonitorSocketDef( + 'Monitors': sockets.MaxwellMonitorSocketDef( is_list=True, ), } output_sockets = { - "FDTD Sim": sockets.MaxwellFDTDSimSocketDef(), + 'FDTD Sim': sockets.MaxwellFDTDSimSocketDef(), } - + #################### # - Output Socket Computation #################### @base.computes_output_socket( - "FDTD Sim", + 'FDTD Sim', kind=ct.DataFlowKind.Value, - input_sockets={ - "Sources", "Structures", "Domain", "BCs", "Monitors" - }, + input_sockets={'Sources', 'Structures', 'Domain', 'BCs', 'Monitors'}, ) def compute_fdtd_sim(self, input_sockets: dict) -> sp.Expr: - sim_domain = input_sockets["Domain"] - sources = input_sockets["Sources"] - structures = input_sockets["Structures"] - bounds = input_sockets["BCs"] - monitors = input_sockets["Monitors"] - - #if not isinstance(sources, list): - # sources = [sources] - #if not isinstance(structures, list): - # structures = [structures] - #if not isinstance(monitors, list): - # monitors = [monitors] - + sim_domain = input_sockets['Domain'] + sources = input_sockets['Sources'] + structures = input_sockets['Structures'] + bounds = input_sockets['BCs'] + monitors = input_sockets['Monitors'] + + # if not isinstance(sources, list): + # sources = [sources] + # if not isinstance(structures, list): + # structures = [structures] + # if not isinstance(monitors, list): + # monitors = [monitors] + return td.Simulation( **sim_domain, ## run_time=, size=, grid=, medium= structures=structures, @@ -62,14 +61,11 @@ class FDTDSimNode(base.MaxwellSimNode): boundary_spec=bounds, ) + #################### # - Blender Registration #################### BL_REGISTER = [ FDTDSimNode, ] -BL_NODES = { - ct.NodeType.FDTDSim: ( - ct.NodeCategory.MAXWELLSIM_SIMS - ) -} +BL_NODES = {ct.NodeType.FDTDSim: (ct.NodeCategory.MAXWELLSIM_SIMS)} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_domain.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_domain.py index 5c57d90..d33a1ac 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_domain.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_domain.py @@ -9,48 +9,51 @@ from ... import sockets from .. import base from ... import managed_objs -GEONODES_DOMAIN_BOX = "simdomain_box" +GEONODES_DOMAIN_BOX = 'simdomain_box' + class SimDomainNode(base.MaxwellSimNode): node_type = ct.NodeType.SimDomain - bl_label = "Sim Domain" - + bl_label = 'Sim Domain' + input_sockets = { - "Duration": sockets.PhysicalTimeSocketDef( - default_value = 5 * spu.ps, - default_unit = spu.ps, + 'Duration': sockets.PhysicalTimeSocketDef( + default_value=5 * spu.ps, + default_unit=spu.ps, ), - "Center": sockets.PhysicalSize3DSocketDef(), - "Size": sockets.PhysicalSize3DSocketDef(), - "Grid": sockets.MaxwellSimGridSocketDef(), - "Ambient Medium": sockets.MaxwellMediumSocketDef(), + 'Center': sockets.PhysicalSize3DSocketDef(), + 'Size': sockets.PhysicalSize3DSocketDef(), + 'Grid': sockets.MaxwellSimGridSocketDef(), + 'Ambient Medium': sockets.MaxwellMediumSocketDef(), } output_sockets = { - "Domain": sockets.MaxwellSimDomainSocketDef(), + 'Domain': sockets.MaxwellSimDomainSocketDef(), } - + managed_obj_defs = { - "domain_box": ct.schemas.ManagedObjDef( + 'domain_box': ct.schemas.ManagedObjDef( mk=lambda name: managed_objs.ManagedBLObject(name), - name_prefix="", + name_prefix='', ) } - + #################### # - Callbacks #################### @base.computes_output_socket( - "Domain", - input_sockets={"Duration", "Center", "Size", "Grid", "Ambient Medium"}, + 'Domain', + input_sockets={'Duration', 'Center', 'Size', 'Grid', 'Ambient Medium'}, ) def compute_sim_domain(self, input_sockets: dict) -> sp.Expr: - if all([ - (_duration := input_sockets["Duration"]), - (_center := input_sockets["Center"]), - (_size := input_sockets["Size"]), - (grid := input_sockets["Grid"]), - (medium := input_sockets["Ambient Medium"]), - ]): + if all( + [ + (_duration := input_sockets['Duration']), + (_center := input_sockets['Center']), + (_size := input_sockets['Size']), + (grid := input_sockets['Grid']), + (medium := input_sockets['Ambient Medium']), + ] + ): duration = spu.convert_to(_duration, spu.second) / spu.second center = tuple(spu.convert_to(_center, spu.um) / spu.um) size = tuple(spu.convert_to(_size, spu.um) / spu.um) @@ -61,72 +64,67 @@ class SimDomainNode(base.MaxwellSimNode): grid_spec=grid, medium=medium, ) - + #################### # - Preview #################### @base.on_value_changed( - socket_name={"Center", "Size"}, - input_sockets={"Center", "Size"}, - managed_objs={"domain_box"}, + socket_name={'Center', 'Size'}, + input_sockets={'Center', 'Size'}, + managed_objs={'domain_box'}, ) def on_value_changed__center_size( self, input_sockets: dict, managed_objs: dict[str, ct.schemas.ManagedObj], ): - _center = input_sockets["Center"] - center = tuple([ - float(el) - for el in spu.convert_to(_center, spu.um) / spu.um - ]) - - _size = input_sockets["Size"] - size = tuple([ - float(el) - for el in spu.convert_to(_size, spu.um) / spu.um - ]) + _center = input_sockets['Center'] + center = tuple( + [float(el) for el in spu.convert_to(_center, spu.um) / spu.um] + ) + + _size = input_sockets['Size'] + size = tuple( + [float(el) for el in spu.convert_to(_size, spu.um) / spu.um] + ) ## TODO: Preview unit system?? Presume um for now - + # Retrieve Hard-Coded GeoNodes and Analyze Input geo_nodes = bpy.data.node_groups[GEONODES_DOMAIN_BOX] geonodes_interface = analyze_geonodes.interface( - geo_nodes, direc="INPUT" + geo_nodes, direc='INPUT' ) - + # Sync Modifier Inputs - managed_objs["domain_box"].sync_geonodes_modifier( + managed_objs['domain_box'].sync_geonodes_modifier( geonodes_node_group=geo_nodes, geonodes_identifier_to_value={ - geonodes_interface["Size"].identifier: size, + geonodes_interface['Size'].identifier: size, ## TODO: Use 'bl_socket_map.value_to_bl`! ## - This accounts for auto-conversion, unit systems, etc. . ## - We could keep it in the node base class... ## - ...But it needs aligning with Blender, too. Hmm. - } + }, ) - + # Sync Object Position - managed_objs["domain_box"].bl_object("MESH").location = center - + managed_objs['domain_box'].bl_object('MESH').location = center + @base.on_show_preview( - managed_objs={"domain_box"}, + managed_objs={'domain_box'}, ) def on_show_preview( self, managed_objs: dict[str, ct.schemas.ManagedObj], ): - managed_objs["domain_box"].show_preview("MESH") + managed_objs['domain_box'].show_preview('MESH') self.on_value_changed__center_size() + #################### # - Blender Registration #################### BL_REGISTER = [ SimDomainNode, ] -BL_NODES = { - ct.NodeType.SimDomain: ( - ct.NodeCategory.MAXWELLSIM_SIMS - ) -} +BL_NODES = {ct.NodeType.SimDomain: (ct.NodeCategory.MAXWELLSIM_SIMS)} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_grid_axes/array_sim_grid_axis.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_grid_axes/array_sim_grid_axis.py index 8f4a665..41fac16 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_grid_axes/array_sim_grid_axis.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_grid_axes/array_sim_grid_axis.py @@ -3,4 +3,3 @@ #################### BL_REGISTER = [] BL_NODES = {} - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_grid_axes/automatic_sim_grid_axis.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_grid_axes/automatic_sim_grid_axis.py index 8f4a665..41fac16 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_grid_axes/automatic_sim_grid_axis.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_grid_axes/automatic_sim_grid_axis.py @@ -3,4 +3,3 @@ #################### BL_REGISTER = [] BL_NODES = {} - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_grid_axes/manual_sim_grid_axis.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_grid_axes/manual_sim_grid_axis.py index 8f4a665..41fac16 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_grid_axes/manual_sim_grid_axis.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_grid_axes/manual_sim_grid_axis.py @@ -3,4 +3,3 @@ #################### BL_REGISTER = [] BL_NODES = {} - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_grid_axes/uniform_sim_grid_axis.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_grid_axes/uniform_sim_grid_axis.py index 8f4a665..41fac16 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_grid_axes/uniform_sim_grid_axis.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/sim_grid_axes/uniform_sim_grid_axis.py @@ -3,4 +3,3 @@ #################### BL_REGISTER = [] BL_NODES = {} - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/__init__.py index 0555c9f..350c6cb 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/__init__.py @@ -1,27 +1,28 @@ from . import temporal_shapes from . import point_dipole_source -#from . import uniform_current_source + +# from . import uniform_current_source from . import plane_wave_source -#from . import gaussian_beam_source -#from . import astigmatic_gaussian_beam_source -#from . import tfsf_source +# from . import gaussian_beam_source +# from . import astigmatic_gaussian_beam_source +# from . import tfsf_source BL_REGISTER = [ *temporal_shapes.BL_REGISTER, *point_dipole_source.BL_REGISTER, -# *uniform_current_source.BL_REGISTER, + # *uniform_current_source.BL_REGISTER, *plane_wave_source.BL_REGISTER, -# *gaussian_beam_source.BL_REGISTER, -# *astigmatic_gaussian_beam_source.BL_REGISTER, -# *tfsf_source.BL_REGISTER, + # *gaussian_beam_source.BL_REGISTER, + # *astigmatic_gaussian_beam_source.BL_REGISTER, + # *tfsf_source.BL_REGISTER, ] BL_NODES = { **temporal_shapes.BL_NODES, **point_dipole_source.BL_NODES, -# **uniform_current_source.BL_NODES, + # **uniform_current_source.BL_NODES, **plane_wave_source.BL_NODES, -# **gaussian_beam_source.BL_NODES, -# **astigmatic_gaussian_beam_source.BL_NODES, -# **tfsf_source.BL_NODES, + # **gaussian_beam_source.BL_NODES, + # **astigmatic_gaussian_beam_source.BL_NODES, + # **tfsf_source.BL_NODES, } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/astigmatic_gaussian_beam_source.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/astigmatic_gaussian_beam_source.py index 8f4a665..41fac16 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/astigmatic_gaussian_beam_source.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/astigmatic_gaussian_beam_source.py @@ -3,4 +3,3 @@ #################### BL_REGISTER = [] BL_NODES = {} - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/eh_equivalence_source.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/eh_equivalence_source.py index 8f4a665..41fac16 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/eh_equivalence_source.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/eh_equivalence_source.py @@ -3,4 +3,3 @@ #################### BL_REGISTER = [] BL_NODES = {} - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/eh_source.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/eh_source.py index 8f4a665..41fac16 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/eh_source.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/eh_source.py @@ -3,4 +3,3 @@ #################### BL_REGISTER = [] BL_NODES = {} - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/gaussian_beam_source.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/gaussian_beam_source.py index 8f4a665..41fac16 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/gaussian_beam_source.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/gaussian_beam_source.py @@ -3,4 +3,3 @@ #################### BL_REGISTER = [] BL_NODES = {} - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/plane_wave_source.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/plane_wave_source.py index fc37fa2..b8af116 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/plane_wave_source.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/plane_wave_source.py @@ -13,88 +13,89 @@ from ... import contracts as ct from ... import sockets from .. import base -GEONODES_PLANE_WAVE = "source_plane_wave" +GEONODES_PLANE_WAVE = 'source_plane_wave' + def convert_vector_to_spherical( v: sp.MatrixBase, ) -> tuple[str, str, sp.Expr, sp.Expr]: """Converts a vector (maybe normalized) to spherical coordinates from an arbitrary choice of injection axis. - + Injection axis is chosen to minimize `theta` """ x, y, z = v - + injection_axis = max( - ('x', abs(x)), - ('y', abs(y)), - ('z', abs(z)), - key=lambda item: item[1] + ('x', abs(x)), ('y', abs(y)), ('z', abs(z)), key=lambda item: item[1] )[0] ## Select injection axis that minimizes 'theta' - - if injection_axis == "x": - direction = "+" if x >= 0 else "-" + + if injection_axis == 'x': + direction = '+' if x >= 0 else '-' theta = sp.acos(x / sp.sqrt(x**2 + y**2 + z**2)) phi = sp.atan2(z, y) - elif injection_axis == "y": - direction = "+" if y >= 0 else "-" + elif injection_axis == 'y': + direction = '+' if y >= 0 else '-' theta = sp.acos(y / sp.sqrt(x**2 + y**2 + z**2)) phi = sp.atan2(x, z) else: - direction = "+" if z >= 0 else "-" + direction = '+' if z >= 0 else '-' theta = sp.acos(z / sp.sqrt(x**2 + y**2 + z**2)) phi = sp.atan2(y, x) - + return injection_axis, direction, theta, phi + class PlaneWaveSourceNode(base.MaxwellSimNode): node_type = ct.NodeType.PlaneWaveSource - bl_label = "Plane Wave Source" - + bl_label = 'Plane Wave Source' + #################### # - Sockets #################### input_sockets = { - "Temporal Shape": sockets.MaxwellTemporalShapeSocketDef(), - "Center": sockets.PhysicalPoint3DSocketDef(), - "Direction": sockets.Real3DVectorSocketDef( + 'Temporal Shape': sockets.MaxwellTemporalShapeSocketDef(), + 'Center': sockets.PhysicalPoint3DSocketDef(), + 'Direction': sockets.Real3DVectorSocketDef( default_value=sp.Matrix([0, 0, -1]) ), - "Pol Angle": sockets.PhysicalAngleSocketDef(), + 'Pol Angle': sockets.PhysicalAngleSocketDef(), } output_sockets = { - "Source": sockets.MaxwellSourceSocketDef(), + 'Source': sockets.MaxwellSourceSocketDef(), } - + managed_obj_defs = { - "plane_wave_source": ct.schemas.ManagedObjDef( + 'plane_wave_source': ct.schemas.ManagedObjDef( mk=lambda name: managed_objs.ManagedBLObject(name), - name_prefix="", + name_prefix='', ) } - + #################### # - Output Socket Computation #################### @base.computes_output_socket( - "Source", - input_sockets={"Temporal Shape", "Center", "Direction", "Pol Angle"}, + 'Source', + input_sockets={'Temporal Shape', 'Center', 'Direction', 'Pol Angle'}, ) def compute_source(self, input_sockets: dict): - temporal_shape = input_sockets["Temporal Shape"] - _center = input_sockets["Center"] - direction = input_sockets["Direction"] - pol_angle = input_sockets["Pol Angle"] - - injection_axis, dir_sgn, theta, phi = convert_vector_to_spherical(direction) - + temporal_shape = input_sockets['Temporal Shape'] + _center = input_sockets['Center'] + direction = input_sockets['Direction'] + pol_angle = input_sockets['Pol Angle'] + + injection_axis, dir_sgn, theta, phi = convert_vector_to_spherical( + direction + ) + size = { - "x": (0, math.inf, math.inf), - "y": (math.inf, 0, math.inf), - "z": (math.inf, math.inf, 0), + 'x': (0, math.inf, math.inf), + 'y': (math.inf, 0, math.inf), + 'z': (math.inf, math.inf, 0), }[injection_axis] center = tuple(spu.convert_to(_center, spu.um) / spu.um) - + # Display the results return td.PlaneWave( center=center, @@ -105,74 +106,65 @@ class PlaneWaveSourceNode(base.MaxwellSimNode): angle_phi=phi, pol_angle=pol_angle, ) - + #################### # - Preview #################### @base.on_value_changed( - socket_name={"Center", "Direction"}, - input_sockets={"Center", "Direction"}, - managed_objs={"plane_wave_source"}, + socket_name={'Center', 'Direction'}, + input_sockets={'Center', 'Direction'}, + managed_objs={'plane_wave_source'}, ) def on_value_changed__center_direction( self, input_sockets: dict, managed_objs: dict[str, ct.schemas.ManagedObj], ): - _center = input_sockets["Center"] - center = tuple([ - float(el) - for el in spu.convert_to(_center, spu.um) / spu.um - ]) - - _direction = input_sockets["Direction"] - direction = tuple([ - float(el) - for el in _direction - ]) + _center = input_sockets['Center'] + center = tuple( + [float(el) for el in spu.convert_to(_center, spu.um) / spu.um] + ) + + _direction = input_sockets['Direction'] + direction = tuple([float(el) for el in _direction]) ## TODO: Preview unit system?? Presume um for now - + # Retrieve Hard-Coded GeoNodes and Analyze Input geo_nodes = bpy.data.node_groups[GEONODES_PLANE_WAVE] geonodes_interface = analyze_geonodes.interface( - geo_nodes, direc="INPUT" + geo_nodes, direc='INPUT' ) - + # Sync Modifier Inputs - managed_objs["plane_wave_source"].sync_geonodes_modifier( + managed_objs['plane_wave_source'].sync_geonodes_modifier( geonodes_node_group=geo_nodes, geonodes_identifier_to_value={ - geonodes_interface["Direction"].identifier: direction, + geonodes_interface['Direction'].identifier: direction, ## TODO: Use 'bl_socket_map.value_to_bl`! ## - This accounts for auto-conversion, unit systems, etc. . ## - We could keep it in the node base class... ## - ...But it needs aligning with Blender, too. Hmm. - } + }, ) - + # Sync Object Position - managed_objs["plane_wave_source"].bl_object("MESH").location = center - + managed_objs['plane_wave_source'].bl_object('MESH').location = center + @base.on_show_preview( - managed_objs={"plane_wave_source"}, + managed_objs={'plane_wave_source'}, ) def on_show_preview( self, managed_objs: dict[str, ct.schemas.ManagedObj], ): - managed_objs["plane_wave_source"].show_preview("MESH") + managed_objs['plane_wave_source'].show_preview('MESH') self.on_value_changed__center_direction() - #################### # - Blender Registration #################### BL_REGISTER = [ PlaneWaveSourceNode, ] -BL_NODES = { - ct.NodeType.PlaneWaveSource: ( - ct.NodeCategory.MAXWELLSIM_SOURCES - ) -} +BL_NODES = {ct.NodeType.PlaneWaveSource: (ct.NodeCategory.MAXWELLSIM_SOURCES)} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/point_dipole_source.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/point_dipole_source.py index ff6db4f..a2ca1f8 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/point_dipole_source.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/point_dipole_source.py @@ -10,79 +10,82 @@ from ... import sockets from .. import base from ... import managed_objs + class PointDipoleSourceNode(base.MaxwellSimNode): node_type = ct.NodeType.PointDipoleSource - bl_label = "Point Dipole Source" - + bl_label = 'Point Dipole Source' + #################### # - Sockets #################### input_sockets = { - "Temporal Shape": sockets.MaxwellTemporalShapeSocketDef(), - "Center": sockets.PhysicalPoint3DSocketDef(), - "Interpolate": sockets.BoolSocketDef( + 'Temporal Shape': sockets.MaxwellTemporalShapeSocketDef(), + 'Center': sockets.PhysicalPoint3DSocketDef(), + 'Interpolate': sockets.BoolSocketDef( default_value=True, ), } output_sockets = { - "Source": sockets.MaxwellSourceSocketDef(), + 'Source': sockets.MaxwellSourceSocketDef(), } - + managed_obj_defs = { - "sphere_empty": ct.schemas.ManagedObjDef( + 'sphere_empty': ct.schemas.ManagedObjDef( mk=lambda name: managed_objs.ManagedBLObject(name), - name_prefix="", + name_prefix='', ) } - + #################### # - Properties #################### pol_axis: bpy.props.EnumProperty( - name="Polarization Axis", - description="Polarization Axis", + name='Polarization Axis', + description='Polarization Axis', items=[ - ("EX", "Ex", "Electric field in x-dir"), - ("EY", "Ey", "Electric field in y-dir"), - ("EZ", "Ez", "Electric field in z-dir"), + ('EX', 'Ex', 'Electric field in x-dir'), + ('EY', 'Ey', 'Electric field in y-dir'), + ('EZ', 'Ez', 'Electric field in z-dir'), ], - default="EX", - update=(lambda self, context: self.sync_prop("pol_axis", context)), + default='EX', + update=(lambda self, context: self.sync_prop('pol_axis', context)), ) - + #################### # - UI #################### def draw_props(self, context, layout): split = layout.split(factor=0.6) - + col = split.column() - col.label(text="Pol Axis") - + col.label(text='Pol Axis') + col = split.column() - col.prop(self, "pol_axis", text="") - + col.prop(self, 'pol_axis', text='') + #################### # - Output Socket Computation #################### @base.computes_output_socket( - "Source", - input_sockets={"Temporal Shape", "Center", "Interpolate"}, - props={"pol_axis"}, + 'Source', + input_sockets={'Temporal Shape', 'Center', 'Interpolate'}, + props={'pol_axis'}, ) - def compute_source(self, input_sockets: dict[str, typ.Any], props: dict[str, typ.Any]) -> td.PointDipole: + def compute_source( + self, input_sockets: dict[str, typ.Any], props: dict[str, typ.Any] + ) -> td.PointDipole: pol_axis = { - "EX": "Ex", - "EY": "Ey", - "EZ": "Ez", - }[props["pol_axis"]] - - temporal_shape = input_sockets["Temporal Shape"] - _center = input_sockets["Center"] - interpolate = input_sockets["Interpolate"] - + 'EX': 'Ex', + 'EY': 'Ey', + 'EZ': 'Ez', + }[props['pol_axis']] + + temporal_shape = input_sockets['Temporal Shape'] + _center = input_sockets['Center'] + interpolate = input_sockets['Interpolate'] + center = tuple(spu.convert_to(_center, spu.um) / spu.um) - + _res = td.PointDipole( center=center, source_time=temporal_shape, @@ -90,41 +93,42 @@ class PointDipoleSourceNode(base.MaxwellSimNode): polarization=pol_axis, ) return _res - + #################### # - Preview #################### @base.on_value_changed( - socket_name="Center", - input_sockets={"Center"}, - managed_objs={"sphere_empty"}, + socket_name='Center', + input_sockets={'Center'}, + managed_objs={'sphere_empty'}, ) def on_value_changed__center( self, input_sockets: dict, managed_objs: dict[str, ct.schemas.ManagedObj], ): - _center = input_sockets["Center"] + _center = input_sockets['Center'] center = tuple(spu.convert_to(_center, spu.um) / spu.um) ## TODO: Preview unit system?? Presume um for now - - mobj = managed_objs["sphere_empty"] - bl_object = mobj.bl_object("EMPTY") - bl_object.location = center #tuple([float(el) for el in center]) - + + mobj = managed_objs['sphere_empty'] + bl_object = mobj.bl_object('EMPTY') + bl_object.location = center # tuple([float(el) for el in center]) + @base.on_show_preview( - managed_objs={"sphere_empty"}, + managed_objs={'sphere_empty'}, ) def on_show_preview( self, managed_objs: dict[str, ct.schemas.ManagedObj], ): - managed_objs["sphere_empty"].show_preview( - "EMPTY", - empty_display_type="SPHERE", + managed_objs['sphere_empty'].show_preview( + 'EMPTY', + empty_display_type='SPHERE', ) - managed_objs["sphere_empty"].bl_object("EMPTY").empty_display_size = 0.2 - + managed_objs['sphere_empty'].bl_object( + 'EMPTY' + ).empty_display_size = 0.2 #################### @@ -134,7 +138,5 @@ BL_REGISTER = [ PointDipoleSourceNode, ] BL_NODES = { - ct.NodeType.PointDipoleSource: ( - ct.NodeCategory.MAXWELLSIM_SOURCES - ) + ct.NodeType.PointDipoleSource: (ct.NodeCategory.MAXWELLSIM_SOURCES) } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/__init__.py index 9618aa1..d541c4a 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/__init__.py @@ -1,14 +1,14 @@ from . import gaussian_pulse_temporal_shape -#from . import continuous_wave_temporal_shape -#from . import array_temporal_shape +# from . import continuous_wave_temporal_shape +# from . import array_temporal_shape BL_REGISTER = [ *gaussian_pulse_temporal_shape.BL_REGISTER, -# *continuous_wave_temporal_shape.BL_REGISTER, -# *array_temporal_shape.BL_REGISTER, + # *continuous_wave_temporal_shape.BL_REGISTER, + # *array_temporal_shape.BL_REGISTER, ] BL_NODES = { **gaussian_pulse_temporal_shape.BL_NODES, -# **continuous_wave_temporal_shape.BL_NODES, -# **array_temporal_shape.BL_NODES, + # **continuous_wave_temporal_shape.BL_NODES, + # **array_temporal_shape.BL_NODES, } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/array_temporal_shape.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/array_temporal_shape.py index 8f4a665..41fac16 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/array_temporal_shape.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/array_temporal_shape.py @@ -3,4 +3,3 @@ #################### BL_REGISTER = [] BL_NODES = {} - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/continuous_wave_temporal_shape.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/continuous_wave_temporal_shape.py index 3547597..4456e06 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/continuous_wave_temporal_shape.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/continuous_wave_temporal_shape.py @@ -6,54 +6,55 @@ from .... import contracts from .... import sockets from ... import base + class ContinuousWaveTemporalShapeNode(base.MaxwellSimTreeNode): node_type = contracts.NodeType.ContinuousWaveTemporalShape - - bl_label = "Continuous Wave Temporal Shape" - #bl_icon = ... - + + bl_label = 'Continuous Wave Temporal Shape' + # bl_icon = ... + #################### # - Sockets #################### input_sockets = { - #"amplitude": sockets.RealNumberSocketDef( - # label="Temporal Shape", - #), ## Should have a unit of some kind... - "phase": sockets.PhysicalAngleSocketDef( - label="Phase", + # "amplitude": sockets.RealNumberSocketDef( + # label="Temporal Shape", + # ), ## Should have a unit of some kind... + 'phase': sockets.PhysicalAngleSocketDef( + label='Phase', ), - "freq_center": sockets.PhysicalFreqSocketDef( - label="Freq Center", + 'freq_center': sockets.PhysicalFreqSocketDef( + label='Freq Center', ), - "freq_std": sockets.PhysicalFreqSocketDef( - label="Freq STD", + 'freq_std': sockets.PhysicalFreqSocketDef( + label='Freq STD', ), - "time_delay_rel_ang_freq": sockets.RealNumberSocketDef( - label="Time Delay rel. Ang. Freq", + 'time_delay_rel_ang_freq': sockets.RealNumberSocketDef( + label='Time Delay rel. Ang. Freq', default_value=5.0, ), } output_sockets = { - "temporal_shape": sockets.MaxwellTemporalShapeSocketDef( - label="Temporal Shape", + 'temporal_shape': sockets.MaxwellTemporalShapeSocketDef( + label='Temporal Shape', ), } - + #################### # - Output Socket Computation #################### - @base.computes_output_socket("temporal_shape") + @base.computes_output_socket('temporal_shape') def compute_source(self: contracts.NodeTypeProtocol) -> td.PointDipole: - _phase = self.compute_input("phase") - _freq_center = self.compute_input("freq_center") - _freq_std = self.compute_input("freq_std") - time_delay_rel_ang_freq = self.compute_input("time_delay_rel_ang_freq") - + _phase = self.compute_input('phase') + _freq_center = self.compute_input('freq_center') + _freq_std = self.compute_input('freq_std') + time_delay_rel_ang_freq = self.compute_input('time_delay_rel_ang_freq') + cheating_amplitude = 1.0 phase = spu.convert_to(_phase, spu.radian) / spu.radian freq_center = spu.convert_to(_freq_center, spu.hertz) / spu.hertz freq_std = spu.convert_to(_freq_std, spu.hertz) / spu.hertz - + return td.ContinuousWave( amplitude=cheating_amplitude, phase=phase, @@ -63,7 +64,6 @@ class ContinuousWaveTemporalShapeNode(base.MaxwellSimTreeNode): ) - #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/gaussian_pulse_temporal_shape.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/gaussian_pulse_temporal_shape.py index b41ccd0..a5753d9 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/gaussian_pulse_temporal_shape.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/gaussian_pulse_temporal_shape.py @@ -13,98 +13,107 @@ from .... import sockets from .... import managed_objs from ... import base + class GaussianPulseTemporalShapeNode(base.MaxwellSimNode): node_type = ct.NodeType.GaussianPulseTemporalShape - bl_label = "Gaussian Pulse Temporal Shape" - #bl_icon = ... - + bl_label = 'Gaussian Pulse Temporal Shape' + # bl_icon = ... + #################### # - Sockets #################### input_sockets = { - #"amplitude": sockets.RealNumberSocketDef( - # label="Temporal Shape", - #), ## Should have a unit of some kind... - "Freq Center": sockets.PhysicalFreqSocketDef( + # "amplitude": sockets.RealNumberSocketDef( + # label="Temporal Shape", + # ), ## Should have a unit of some kind... + 'Freq Center': sockets.PhysicalFreqSocketDef( default_value=500 * spuex.terahertz, ), - "Freq Std.": sockets.PhysicalFreqSocketDef( + 'Freq Std.': sockets.PhysicalFreqSocketDef( default_value=200 * spuex.terahertz, ), - "Phase": sockets.PhysicalAngleSocketDef(), - "Delay rel. AngFreq": sockets.RealNumberSocketDef( + 'Phase': sockets.PhysicalAngleSocketDef(), + 'Delay rel. AngFreq': sockets.RealNumberSocketDef( default_value=5.0, ), - "Remove DC": sockets.BoolSocketDef( + 'Remove DC': sockets.BoolSocketDef( default_value=True, ), } output_sockets = { - "Temporal Shape": sockets.MaxwellTemporalShapeSocketDef(), + 'Temporal Shape': sockets.MaxwellTemporalShapeSocketDef(), } - + managed_obj_defs = { - "amp_time": ct.schemas.ManagedObjDef( + 'amp_time': ct.schemas.ManagedObjDef( mk=lambda name: managed_objs.ManagedBLImage(name), - name_prefix="amp_time_", + name_prefix='amp_time_', ) } - + #################### # - Properties #################### plot_time_start: bpy.props.FloatProperty( - name="Plot Time Start (ps)", - description="The instance ID of a particular MaxwellSimNode instance, used to index caches", + name='Plot Time Start (ps)', + description='The instance ID of a particular MaxwellSimNode instance, used to index caches', default=0.0, - update=(lambda self, context: self.sync_prop("plot_time_start", context)), + update=( + lambda self, context: self.sync_prop('plot_time_start', context) + ), ) plot_time_end: bpy.props.FloatProperty( - name="Plot Time End (ps)", - description="The instance ID of a particular MaxwellSimNode instance, used to index caches", + name='Plot Time End (ps)', + description='The instance ID of a particular MaxwellSimNode instance, used to index caches', default=5, - update=(lambda self, context: self.sync_prop("plot_time_start", context)), + update=( + lambda self, context: self.sync_prop('plot_time_start', context) + ), ) - + #################### # - UI #################### def draw_props(self, context, layout): - layout.label(text="Plot Settings") + layout.label(text='Plot Settings') split = layout.split(factor=0.6) - + col = split.column() - col.label(text="t-Range (ps)") - + col.label(text='t-Range (ps)') + col = split.column() - col.prop(self, "plot_time_start", text="") - col.prop(self, "plot_time_end", text="") - + col.prop(self, 'plot_time_start', text='') + col.prop(self, 'plot_time_end', text='') + #################### # - Output Socket Computation #################### @base.computes_output_socket( - "Temporal Shape", + 'Temporal Shape', input_sockets={ - "Freq Center", "Freq Std.", "Phase", "Delay rel. AngFreq", - "Remove DC", - } + 'Freq Center', + 'Freq Std.', + 'Phase', + 'Delay rel. AngFreq', + 'Remove DC', + }, ) def compute_source(self, input_sockets: dict) -> td.GaussianPulse: if ( - (_freq_center := input_sockets["Freq Center"]) is None - or (_freq_std := input_sockets["Freq Std."]) is None - or (_phase := input_sockets["Phase"]) is None - or (time_delay_rel_ang_freq := input_sockets["Delay rel. AngFreq"]) is None - or (remove_dc_component := input_sockets["Remove DC"]) is None + (_freq_center := input_sockets['Freq Center']) is None + or (_freq_std := input_sockets['Freq Std.']) is None + or (_phase := input_sockets['Phase']) is None + or (time_delay_rel_ang_freq := input_sockets['Delay rel. AngFreq']) + is None + or (remove_dc_component := input_sockets['Remove DC']) is None ): - raise ValueError("Inputs not defined") - + raise ValueError('Inputs not defined') + cheating_amplitude = 1.0 freq_center = spu.convert_to(_freq_center, spu.hertz) / spu.hertz freq_std = spu.convert_to(_freq_std, spu.hertz) / spu.hertz phase = spu.convert_to(_phase, spu.radian) / spu.radian - + return td.GaussianPulse( amplitude=cheating_amplitude, phase=phase, @@ -113,11 +122,11 @@ class GaussianPulseTemporalShapeNode(base.MaxwellSimNode): offset=time_delay_rel_ang_freq, remove_dc_component=remove_dc_component, ) - + @base.on_show_plot( - managed_objs={"amp_time"}, - props={"plot_time_start", "plot_time_end"}, - output_sockets={"Temporal Shape"}, + managed_objs={'amp_time'}, + props={'plot_time_start', 'plot_time_end'}, + output_sockets={'Temporal Shape'}, stop_propagation=True, ) def on_show_plot( @@ -126,19 +135,18 @@ class GaussianPulseTemporalShapeNode(base.MaxwellSimNode): output_sockets: dict[str, typ.Any], props: dict[str, typ.Any], ): - temporal_shape = output_sockets["Temporal Shape"] - plot_time_start = props["plot_time_start"] * 1e-15 - plot_time_end = props["plot_time_end"] * 1e-15 - + temporal_shape = output_sockets['Temporal Shape'] + plot_time_start = props['plot_time_start'] * 1e-15 + plot_time_end = props['plot_time_end'] * 1e-15 + times = np.linspace(plot_time_start, plot_time_end) - - managed_objs["amp_time"].mpl_plot_to_image( + + managed_objs['amp_time'].mpl_plot_to_image( lambda ax: temporal_shape.plot_spectrum(times, ax=ax), bl_select=True, ) - #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/uniform_current_source.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/uniform_current_source.py index 8f4a665..41fac16 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/uniform_current_source.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/uniform_current_source.py @@ -3,4 +3,3 @@ #################### BL_REGISTER = [] BL_NODES = {} - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/__init__.py index 910d751..f0ee4e5 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/__init__.py @@ -1,17 +1,15 @@ -#from . import object_structure +# from . import object_structure from . import geonodes_structure from . import primitives BL_REGISTER = [ -# *object_structure.BL_REGISTER, + # *object_structure.BL_REGISTER, *geonodes_structure.BL_REGISTER, - *primitives.BL_REGISTER, ] BL_NODES = { -# **object_structure.BL_NODES, + # **object_structure.BL_NODES, **geonodes_structure.BL_NODES, - **primitives.BL_NODES, } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/geonodes_structure.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/geonodes_structure.py index ac3e199..3197bea 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/geonodes_structure.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/geonodes_structure.py @@ -16,36 +16,37 @@ from ... import sockets from .. import base from ... import managed_objs + class GeoNodesStructureNode(base.MaxwellSimNode): node_type = ct.NodeType.GeoNodesStructure - bl_label = "GeoNodes Structure" - + bl_label = 'GeoNodes Structure' + #################### # - Sockets #################### input_sockets = { - "Unit System": sockets.PhysicalUnitSystemSocketDef(), - "Medium": sockets.MaxwellMediumSocketDef(), - "GeoNodes": sockets.BlenderGeoNodesSocketDef(), + 'Unit System': sockets.PhysicalUnitSystemSocketDef(), + 'Medium': sockets.MaxwellMediumSocketDef(), + 'GeoNodes': sockets.BlenderGeoNodesSocketDef(), } output_sockets = { - "Structure": sockets.MaxwellStructureSocketDef(), + 'Structure': sockets.MaxwellStructureSocketDef(), } - + managed_obj_defs = { - "geometry": ct.schemas.ManagedObjDef( + 'geometry': ct.schemas.ManagedObjDef( mk=lambda name: managed_objs.ManagedBLObject(name), - name_prefix="", + name_prefix='', ) } - + #################### # - Output Socket Computation #################### @base.computes_output_socket( - "Structure", - input_sockets={"Medium"}, - managed_objs={"geometry"}, + 'Structure', + input_sockets={'Medium'}, + managed_objs={'geometry'}, ) def compute_structure( self, @@ -53,28 +54,27 @@ class GeoNodesStructureNode(base.MaxwellSimNode): managed_objs: dict[str, ct.schemas.ManagedObj], ) -> td.Structure: # Extract the Managed Blender Object - mobj = managed_objs["geometry"] - + mobj = managed_objs['geometry'] + # Extract Geometry as Arrays geometry_as_arrays = mobj.mesh_as_arrays - + # Return TriMesh Structure return td.Structure( geometry=td.TriangleMesh.from_vertices_faces( - geometry_as_arrays["verts"], - geometry_as_arrays["faces"], + geometry_as_arrays['verts'], + geometry_as_arrays['faces'], ), - medium=input_sockets["Medium"], + medium=input_sockets['Medium'], ) - + #################### # - Event Methods #################### @base.on_value_changed( - socket_name="GeoNodes", - - managed_objs={"geometry"}, - input_sockets={"GeoNodes"}, + socket_name='GeoNodes', + managed_objs={'geometry'}, + input_sockets={'GeoNodes'}, ) def on_value_changed__geonodes( self, @@ -82,20 +82,20 @@ class GeoNodesStructureNode(base.MaxwellSimNode): input_sockets: dict[str, typ.Any], ) -> None: """Called whenever the GeoNodes socket is changed. - + Refreshes the Loose Input Sockets, which map directly to the GeoNodes tree input sockets. """ - if not (geo_nodes := input_sockets["GeoNodes"]): - managed_objs["geometry"].free() + if not (geo_nodes := input_sockets['GeoNodes']): + managed_objs['geometry'].free() self.loose_input_sockets = {} return - + # Analyze GeoNodes ## Extract Valid Inputs (via GeoNodes Tree "Interface") geonodes_interface = analyze_geonodes.interface( - geo_nodes, direc="INPUT" + geo_nodes, direc='INPUT' ) - + # Set Loose Input Sockets ## Retrieve the appropriate SocketDef for the Blender Interface Socket self.loose_input_sockets = { @@ -104,21 +104,20 @@ class GeoNodesStructureNode(base.MaxwellSimNode): )() ## === SocketDef(), but with dynamic SocketDef for socket_name, bl_interface_socket in geonodes_interface.items() } - + ## Set Loose `socket.value` from Interface `default_value` for socket_name in self.loose_input_sockets: socket = self.inputs[socket_name] bl_interface_socket = geonodes_interface[socket_name] - + socket.value = bl_socket_map.value_from_bl(bl_interface_socket) - + ## Implicitly triggers the loose-input `on_value_changed` for each. - + @base.on_value_changed( any_loose_input_socket=True, - - managed_objs={"geometry"}, - input_sockets={"Unit System", "GeoNodes"}, + managed_objs={'geometry'}, + input_sockets={'Unit System', 'GeoNodes'}, ) def on_value_changed__loose_inputs( self, @@ -127,26 +126,27 @@ class GeoNodesStructureNode(base.MaxwellSimNode): loose_input_sockets: dict[str, typ.Any], ): """Called whenever a Loose Input Socket is altered. - + Synchronizes the change to the actual GeoNodes modifier, so that the change is immediately visible. """ # Retrieve Data - unit_system = input_sockets["Unit System"] - mobj = managed_objs["geometry"] - - if not (geo_nodes := input_sockets["GeoNodes"]): return - + unit_system = input_sockets['Unit System'] + mobj = managed_objs['geometry'] + + if not (geo_nodes := input_sockets['GeoNodes']): + return + # Analyze GeoNodes Interface (input direction) ## This retrieves NodeTreeSocketInterface elements geonodes_interface = analyze_geonodes.interface( - geo_nodes, direc="INPUT" + geo_nodes, direc='INPUT' ) - + ## TODO: Check that Loose Sockets matches the Interface ## - If the user deletes an interface socket, bad things will happen. ## - We will try to set an identifier that doesn't exist! ## - Instead, this should update the loose input sockets. - + ## Push Values to the GeoNodes Modifier mobj.sync_geonodes_modifier( geonodes_node_group=geo_nodes, @@ -159,24 +159,24 @@ class GeoNodesStructureNode(base.MaxwellSimNode): for socket_name, bl_interface_socket in ( geonodes_interface.items() ) - } + }, ) - + #################### # - Event Methods #################### @base.on_show_preview( - managed_objs={"geometry"}, + managed_objs={'geometry'}, ) def on_show_preview( self, managed_objs: dict[str, ct.schemas.ManagedObj], ): """Called whenever a Loose Input Socket is altered. - + Synchronizes the change to the actual GeoNodes modifier, so that the change is immediately visible. """ - managed_objs["geometry"].show_preview("MESH") + managed_objs['geometry'].show_preview('MESH') #################### @@ -186,7 +186,5 @@ BL_REGISTER = [ GeoNodesStructureNode, ] BL_NODES = { - ct.NodeType.GeoNodesStructure: ( - ct.NodeCategory.MAXWELLSIM_STRUCTURES - ) + ct.NodeType.GeoNodesStructure: (ct.NodeCategory.MAXWELLSIM_STRUCTURES) } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/object_structure.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/object_structure.py index e02fa35..ea6d4c8 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/object_structure.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/object_structure.py @@ -10,39 +10,40 @@ from ... import contracts from ... import sockets from .. import base + class ObjectStructureNode(base.MaxwellSimTreeNode): node_type = contracts.NodeType.ObjectStructure - bl_label = "Object Structure" - #bl_icon = ... - + bl_label = 'Object Structure' + # bl_icon = ... + #################### # - Sockets #################### input_sockets = { - "medium": sockets.MaxwellMediumSocketDef( - label="Medium", + 'medium': sockets.MaxwellMediumSocketDef( + label='Medium', ), - "object": sockets.BlenderObjectSocketDef( - label="Object", + 'object': sockets.BlenderObjectSocketDef( + label='Object', ), } output_sockets = { - "structure": sockets.MaxwellStructureSocketDef( - label="Structure", + 'structure': sockets.MaxwellStructureSocketDef( + label='Structure', ), } - + #################### # - Output Socket Computation #################### - @base.computes_output_socket("structure") + @base.computes_output_socket('structure') def compute_structure(self: contracts.NodeTypeProtocol) -> td.Structure: # Extract the Blender Object - bl_object = self.compute_input("object") - + bl_object = self.compute_input('object') + # Ensure Updated Geometry bpy.context.view_layer.update() - + # Triangulate Object Mesh bmesh_mesh = bmesh.new() bmesh_mesh.from_object( @@ -50,26 +51,24 @@ class ObjectStructureNode(base.MaxwellSimTreeNode): bpy.context.evaluated_depsgraph_get(), ) bmesh.ops.triangulate(bmesh_mesh, faces=bmesh_mesh.faces) - - mesh = bpy.data.meshes.new(name="TriangulatedMesh") + + mesh = bpy.data.meshes.new(name='TriangulatedMesh') bmesh_mesh.to_mesh(mesh) bmesh_mesh.free() - + # Extract Vertices and Faces vertices = np.array([vert.co for vert in mesh.vertices]) - faces = np.array([ - [vert for vert in poly.vertices] - for poly in mesh.polygons - ]) - - # Remove Temporary Mesh - bpy.data.meshes.remove(mesh) - - return td.Structure( - geometry=td.TriangleMesh.from_vertices_faces(vertices, faces), - medium=self.compute_input("medium") + faces = np.array( + [[vert for vert in poly.vertices] for poly in mesh.polygons] ) + # Remove Temporary Mesh + bpy.data.meshes.remove(mesh) + + return td.Structure( + geometry=td.TriangleMesh.from_vertices_faces(vertices, faces), + medium=self.compute_input('medium'), + ) #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/__init__.py index 9bba897..be3aa33 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/__init__.py @@ -1,14 +1,15 @@ from . import box_structure -#from . import cylinder_structure + +# from . import cylinder_structure from . import sphere_structure BL_REGISTER = [ *box_structure.BL_REGISTER, -# *cylinder_structure.BL_REGISTER, + # *cylinder_structure.BL_REGISTER, *sphere_structure.BL_REGISTER, ] BL_NODES = { **box_structure.BL_NODES, -# **cylinder_structure.BL_NODES, + # **cylinder_structure.BL_NODES, **sphere_structure.BL_NODES, } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/box_structure.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/box_structure.py index ba31c02..2bf4d81 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/box_structure.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/box_structure.py @@ -10,48 +10,49 @@ from .... import sockets from .... import managed_objs from ... import base -GEONODES_STRUCTURE_BOX = "structure_box" +GEONODES_STRUCTURE_BOX = 'structure_box' + class BoxStructureNode(base.MaxwellSimNode): node_type = ct.NodeType.BoxStructure - bl_label = "Box Structure" - + bl_label = 'Box Structure' + #################### # - Sockets #################### input_sockets = { - "Medium": sockets.MaxwellMediumSocketDef(), - "Center": sockets.PhysicalPoint3DSocketDef(), - "Size": sockets.PhysicalSize3DSocketDef( + 'Medium': sockets.MaxwellMediumSocketDef(), + 'Center': sockets.PhysicalPoint3DSocketDef(), + 'Size': sockets.PhysicalSize3DSocketDef( default_value=sp.Matrix([500, 500, 500]) * spu.nm ), } output_sockets = { - "Structure": sockets.MaxwellStructureSocketDef(), + 'Structure': sockets.MaxwellStructureSocketDef(), } - + managed_obj_defs = { - "structure_box": ct.schemas.ManagedObjDef( + 'structure_box': ct.schemas.ManagedObjDef( mk=lambda name: managed_objs.ManagedBLObject(name), - name_prefix="", + name_prefix='', ) } - + #################### # - Output Socket Computation #################### @base.computes_output_socket( - "Structure", - input_sockets={"Medium", "Center", "Size"}, + 'Structure', + input_sockets={'Medium', 'Center', 'Size'}, ) def compute_simulation(self, input_sockets: dict) -> td.Box: - medium = input_sockets["Medium"] - _center = input_sockets["Center"] - _size = input_sockets["Size"] - + medium = input_sockets['Medium'] + _center = input_sockets['Center'] + _size = input_sockets['Size'] + center = tuple(spu.convert_to(_center, spu.um) / spu.um) size = tuple(spu.convert_to(_size, spu.um) / spu.um) - + return td.Structure( geometry=td.Box( center=center, @@ -59,69 +60,66 @@ class BoxStructureNode(base.MaxwellSimNode): ), medium=medium, ) - + #################### # - Preview - Changes to Input Sockets #################### @base.on_value_changed( - socket_name={"Center", "Size"}, - input_sockets={"Center", "Size"}, - managed_objs={"structure_box"}, + socket_name={'Center', 'Size'}, + input_sockets={'Center', 'Size'}, + managed_objs={'structure_box'}, ) def on_value_changed__center_size( self, input_sockets: dict, managed_objs: dict[str, ct.schemas.ManagedObj], ): - _center = input_sockets["Center"] - center = tuple([ - float(el) - for el in spu.convert_to(_center, spu.um) / spu.um - ]) - - _size = input_sockets["Size"] - size = tuple([ - float(el) - for el in spu.convert_to(_size, spu.um) / spu.um - ]) + _center = input_sockets['Center'] + center = tuple( + [float(el) for el in spu.convert_to(_center, spu.um) / spu.um] + ) + + _size = input_sockets['Size'] + size = tuple( + [float(el) for el in spu.convert_to(_size, spu.um) / spu.um] + ) ## TODO: Preview unit system?? Presume um for now - + # Retrieve Hard-Coded GeoNodes and Analyze Input geo_nodes = bpy.data.node_groups[GEONODES_STRUCTURE_BOX] geonodes_interface = analyze_geonodes.interface( - geo_nodes, direc="INPUT" + geo_nodes, direc='INPUT' ) - + # Sync Modifier Inputs - managed_objs["structure_box"].sync_geonodes_modifier( + managed_objs['structure_box'].sync_geonodes_modifier( geonodes_node_group=geo_nodes, geonodes_identifier_to_value={ - geonodes_interface["Size"].identifier: size, + geonodes_interface['Size'].identifier: size, ## TODO: Use 'bl_socket_map.value_to_bl`! ## - This accounts for auto-conversion, unit systems, etc. . ## - We could keep it in the node base class... ## - ...But it needs aligning with Blender, too. Hmm. - } + }, ) - + # Sync Object Position - managed_objs["structure_box"].bl_object("MESH").location = center - + managed_objs['structure_box'].bl_object('MESH').location = center + #################### # - Preview - Show Preview #################### @base.on_show_preview( - managed_objs={"structure_box"}, + managed_objs={'structure_box'}, ) def on_show_preview( self, managed_objs: dict[str, ct.schemas.ManagedObj], ): - managed_objs["structure_box"].show_preview("MESH") + managed_objs['structure_box'].show_preview('MESH') self.on_value_changed__center_size() - #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/cylinder_structure.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/cylinder_structure.py index f5ce682..cafe18f 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/cylinder_structure.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/cylinder_structure.py @@ -6,48 +6,49 @@ from .... import contracts from .... import sockets from ... import base + class CylinderStructureNode(base.MaxwellSimTreeNode): node_type = contracts.NodeType.CylinderStructure - bl_label = "Cylinder Structure" - #bl_icon = ... - + bl_label = 'Cylinder Structure' + # bl_icon = ... + #################### # - Sockets #################### input_sockets = { - "medium": sockets.MaxwellMediumSocketDef( - label="Medium", + 'medium': sockets.MaxwellMediumSocketDef( + label='Medium', ), - "center": sockets.PhysicalPoint3DSocketDef( - label="Center", + 'center': sockets.PhysicalPoint3DSocketDef( + label='Center', ), - "radius": sockets.PhysicalLengthSocketDef( - label="Radius", + 'radius': sockets.PhysicalLengthSocketDef( + label='Radius', ), - "height": sockets.PhysicalLengthSocketDef( - label="Height", + 'height': sockets.PhysicalLengthSocketDef( + label='Height', ), } output_sockets = { - "structure": sockets.MaxwellStructureSocketDef( - label="Structure", + 'structure': sockets.MaxwellStructureSocketDef( + label='Structure', ), } - + #################### # - Output Socket Computation #################### - @base.computes_output_socket("structure") + @base.computes_output_socket('structure') def compute_simulation(self: contracts.NodeTypeProtocol) -> td.Box: - medium = self.compute_input("medium") - _center = self.compute_input("center") - _radius = self.compute_input("radius") - _height = self.compute_input("height") - + medium = self.compute_input('medium') + _center = self.compute_input('center') + _radius = self.compute_input('radius') + _height = self.compute_input('height') + center = tuple(spu.convert_to(_center, spu.um) / spu.um) radius = spu.convert_to(_radius, spu.um) / spu.um height = spu.convert_to(_height, spu.um) / spu.um - + return td.Structure( geometry=td.Cylinder( radius=radius, @@ -58,7 +59,6 @@ class CylinderStructureNode(base.MaxwellSimTreeNode): ) - #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/sphere_structure.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/sphere_structure.py index 9d4b66c..5179510 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/sphere_structure.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/sphere_structure.py @@ -10,48 +10,49 @@ from .... import sockets from .... import managed_objs from ... import base -GEONODES_STRUCTURE_SPHERE = "structure_sphere" +GEONODES_STRUCTURE_SPHERE = 'structure_sphere' + class SphereStructureNode(base.MaxwellSimNode): node_type = ct.NodeType.SphereStructure - bl_label = "Sphere Structure" - + bl_label = 'Sphere Structure' + #################### # - Sockets #################### input_sockets = { - "Center": sockets.PhysicalPoint3DSocketDef(), - "Radius": sockets.PhysicalLengthSocketDef( - default_value=150*spu.nm, + 'Center': sockets.PhysicalPoint3DSocketDef(), + 'Radius': sockets.PhysicalLengthSocketDef( + default_value=150 * spu.nm, ), - "Medium": sockets.MaxwellMediumSocketDef(), + 'Medium': sockets.MaxwellMediumSocketDef(), } output_sockets = { - "Structure": sockets.MaxwellStructureSocketDef(), + 'Structure': sockets.MaxwellStructureSocketDef(), } - + managed_obj_defs = { - "structure_sphere": ct.schemas.ManagedObjDef( + 'structure_sphere': ct.schemas.ManagedObjDef( mk=lambda name: managed_objs.ManagedBLObject(name), - name_prefix="", + name_prefix='', ) } - + #################### # - Output Socket Computation #################### @base.computes_output_socket( - "Structure", - input_sockets={"Center", "Radius", "Medium"}, + 'Structure', + input_sockets={'Center', 'Radius', 'Medium'}, ) def compute_structure(self, input_sockets: dict) -> td.Box: - medium = input_sockets["Medium"] - _center = input_sockets["Center"] - _radius = input_sockets["Radius"] - + medium = input_sockets['Medium'] + _center = input_sockets['Center'] + _radius = input_sockets['Radius'] + center = tuple(spu.convert_to(_center, spu.um) / spu.um) radius = spu.convert_to(_radius, spu.um) / spu.um - + return td.Structure( geometry=td.Sphere( radius=radius, @@ -59,66 +60,64 @@ class SphereStructureNode(base.MaxwellSimNode): ), medium=medium, ) - + #################### # - Preview - Changes to Input Sockets #################### @base.on_value_changed( - socket_name={"Center", "Radius"}, - input_sockets={"Center", "Radius"}, - managed_objs={"structure_sphere"}, + socket_name={'Center', 'Radius'}, + input_sockets={'Center', 'Radius'}, + managed_objs={'structure_sphere'}, ) def on_value_changed__center_radius( self, input_sockets: dict, managed_objs: dict[str, ct.schemas.ManagedObj], ): - _center = input_sockets["Center"] - center = tuple([ - float(el) - for el in spu.convert_to(_center, spu.um) / spu.um - ]) - - _radius = input_sockets["Radius"] + _center = input_sockets['Center'] + center = tuple( + [float(el) for el in spu.convert_to(_center, spu.um) / spu.um] + ) + + _radius = input_sockets['Radius'] radius = float(spu.convert_to(_radius, spu.um) / spu.um) ## TODO: Preview unit system?? Presume um for now - + # Retrieve Hard-Coded GeoNodes and Analyze Input geo_nodes = bpy.data.node_groups[GEONODES_STRUCTURE_SPHERE] geonodes_interface = analyze_geonodes.interface( - geo_nodes, direc="INPUT" + geo_nodes, direc='INPUT' ) - + # Sync Modifier Inputs - managed_objs["structure_sphere"].sync_geonodes_modifier( + managed_objs['structure_sphere'].sync_geonodes_modifier( geonodes_node_group=geo_nodes, geonodes_identifier_to_value={ - geonodes_interface["Radius"].identifier: radius, + geonodes_interface['Radius'].identifier: radius, ## TODO: Use 'bl_socket_map.value_to_bl`! ## - This accounts for auto-conversion, unit systems, etc. . ## - We could keep it in the node base class... ## - ...But it needs aligning with Blender, too. Hmm. - } + }, ) - + # Sync Object Position - managed_objs["structure_sphere"].bl_object("MESH").location = center - + managed_objs['structure_sphere'].bl_object('MESH').location = center + #################### # - Preview - Show Preview #################### @base.on_show_preview( - managed_objs={"structure_sphere"}, + managed_objs={'structure_sphere'}, ) def on_show_preview( self, managed_objs: dict[str, ct.schemas.ManagedObj], ): - managed_objs["structure_sphere"].show_preview("MESH") + managed_objs['structure_sphere'].show_preview('MESH') self.on_value_changed__center_radius() - #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/scripted_structure.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/scripted_structure.py index 8f4a665..41fac16 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/scripted_structure.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/scripted_structure.py @@ -3,4 +3,3 @@ #################### BL_REGISTER = [] BL_NODES = {} - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/__init__.py index aec6a94..4ab0af1 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/__init__.py @@ -1,16 +1,14 @@ -#from . import math +# from . import math from . import combine -#from . import separate +# from . import separate BL_REGISTER = [ -# *math.BL_REGISTER, - + # *math.BL_REGISTER, *combine.BL_REGISTER, - #*separate.BL_REGISTER, + # *separate.BL_REGISTER, ] BL_NODES = { -# **math.BL_NODES, - + # **math.BL_NODES, **combine.BL_NODES, - #**separate.BL_NODES, + # **separate.BL_NODES, } diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/combine.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/combine.py index b31587c..f8d03c3 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/combine.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/combine.py @@ -10,152 +10,143 @@ from .. import base MAX_AMOUNT = 20 + class CombineNode(base.MaxwellSimNode): node_type = ct.NodeType.Combine - bl_label = "Combine" - #bl_icon = ... - + bl_label = 'Combine' + # bl_icon = ... + #################### # - Sockets #################### input_socket_sets = { - "Maxwell Sources": {}, - "Maxwell Structures": {}, - "Maxwell Monitors": {}, - "Real 3D Vector": { - f"x_{i}": sockets.RealNumberSocketDef() - for i in range(3) + 'Maxwell Sources': {}, + 'Maxwell Structures': {}, + 'Maxwell Monitors': {}, + 'Real 3D Vector': { + f'x_{i}': sockets.RealNumberSocketDef() for i in range(3) }, - #"Point 3D": { - # axis: sockets.PhysicalLengthSocketDef() - # for i, axis in zip( - # range(3), - # ["x", "y", "z"] - # ) - #}, - #"Size 3D": { - # axis_key: sockets.PhysicalLengthSocketDef() - # for i, axis_key, axis_label in zip( - # range(3), - # ["x_size", "y_size", "z_size"], - # ["X Size", "Y Size", "Z Size"], - # ) - #}, + # "Point 3D": { + # axis: sockets.PhysicalLengthSocketDef() + # for i, axis in zip( + # range(3), + # ["x", "y", "z"] + # ) + # }, + # "Size 3D": { + # axis_key: sockets.PhysicalLengthSocketDef() + # for i, axis_key, axis_label in zip( + # range(3), + # ["x_size", "y_size", "z_size"], + # ["X Size", "Y Size", "Z Size"], + # ) + # }, } output_socket_sets = { - "Maxwell Sources": { - "Sources": sockets.MaxwellSourceSocketDef( + 'Maxwell Sources': { + 'Sources': sockets.MaxwellSourceSocketDef( is_list=True, ), }, - "Maxwell Structures": { - "Structures": sockets.MaxwellStructureSocketDef( + 'Maxwell Structures': { + 'Structures': sockets.MaxwellStructureSocketDef( is_list=True, ), }, - "Maxwell Monitors": { - "Monitors": sockets.MaxwellMonitorSocketDef( + 'Maxwell Monitors': { + 'Monitors': sockets.MaxwellMonitorSocketDef( is_list=True, ), }, - "Real 3D Vector": { - "Real 3D Vector": sockets.Real3DVectorSocketDef(), + 'Real 3D Vector': { + 'Real 3D Vector': sockets.Real3DVectorSocketDef(), }, - #"Point 3D": { - # "3D Point": sockets.PhysicalPoint3DSocketDef(), - #}, - #"Size 3D": { - # "3D Size": sockets.PhysicalSize3DSocketDef(), - #}, + # "Point 3D": { + # "3D Point": sockets.PhysicalPoint3DSocketDef(), + # }, + # "Size 3D": { + # "3D Size": sockets.PhysicalSize3DSocketDef(), + # }, } - + amount: bpy.props.IntProperty( - name="# Objects to Combine", - description="Amount of Objects to Combine", + name='# Objects to Combine', + description='Amount of Objects to Combine', default=1, min=1, max=MAX_AMOUNT, - update=lambda self, context: self.sync_prop("amount", context) + update=lambda self, context: self.sync_prop('amount', context), ) - + #################### # - Draw #################### def draw_props(self, context, layout): - layout.prop(self, "amount", text="#") - + layout.prop(self, 'amount', text='#') + #################### # - Output Socket Computation #################### @base.computes_output_socket( - "Real 3D Vector", - input_sockets={"x_0", "x_1", "x_2"} + 'Real 3D Vector', input_sockets={'x_0', 'x_1', 'x_2'} ) def compute_real_3d_vector(self, input_sockets) -> sp.Expr: - return sp.Matrix([input_sockets[f"x_{i}"] for i in range(3)]) - + return sp.Matrix([input_sockets[f'x_{i}'] for i in range(3)]) + @base.computes_output_socket( - "Sources", - input_sockets={f"Source #{i}" for i in range(MAX_AMOUNT)}, - props={"amount"}, + 'Sources', + input_sockets={f'Source #{i}' for i in range(MAX_AMOUNT)}, + props={'amount'}, ) def compute_sources(self, input_sockets, props) -> sp.Expr: - return [ - input_sockets[f"Source #{i}"] - for i in range(props["amount"]) - ] - + return [input_sockets[f'Source #{i}'] for i in range(props['amount'])] + @base.computes_output_socket( - "Structures", - input_sockets={f"Structure #{i}" for i in range(MAX_AMOUNT)}, - props={"amount"}, + 'Structures', + input_sockets={f'Structure #{i}' for i in range(MAX_AMOUNT)}, + props={'amount'}, ) def compute_structures(self, input_sockets, props) -> sp.Expr: return [ - input_sockets[f"Structure #{i}"] - for i in range(props["amount"]) + input_sockets[f'Structure #{i}'] for i in range(props['amount']) ] - + @base.computes_output_socket( - "Monitors", - input_sockets={f"Monitor #{i}" for i in range(MAX_AMOUNT)}, - props={"amount"}, + 'Monitors', + input_sockets={f'Monitor #{i}' for i in range(MAX_AMOUNT)}, + props={'amount'}, ) def compute_monitors(self, input_sockets, props) -> sp.Expr: - return [ - input_sockets[f"Monitor #{i}"] - for i in range(props["amount"]) - ] - - + return [input_sockets[f'Monitor #{i}'] for i in range(props['amount'])] + #################### # - Input Socket Compilation #################### @base.on_value_changed( - prop_name="active_socket_set", - props={"active_socket_set", "amount"}, + prop_name='active_socket_set', + props={'active_socket_set', 'amount'}, ) def on_value_changed__active_socket_set(self, props): - if props["active_socket_set"] == "Maxwell Sources": + if props['active_socket_set'] == 'Maxwell Sources': self.loose_input_sockets = { - f"Source #{i}": sockets.MaxwellSourceSocketDef() - for i in range(props["amount"]) + f'Source #{i}': sockets.MaxwellSourceSocketDef() + for i in range(props['amount']) } - elif props["active_socket_set"] == "Maxwell Structures": + elif props['active_socket_set'] == 'Maxwell Structures': self.loose_input_sockets = { - f"Structure #{i}": sockets.MaxwellStructureSocketDef() - for i in range(props["amount"]) + f'Structure #{i}': sockets.MaxwellStructureSocketDef() + for i in range(props['amount']) } - elif props["active_socket_set"] == "Maxwell Monitors": + elif props['active_socket_set'] == 'Maxwell Monitors': self.loose_input_sockets = { - f"Monitor #{i}": sockets.MaxwellMonitorSocketDef() - for i in range(props["amount"]) + f'Monitor #{i}': sockets.MaxwellMonitorSocketDef() + for i in range(props['amount']) } else: self.loose_input_sockets = {} - + @base.on_value_changed( - prop_name="amount", + prop_name='amount', ) def on_value_changed__amount(self): self.on_value_changed__active_socket_set() @@ -171,8 +162,4 @@ class CombineNode(base.MaxwellSimNode): BL_REGISTER = [ CombineNode, ] -BL_NODES = { - ct.NodeType.Combine: ( - ct.NodeCategory.MAXWELLSIM_UTILITIES - ) -} +BL_NODES = {ct.NodeType.Combine: (ct.NodeCategory.MAXWELLSIM_UTILITIES)} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/separate.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/separate.py index 8db2d15..02197e5 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/separate.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/separate.py @@ -7,68 +7,72 @@ from .... import contracts from .... import sockets from ... import base + class WaveConverterNode(base.MaxwellSimTreeNode): node_type = contracts.NodeType.WaveConverter - bl_label = "Wave Converter" - #bl_icon = ... - + bl_label = 'Wave Converter' + # bl_icon = ... + #################### # - Sockets #################### input_sockets = {} input_socket_sets = { - "freq_to_vacwl": { - "freq": sockets.PhysicalFreqSocketDef( - label="Freq", + 'freq_to_vacwl': { + 'freq': sockets.PhysicalFreqSocketDef( + label='Freq', ), }, - "vacwl_to_freq": { - "vacwl": sockets.PhysicalVacWLSocketDef( - label="Vac WL", + 'vacwl_to_freq': { + 'vacwl': sockets.PhysicalVacWLSocketDef( + label='Vac WL', ), }, } output_sockets = {} output_socket_sets = { - "freq_to_vacwl": { - "vacwl": sockets.PhysicalVacWLSocketDef( - label="Vac WL", + 'freq_to_vacwl': { + 'vacwl': sockets.PhysicalVacWLSocketDef( + label='Vac WL', ), }, - "vacwl_to_freq": { - "freq": sockets.PhysicalFreqSocketDef( - label="Freq", + 'vacwl_to_freq': { + 'freq': sockets.PhysicalFreqSocketDef( + label='Freq', ), }, } - + #################### # - Output Socket Computation #################### - @base.computes_output_socket("freq") + @base.computes_output_socket('freq') def compute_freq(self: contracts.NodeTypeProtocol) -> sp.Expr: - vac_speed_of_light = sc.constants.speed_of_light * spu.meter/spu.second - - vacwl = self.compute_input("vacwl") - + vac_speed_of_light = ( + sc.constants.speed_of_light * spu.meter / spu.second + ) + + vacwl = self.compute_input('vacwl') + return spu.convert_to( vac_speed_of_light / vacwl, spu.hertz, ) - - @base.computes_output_socket("vacwl") + + @base.computes_output_socket('vacwl') def compute_vacwl(self: contracts.NodeTypeProtocol) -> sp.Expr: - vac_speed_of_light = sc.constants.speed_of_light * spu.meter/spu.second - - freq = self.compute_input("freq") - + vac_speed_of_light = ( + sc.constants.speed_of_light * spu.meter / spu.second + ) + + freq = self.compute_input('freq') + return spu.convert_to( vac_speed_of_light / freq, spu.meter, ) - #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/viz/sim_data_viz.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/viz/sim_data_viz.py index 7a1efe9..4c6313c 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/viz/sim_data_viz.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/viz/sim_data_viz.py @@ -16,177 +16,197 @@ from ... import managed_objs CACHE = {} + class FDTDSimDataVizNode(base.MaxwellSimNode): node_type = ct.NodeType.FDTDSimDataViz - bl_label = "FDTD Sim Data Viz" - + bl_label = 'FDTD Sim Data Viz' + #################### # - Sockets #################### input_sockets = { - "FDTD Sim Data": sockets.MaxwellFDTDSimDataSocketDef(), + 'FDTD Sim Data': sockets.MaxwellFDTDSimDataSocketDef(), } - output_sockets= { - "Preview": sockets.AnySocketDef() - } - + output_sockets = {'Preview': sockets.AnySocketDef()} + managed_obj_defs = { - "viz_plot": ct.schemas.ManagedObjDef( + 'viz_plot': ct.schemas.ManagedObjDef( mk=lambda name: managed_objs.ManagedBLImage(name), - name_prefix="", + name_prefix='', ), - "viz_object": ct.schemas.ManagedObjDef( + 'viz_object': ct.schemas.ManagedObjDef( mk=lambda name: managed_objs.ManagedBLObject(name), - name_prefix="", + name_prefix='', ), } - + #################### # - Properties #################### viz_monitor_name: bpy.props.EnumProperty( - name="Viz Monitor Name", - description="Monitor to visualize within the attached SimData", + name='Viz Monitor Name', + description='Monitor to visualize within the attached SimData', items=lambda self, context: self.retrieve_monitors(context), update=(lambda self, context: self.sync_viz_monitor_name(context)), ) cache_viz_monitor_type: bpy.props.StringProperty( - name="Viz Monitor Type", - description="Type of the viz monitor", - default="" + name='Viz Monitor Type', + description='Type of the viz monitor', + default='', ) - + # Field Monitor Type field_viz_component: bpy.props.EnumProperty( - name="Field Component", - description="Field component to visualize", + name='Field Component', + description='Field component to visualize', items=[ - ("E", "E", "Electric"), - #("H", "H", "Magnetic"), - #("S", "S", "Poynting"), - ("Ex", "Ex", "Ex"), - ("Ey", "Ey", "Ey"), - ("Ez", "Ez", "Ez"), - #("Hx", "Hx", "Hx"), - #("Hy", "Hy", "Hy"), - #("Hz", "Hz", "Hz"), + ('E', 'E', 'Electric'), + # ("H", "H", "Magnetic"), + # ("S", "S", "Poynting"), + ('Ex', 'Ex', 'Ex'), + ('Ey', 'Ey', 'Ey'), + ('Ez', 'Ez', 'Ez'), + # ("Hx", "Hx", "Hx"), + # ("Hy", "Hy", "Hy"), + # ("Hz", "Hz", "Hz"), ], - default="E", - update=lambda self, context: self.sync_prop("field_viz_component", context), + default='E', + update=lambda self, context: self.sync_prop( + 'field_viz_component', context + ), ) field_viz_part: bpy.props.EnumProperty( - name="Field Part", - description="Field part to visualize", + name='Field Part', + description='Field part to visualize', items=[ - ("real", "Real", "Electric"), - ("imag", "Imaginary", "Imaginary"), - ("abs", "Abs", "Abs"), - ("abs^2", "Squared Abs", "Square Abs"), - ("phase", "Phase", "Phase"), + ('real', 'Real', 'Electric'), + ('imag', 'Imaginary', 'Imaginary'), + ('abs', 'Abs', 'Abs'), + ('abs^2', 'Squared Abs', 'Square Abs'), + ('phase', 'Phase', 'Phase'), ], - default="real", - update=lambda self, context: self.sync_prop("field_viz_part", context), + default='real', + update=lambda self, context: self.sync_prop('field_viz_part', context), ) field_viz_scale: bpy.props.EnumProperty( - name="Field Scale", - description="Field scale to visualize in, Linear or Log", + name='Field Scale', + description='Field scale to visualize in, Linear or Log', items=[ - ("lin", "Linear", "Linear Scale"), - ("dB", "Log (dB)", "Logarithmic (dB) Scale"), + ('lin', 'Linear', 'Linear Scale'), + ('dB', 'Log (dB)', 'Logarithmic (dB) Scale'), ], - default="lin", - update=lambda self, context: self.sync_prop("field_viz_scale", context), + default='lin', + update=lambda self, context: self.sync_prop( + 'field_viz_scale', context + ), ) field_viz_structure_visibility: bpy.props.FloatProperty( - name="Field Viz Plot: Structure Visibility", - description="Visibility of structes", + name='Field Viz Plot: Structure Visibility', + description='Visibility of structes', default=0.2, min=0.0, max=1.0, - update=lambda self, context: self.sync_prop("field_viz_plot_fixed_f", context), + update=lambda self, context: self.sync_prop( + 'field_viz_plot_fixed_f', context + ), ) - + field_viz_plot_fix_x: bpy.props.BoolProperty( - name="Field Viz Plot: Fix X", - description="Fix the x-coordinate on the plot", + name='Field Viz Plot: Fix X', + description='Fix the x-coordinate on the plot', default=False, - update=lambda self, context: self.sync_prop("field_viz_plot_fix_x", context), + update=lambda self, context: self.sync_prop( + 'field_viz_plot_fix_x', context + ), ) field_viz_plot_fix_y: bpy.props.BoolProperty( - name="Field Viz Plot: Fix Y", - description="Fix the y coordinate on the plot", + name='Field Viz Plot: Fix Y', + description='Fix the y coordinate on the plot', default=False, - update=lambda self, context: self.sync_prop("field_viz_plot_fix_y", context), + update=lambda self, context: self.sync_prop( + 'field_viz_plot_fix_y', context + ), ) field_viz_plot_fix_z: bpy.props.BoolProperty( - name="Field Viz Plot: Fix Z", - description="Fix the z coordinate on the plot", + name='Field Viz Plot: Fix Z', + description='Fix the z coordinate on the plot', default=False, - update=lambda self, context: self.sync_prop("field_viz_plot_fix_z", context), + update=lambda self, context: self.sync_prop( + 'field_viz_plot_fix_z', context + ), ) field_viz_plot_fix_f: bpy.props.BoolProperty( - name="Field Viz Plot: Fix Freq", - description="Fix the frequency coordinate on the plot", + name='Field Viz Plot: Fix Freq', + description='Fix the frequency coordinate on the plot', default=False, - update=lambda self, context: self.sync_prop("field_viz_plot_fix_f", context), + update=lambda self, context: self.sync_prop( + 'field_viz_plot_fix_f', context + ), ) - + field_viz_plot_fixed_x: bpy.props.FloatProperty( - name="Field Viz Plot: Fix X", - description="Fix the x-coordinate on the plot", + name='Field Viz Plot: Fix X', + description='Fix the x-coordinate on the plot', default=0.0, - update=lambda self, context: self.sync_prop("field_viz_plot_fixed_x", context), + update=lambda self, context: self.sync_prop( + 'field_viz_plot_fixed_x', context + ), ) field_viz_plot_fixed_y: bpy.props.FloatProperty( - name="Field Viz Plot: Fixed Y", - description="Fix the y coordinate on the plot", + name='Field Viz Plot: Fixed Y', + description='Fix the y coordinate on the plot', default=0.0, - update=lambda self, context: self.sync_prop("field_viz_plot_fixed_y", context), + update=lambda self, context: self.sync_prop( + 'field_viz_plot_fixed_y', context + ), ) field_viz_plot_fixed_z: bpy.props.FloatProperty( - name="Field Viz Plot: Fixed Z", - description="Fix the z coordinate on the plot", + name='Field Viz Plot: Fixed Z', + description='Fix the z coordinate on the plot', default=0.0, - update=lambda self, context: self.sync_prop("field_viz_plot_fixed_z", context), + update=lambda self, context: self.sync_prop( + 'field_viz_plot_fixed_z', context + ), ) field_viz_plot_fixed_f: bpy.props.FloatProperty( - name="Field Viz Plot: Fixed Freq (Thz)", - description="Fix the frequency coordinate on the plot", + name='Field Viz Plot: Fixed Freq (Thz)', + description='Fix the frequency coordinate on the plot', default=0.0, - update=lambda self, context: self.sync_prop("field_viz_plot_fixed_f", context), + update=lambda self, context: self.sync_prop( + 'field_viz_plot_fixed_f', context + ), ) - - + #################### # - Derived Properties #################### def sync_viz_monitor_name(self, context): - if (sim_data := self._compute_input("FDTD Sim Data")) is None: + if (sim_data := self._compute_input('FDTD Sim Data')) is None: return - + self.cache_viz_monitor_type = sim_data.monitor_data[ self.viz_monitor_name ].type - self.sync_prop("viz_monitor_name", context) - + self.sync_prop('viz_monitor_name', context) + def retrieve_monitors(self, context) -> list[tuple]: global CACHE if not CACHE.get(self.instance_id): - sim_data = self._compute_input("FDTD Sim Data") - + sim_data = self._compute_input('FDTD Sim Data') + if sim_data is not None: - CACHE[self.instance_id] = {"monitors": list( - sim_data.monitor_data.keys() - )} + CACHE[self.instance_id] = { + 'monitors': list(sim_data.monitor_data.keys()) + } else: - return [("NONE", "None", "No monitors")] - - monitor_names = CACHE[self.instance_id]["monitors"] - + return [('NONE', 'None', 'No monitors')] + + monitor_names = CACHE[self.instance_id]['monitors'] + # Check for No Monitors if not monitor_names: - return [("NONE", "None", "No monitors")] - + return [('NONE', 'None', 'No monitors')] + return [ ( monitor_name, @@ -195,48 +215,47 @@ class FDTDSimDataVizNode(base.MaxwellSimNode): ) for monitor_name in monitor_names ] - + #################### # - UI #################### def draw_props(self, context, layout): row = layout.row() - row.prop(self, "viz_monitor_name", text="") - if self.cache_viz_monitor_type == "FieldData": + row.prop(self, 'viz_monitor_name', text='') + if self.cache_viz_monitor_type == 'FieldData': # Array Selection split = layout.split(factor=0.45) col = split.column(align=False) - col.label(text="Component") - col.label(text="Part") - col.label(text="Scale") - + col.label(text='Component') + col.label(text='Part') + col.label(text='Scale') + col = split.column(align=False) - col.prop(self, "field_viz_component", text="") - col.prop(self, "field_viz_part", text="") - col.prop(self, "field_viz_scale", text="") - + col.prop(self, 'field_viz_component', text='') + col.prop(self, 'field_viz_part', text='') + col.prop(self, 'field_viz_scale', text='') + # Coordinate Fixing split = layout.split(factor=0.45) col = split.column(align=False) - col.prop(self, "field_viz_plot_fix_x", text="Fix x (um)") - col.prop(self, "field_viz_plot_fix_y", text="Fix y (um)") - col.prop(self, "field_viz_plot_fix_z", text="Fix z (um)") - col.prop(self, "field_viz_plot_fix_f", text="Fix f (THz)") - + col.prop(self, 'field_viz_plot_fix_x', text='Fix x (um)') + col.prop(self, 'field_viz_plot_fix_y', text='Fix y (um)') + col.prop(self, 'field_viz_plot_fix_z', text='Fix z (um)') + col.prop(self, 'field_viz_plot_fix_f', text='Fix f (THz)') + col = split.column(align=False) - col.prop(self, "field_viz_plot_fixed_x", text="") - col.prop(self, "field_viz_plot_fixed_y", text="") - col.prop(self, "field_viz_plot_fixed_z", text="") - col.prop(self, "field_viz_plot_fixed_f", text="") - + col.prop(self, 'field_viz_plot_fixed_x', text='') + col.prop(self, 'field_viz_plot_fixed_y', text='') + col.prop(self, 'field_viz_plot_fixed_z', text='') + col.prop(self, 'field_viz_plot_fixed_f', text='') + #################### # - On Value Changed Methods #################### @base.on_value_changed( - socket_name="FDTD Sim Data", - - managed_objs={"viz_object"}, - input_sockets={"FDTD Sim Data"}, + socket_name='FDTD Sim Data', + managed_objs={'viz_object'}, + input_sockets={'FDTD Sim Data'}, ) def on_value_changed__fdtd_sim_data( self, @@ -244,30 +263,36 @@ class FDTDSimDataVizNode(base.MaxwellSimNode): input_sockets: dict[str, typ.Any], ) -> None: global CACHE - - if (sim_data := input_sockets["FDTD Sim Data"]) is None: + + if (sim_data := input_sockets['FDTD Sim Data']) is None: CACHE.pop(self.instance_id, None) return - - CACHE[self.instance_id] = {"monitors": list( - sim_data.monitor_data.keys() - )} - + + CACHE[self.instance_id] = { + 'monitors': list(sim_data.monitor_data.keys()) + } + #################### # - Plotting #################### @base.on_show_plot( - managed_objs={"viz_plot"}, + managed_objs={'viz_plot'}, props={ - "viz_monitor_name", "field_viz_component", - "field_viz_part", "field_viz_scale", - "field_viz_structure_visibility", - "field_viz_plot_fix_x", "field_viz_plot_fix_y", - "field_viz_plot_fix_z", "field_viz_plot_fix_f", - "field_viz_plot_fixed_x", "field_viz_plot_fixed_y", - "field_viz_plot_fixed_z", "field_viz_plot_fixed_f", + 'viz_monitor_name', + 'field_viz_component', + 'field_viz_part', + 'field_viz_scale', + 'field_viz_structure_visibility', + 'field_viz_plot_fix_x', + 'field_viz_plot_fix_y', + 'field_viz_plot_fix_z', + 'field_viz_plot_fix_f', + 'field_viz_plot_fixed_x', + 'field_viz_plot_fixed_y', + 'field_viz_plot_fixed_z', + 'field_viz_plot_fixed_f', }, - input_sockets={"FDTD Sim Data"}, + input_sockets={'FDTD Sim Data'}, stop_propagation=True, ) def on_show_plot( @@ -276,47 +301,47 @@ class FDTDSimDataVizNode(base.MaxwellSimNode): input_sockets: dict[str, typ.Any], props: dict[str, typ.Any], ): - if ( - (sim_data := input_sockets["FDTD Sim Data"]) is None - or (monitor_name := props["viz_monitor_name"]) == "NONE" - ): + if (sim_data := input_sockets['FDTD Sim Data']) is None or ( + monitor_name := props['viz_monitor_name'] + ) == 'NONE': return - + coord_fix = {} - for coord in ["x", "y", "z", "f"]: - if props[f"field_viz_plot_fix_{coord}"]: + for coord in ['x', 'y', 'z', 'f']: + if props[f'field_viz_plot_fix_{coord}']: coord_fix |= { - coord: props[f"field_viz_plot_fixed_{coord}"], + coord: props[f'field_viz_plot_fixed_{coord}'], } - - if "f" in coord_fix: - coord_fix["f"] *= 1e12 - - managed_objs["viz_plot"].mpl_plot_to_image( + + if 'f' in coord_fix: + coord_fix['f'] *= 1e12 + + managed_objs['viz_plot'].mpl_plot_to_image( lambda ax: sim_data.plot_field( monitor_name, - props["field_viz_component"], - val=props["field_viz_part"], - scale=props["field_viz_scale"], - eps_alpha=props["field_viz_structure_visibility"], + props['field_viz_component'], + val=props['field_viz_part'], + scale=props['field_viz_scale'], + eps_alpha=props['field_viz_structure_visibility'], phase=0, **coord_fix, ax=ax, ), bl_select=True, ) - #@base.on_show_preview( - # managed_objs={"viz_object"}, - #) - #def on_show_preview( - # self, - # managed_objs: dict[str, ct.schemas.ManagedObj], - #): - # """Called whenever a Loose Input Socket is altered. - # - # Synchronizes the change to the actual GeoNodes modifier, so that the change is immediately visible. - # """ - # managed_objs["viz_object"].show_preview("MESH") + + # @base.on_show_preview( + # managed_objs={"viz_object"}, + # ) + # def on_show_preview( + # self, + # managed_objs: dict[str, ct.schemas.ManagedObj], + # ): + # """Called whenever a Loose Input Socket is altered. + # + # Synchronizes the change to the actual GeoNodes modifier, so that the change is immediately visible. + # """ + # managed_objs["viz_object"].show_preview("MESH") #################### @@ -325,8 +350,4 @@ class FDTDSimDataVizNode(base.MaxwellSimNode): BL_REGISTER = [ FDTDSimDataVizNode, ] -BL_NODES = { - ct.NodeType.FDTDSimDataViz: ( - ct.NodeCategory.MAXWELLSIM_VIZ - ) -} +BL_NODES = {ct.NodeType.FDTDSimDataViz: (ct.NodeCategory.MAXWELLSIM_VIZ)} diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/__init__.py index f7f2552..e1c74b4 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/__init__.py @@ -1,18 +1,21 @@ from . import base from . import basic + AnySocketDef = basic.AnySocketDef BoolSocketDef = basic.BoolSocketDef StringSocketDef = basic.StringSocketDef FilePathSocketDef = basic.FilePathSocketDef from . import number + IntegerNumberSocketDef = number.IntegerNumberSocketDef RationalNumberSocketDef = number.RationalNumberSocketDef RealNumberSocketDef = number.RealNumberSocketDef ComplexNumberSocketDef = number.ComplexNumberSocketDef from . import vector + Real2DVectorSocketDef = vector.Real2DVectorSocketDef Complex2DVectorSocketDef = vector.Complex2DVectorSocketDef Integer3DVectorSocketDef = vector.Integer3DVectorSocketDef @@ -20,6 +23,7 @@ Real3DVectorSocketDef = vector.Real3DVectorSocketDef Complex3DVectorSocketDef = vector.Complex3DVectorSocketDef from . import physical + PhysicalUnitSystemSocketDef = physical.PhysicalUnitSystemSocketDef PhysicalTimeSocketDef = physical.PhysicalTimeSocketDef PhysicalAngleSocketDef = physical.PhysicalAngleSocketDef @@ -36,6 +40,7 @@ PhysicalPolSocketDef = physical.PhysicalPolSocketDef PhysicalFreqSocketDef = physical.PhysicalFreqSocketDef from . import blender + BlenderObjectSocketDef = blender.BlenderObjectSocketDef BlenderCollectionSocketDef = blender.BlenderCollectionSocketDef BlenderImageSocketDef = blender.BlenderImageSocketDef @@ -43,6 +48,7 @@ BlenderGeoNodesSocketDef = blender.BlenderGeoNodesSocketDef BlenderTextSocketDef = blender.BlenderTextSocketDef from . import maxwell + MaxwellBoundCondSocketDef = maxwell.MaxwellBoundCondSocketDef MaxwellBoundCondsSocketDef = maxwell.MaxwellBoundCondsSocketDef MaxwellMediumSocketDef = maxwell.MaxwellMediumSocketDef @@ -58,6 +64,7 @@ MaxwellSimGridAxisSocketDef = maxwell.MaxwellSimGridAxisSocketDef MaxwellSimDomainSocketDef = maxwell.MaxwellSimDomainSocketDef from . import tidy3d + Tidy3DCloudTaskSocketDef = tidy3d.Tidy3DCloudTaskSocketDef BL_REGISTER = [ diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py index 943599c..f4767f8 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py @@ -9,15 +9,20 @@ import sympy as sp import sympy.physics.units as spu from .. import contracts as ct + class MaxwellSimSocket(bpy.types.NodeSocket): # Fundamentals socket_type: ct.SocketType bl_label: str - + # Style display_shape: typx.Literal[ - "CIRCLE", "SQUARE", "DIAMOND", "CIRCLE_DOT", "SQUARE_DOT", - "DIAMOND_DOT", + 'CIRCLE', + 'SQUARE', + 'DIAMOND', + 'CIRCLE_DOT', + 'SQUARE_DOT', + 'DIAMOND_DOT', ] ## We use the following conventions for shapes: ## - CIRCLE: Single Value. @@ -25,266 +30,288 @@ class MaxwellSimSocket(bpy.types.NodeSocket): ## - DIAMOND: Pointer Value. ## - +DOT: Uses Units socket_color: tuple - + # Options - #link_limit: int = 0 + # link_limit: int = 0 use_units: bool = False use_prelock: bool = False - + # Computed bl_idname: str - + #################### # - Initialization #################### def __init_subclass__(cls, **kwargs: typ.Any): super().__init_subclass__(**kwargs) ## Yucky superclass setup. - + # Setup Blender ID for Node - if not hasattr(cls, "socket_type"): + if not hasattr(cls, 'socket_type'): msg = f"Socket class {cls} does not define 'socket_type'" raise ValueError(msg) cls.bl_idname = str(cls.socket_type.value) - + # Setup Locked Property for Node - cls.__annotations__["locked"] = bpy.props.BoolProperty( - name="Locked State", + cls.__annotations__['locked'] = bpy.props.BoolProperty( + name='Locked State', description="The lock-state of a particular socket, which determines the socket's user editability", default=False, ) - + # Setup Style cls.socket_color = ct.SOCKET_COLORS[cls.socket_type] cls.socket_shape = ct.SOCKET_SHAPES[cls.socket_type] - + # Setup List - cls.__annotations__["is_list"] = bpy.props.BoolProperty( - name="Is List", - description="Whether or not a particular socket is a list type socket", + cls.__annotations__['is_list'] = bpy.props.BoolProperty( + name='Is List', + description='Whether or not a particular socket is a list type socket', default=False, - update=lambda self, context: self.sync_is_list(context) + update=lambda self, context: self.sync_is_list(context), ) - + # Configure Use of Units if cls.use_units: # Set Shape :) - cls.socket_shape += "_DOT" - + cls.socket_shape += '_DOT' + if not (socket_units := ct.SOCKET_UNITS.get(cls.socket_type)): - msg = "Tried to `use_units` on {cls.bl_idname} socket, but `SocketType` has no units defined in `contracts.SOCKET_UNITS`" + msg = 'Tried to `use_units` on {cls.bl_idname} socket, but `SocketType` has no units defined in `contracts.SOCKET_UNITS`' raise RuntimeError(msg) - + # Current Unit - cls.__annotations__["active_unit"] = bpy.props.EnumProperty( - name="Unit", - description="Choose a unit", + cls.__annotations__['active_unit'] = bpy.props.EnumProperty( + name='Unit', + description='Choose a unit', items=[ (unit_name, str(unit_value), str(unit_value)) - for unit_name, unit_value in socket_units["values"].items() + for unit_name, unit_value in socket_units['values'].items() ], - default=socket_units["default"], + default=socket_units['default'], update=lambda self, context: self.sync_unit_change(), ) - + # Previous Unit (for conversion) - cls.__annotations__["prev_active_unit"] = bpy.props.StringProperty( - default=socket_units["default"], + cls.__annotations__['prev_active_unit'] = bpy.props.StringProperty( + default=socket_units['default'], ) - + #################### # - Action Chain #################### def trigger_action( self, - action: typx.Literal["enable_lock", "disable_lock", "value_changed", "show_preview", "show_plot"], + action: typx.Literal[ + 'enable_lock', + 'disable_lock', + 'value_changed', + 'show_preview', + 'show_plot', + ], ) -> None: """Called whenever the socket's output value has changed. - + This also invalidates any of the socket's caches. - + When called on an input node, the containing node's `trigger_action` method will be called with this socket. - + When called on a linked output node, the linked socket's `trigger_action` method will be called. """ # Forwards Chains - if action in {"value_changed"}: + if action in {'value_changed'}: ## Input Socket if not self.is_output: self.node.trigger_action(action, socket_name=self.name) - + ## Linked Output Socket elif self.is_output and self.is_linked: for link in self.links: link.to_socket.trigger_action(action) - + # Backwards Chains - elif action in {"enable_lock", "disable_lock", "show_preview", "show_plot"}: - if action == "enable_lock": + elif action in { + 'enable_lock', + 'disable_lock', + 'show_preview', + 'show_plot', + }: + if action == 'enable_lock': self.locked = True - - if action == "disable_lock": + + if action == 'disable_lock': self.locked = False - + ## Output Socket if self.is_output: self.node.trigger_action(action, socket_name=self.name) - + ## Linked Input Socket elif not self.is_output and self.is_linked: for link in self.links: link.from_socket.trigger_action(action) - + #################### # - Action Chain: Event Handlers #################### def sync_is_list(self, context: bpy.types.Context): - """Called when the "is_list_ property has been updated. - """ + """Called when the "is_list_ property has been updated.""" if self.is_list: if self.use_units: - self.display_shape = "SQUARE_DOT" + self.display_shape = 'SQUARE_DOT' else: - self.display_shape = "SQUARE" - - self.trigger_action("value_changed") - + self.display_shape = 'SQUARE' + + self.trigger_action('value_changed') + def sync_prop(self, prop_name: str, context: bpy.types.Context): - """Called when a property has been updated. - """ + """Called when a property has been updated.""" if not hasattr(self, prop_name): - msg = f"Property {prop_name} not defined on socket {self}" + msg = f'Property {prop_name} not defined on socket {self}' raise RuntimeError(msg) - - self.trigger_action("value_changed") - + + self.trigger_action('value_changed') + def sync_link_added(self, link) -> bool: """Called when a link has been added to this (input) socket. - + Returns a bool, whether or not the socket consents to the link change. """ - if self.locked: return False + if self.locked: + return False if self.is_output: msg = f"Tried to sync 'link add' on output socket" raise RuntimeError(msg) - - self.trigger_action("value_changed") - + + self.trigger_action('value_changed') + return True - + def sync_link_removed(self, from_socket) -> bool: """Called when a link has been removed from this (input) socket. - + Returns a bool, whether or not the socket consents to the link change. """ - if self.locked: return False + if self.locked: + return False if self.is_output: msg = f"Tried to sync 'link add' on output socket" raise RuntimeError(msg) - - self.trigger_action("value_changed") - + + self.trigger_action('value_changed') + return True - + #################### # - Data Chain #################### @property def value(self) -> typ.Any: raise NotImplementedError + @value.setter def value(self, value: typ.Any) -> None: raise NotImplementedError - + @property def value_list(self) -> typ.Any: return [self.value] + @value_list.setter def value_list(self, value: typ.Any) -> None: raise NotImplementedError - + def value_as_unit_system( - self, - unit_system: dict, - dimensionless: bool = True + self, unit_system: dict, dimensionless: bool = True ) -> typ.Any: ## TODO: Caching could speed this boi up quite a bit - + unit_system_unit = unit_system[self.socket_type] - return spu.convert_to( - self.value, - unit_system_unit, - ) / unit_system_unit - + return ( + spu.convert_to( + self.value, + unit_system_unit, + ) + / unit_system_unit + ) + @property def lazy_value(self) -> None: raise NotImplementedError + @lazy_value.setter def lazy_value(self, lazy_value: typ.Any) -> None: raise NotImplementedError - + @property def lazy_value_list(self) -> typ.Any: return [self.lazy_value] + @lazy_value_list.setter def lazy_value_list(self, value: typ.Any) -> None: raise NotImplementedError - + @property def capabilities(self) -> None: raise NotImplementedError - + def _compute_data( self, kind: ct.DataFlowKind = ct.DataFlowKind.Value, ) -> typ.Any: """Computes the internal data of this socket, ONLY. - + **NOTE**: Low-level method. Use `compute_data` instead. """ if kind == ct.DataFlowKind.Value: - if self.is_list: return self.value_list - else: return self.value + if self.is_list: + return self.value_list + else: + return self.value elif kind == ct.DataFlowKind.LazyValue: - if self.is_list: return self.lazy_value_list - else: return self.lazy_value + if self.is_list: + return self.lazy_value_list + else: + return self.lazy_value return self.lazy_value elif kind == ct.DataFlowKind.Capabilities: return self.capabilities - + return None - + def compute_data( self, kind: ct.DataFlowKind = ct.DataFlowKind.Value, ): """Computes the value of this socket, including all relevant factors: - - If input socket, and unlinked, compute internal data. - - If input socket, and linked, compute linked socket data. - - If output socket, ask node for data. + - If input socket, and unlinked, compute internal data. + - If input socket, and linked, compute linked socket data. + - If output socket, ask node for data. """ # Compute Output Socket ## List-like sockets guarantee that a list of a thing is passed. if self.is_output: res = self.node.compute_output(self.name, kind=kind) - if self.is_list and not isinstance(res, list): return [res] + if self.is_list and not isinstance(res, list): + return [res] return res - + # Compute Input Socket ## Unlinked: Retrieve Socket Value - if not self.is_linked: return self._compute_data(kind) - + if not self.is_linked: + return self._compute_data(kind) + ## Linked: Compute Output of Linked Sockets linked_values = [ - link.from_socket.compute_data(kind) - for link in self.links + link.from_socket.compute_data(kind) for link in self.links ] - + ## Return Single Value / List of Values - if len(linked_values) == 1: return linked_values[0] + if len(linked_values) == 1: + return linked_values[0] return linked_values - + #################### # - Unit Properties #################### @@ -293,26 +320,24 @@ class MaxwellSimSocket(bpy.types.NodeSocket): 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"] - + + return ct.SOCKET_UNITS[self.socket_type]['values'] + @property def unit(self) -> sp.Expr: return self.possible_units[self.active_unit] - + @property def prev_unit(self) -> sp.Expr: return self.possible_units[self.prev_active_unit] - + @unit.setter def unit(self, value: str | sp.Expr) -> None: # Retrieve Unit by String if isinstance(value, str) and value in self.possible_units: self.active_unit = self.possible_units[value] return - + # Retrieve =1 Matching Unit Name matching_unit_names = [ unit_name @@ -322,34 +347,33 @@ class MaxwellSimSocket(bpy.types.NodeSocket): 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(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(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] - + def sync_unit_change(self) -> None: """In unit-aware sockets, the internal `value()` property multiplies the Blender property value by the current active unit. - + When the unit is changed, `value()` will display the old scalar with the new unit. To fix this, we need to update the scalar to use the new unit. - + Can be overridden if more specific logic is required. """ - + prev_value = self.value / self.unit * self.prev_unit ## After changing units, self.value is expressed in the wrong unit. ## - Therefore, we removing the new unit, and re-add the prev unit. ## - Using only self.value avoids implementation-specific details. - + self.value = spu.convert_to( - prev_value, - self.unit + prev_value, self.unit ) ## Now, the unit conversion can be done correctly. - + self.prev_active_unit = self.active_unit - + #################### # - Style #################### @@ -358,16 +382,14 @@ class MaxwellSimSocket(bpy.types.NodeSocket): context: bpy.types.Context, node: bpy.types.Node, ) -> ct.BLColorRGBA: - """Color of the socket icon, when embedded in a node. - """ + """Color of the socket icon, when embedded in a node.""" return self.socket_color - + @classmethod def draw_color_simple(cls) -> ct.BLColorRGBA: - """Fallback color of the socket icon (ex.when not embedded in a node). - """ + """Fallback color of the socket icon (ex.when not embedded in a node).""" return cls.socket_color - + #################### # - UI Methods #################### @@ -378,14 +400,13 @@ class MaxwellSimSocket(bpy.types.NodeSocket): node: bpy.types.Node, text: str, ) -> None: - """Called by Blender to draw the socket UI. - """ - + """Called by Blender to draw the socket UI.""" + if self.is_output: self.draw_output(context, layout, node, text) else: self.draw_input(context, layout, node, text) - + def draw_prelock( self, context: bpy.types.Context, @@ -394,7 +415,7 @@ class MaxwellSimSocket(bpy.types.NodeSocket): text: str, ) -> None: pass - + def draw_input( self, context: bpy.types.Context, @@ -402,51 +423,52 @@ class MaxwellSimSocket(bpy.types.NodeSocket): node: bpy.types.Node, text: str, ) -> None: - """Draws the socket UI, when the socket is an input socket. - """ + """Draws the socket UI, when the socket is an input socket.""" col = layout.column(align=False) - + # Label Row row = col.row(align=False) - if self.locked: row.enabled = False - + if self.locked: + row.enabled = False + ## Linked Label if self.is_linked: row.label(text=text) return - + ## User Label Row (incl. Units) if self.use_units: split = row.split(factor=0.6, align=True) - + _row = split.row(align=True) self.draw_label_row(_row, text) - + _col = split.column(align=True) - _col.prop(self, "active_unit", text="") + _col.prop(self, 'active_unit', text='') else: self.draw_label_row(row, text) - + # Prelock Row row = col.row(align=False) if self.use_prelock: _col = row.column(align=False) _col.enabled = True self.draw_prelock(context, _col, node, text) - + if self.locked: row = col.row(align=False) row.enabled = False else: - if self.locked: row.enabled = False - + if self.locked: + row.enabled = False + # Value Column(s) col = row.column(align=True) if self.is_list: self.draw_value_list(col) else: self.draw_value(col) - + def draw_output( self, context: bpy.types.Context, @@ -454,10 +476,9 @@ class MaxwellSimSocket(bpy.types.NodeSocket): node: bpy.types.Node, text: str, ) -> None: - """Draws the socket UI, when the socket is an output socket. - """ + """Draws the socket UI, when the socket is an output socket.""" layout.label(text=text) - + #################### # - UI Methods #################### @@ -467,22 +488,21 @@ class MaxwellSimSocket(bpy.types.NodeSocket): text: str, ) -> None: """Called to draw the label row (same height as socket shape). - + Can be overridden. """ row.label(text=text) - + def draw_value(self, col: bpy.types.UILayout) -> None: """Called to draw the value column in unlinked input sockets. - + Can be overridden. """ pass - + def draw_value_list(self, col: bpy.types.UILayout) -> None: """Called to draw the value list column in unlinked input sockets. - + Can be overridden. """ pass - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/__init__.py index 11935e5..bc647c5 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/__init__.py @@ -1,13 +1,17 @@ from . import any as any_socket + AnySocketDef = any_socket.AnySocketDef from . import bool as bool_socket + BoolSocketDef = bool_socket.BoolSocketDef from . import string + StringSocketDef = string.StringSocketDef from . import file_path + FilePathSocketDef = file_path.FilePathSocketDef diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/any.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/any.py index 6961145..140d1c1 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/any.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/any.py @@ -7,22 +7,25 @@ import pydantic as pyd from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class AnyBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.Any - bl_label = "Any" + bl_label = 'Any' + #################### # - Socket Configuration #################### class AnySocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.Any - + def init(self, bl_socket: AnyBLSocket) -> None: pass + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/bool.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/bool.py index b0cd559..ab6b8f4 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/bool.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/bool.py @@ -7,52 +7,57 @@ import pydantic as pyd from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class BoolBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.Bool - bl_label = "Bool" - + bl_label = 'Bool' + #################### # - Properties #################### raw_value: bpy.props.BoolProperty( - name="Boolean", - description="Represents a boolean value", + name='Boolean', + description='Represents a boolean value', default=False, - update=(lambda self, context: self.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - Socket UI #################### - def draw_label_row(self, label_col_row: bpy.types.UILayout, text: str) -> None: + def draw_label_row( + self, label_col_row: bpy.types.UILayout, text: str + ) -> None: label_col_row.label(text=text) - label_col_row.prop(self, "raw_value", text="") - + label_col_row.prop(self, 'raw_value', text='') + #################### # - Computation of Default Value #################### @property def value(self) -> bool: return self.raw_value - + @value.setter def value(self, value: bool) -> None: self.raw_value = value + #################### # - Socket Configuration #################### class BoolSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.Bool - + default_value: bool = False - + def init(self, bl_socket: BoolBLSocket) -> None: bl_socket.value = self.default_value + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/file_path.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/file_path.py index c2ae889..d7f3499 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/file_path.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/file_path.py @@ -8,52 +8,55 @@ import pydantic as pyd from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class FilePathBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.FilePath - bl_label = "File Path" - + bl_label = 'File Path' + #################### # - Properties #################### raw_value: bpy.props.StringProperty( - name="File Path", - description="Represents the path to a file", - subtype="FILE_PATH", - update=(lambda self, context: self.sync_prop("raw_value", context)), + name='File Path', + description='Represents the path to a file', + subtype='FILE_PATH', + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - Socket UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: col_row = col.row(align=True) - col_row.prop(self, "raw_value", text="") - + col_row.prop(self, 'raw_value', text='') + #################### # - Computation of Default Value #################### @property def value(self) -> Path: return Path(bpy.path.abspath(self.raw_value)) - + @value.setter def value(self, value: Path) -> None: self.raw_value = bpy.path.relpath(str(value)) + #################### # - Socket Configuration #################### class FilePathSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.FilePath - - default_path: Path = Path("") - + + default_path: Path = Path('') + def init(self, bl_socket: FilePathBLSocket) -> None: bl_socket.value = self.default_path + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/string.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/string.py index 5d5f59a..d2cc45a 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/string.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/basic/string.py @@ -7,51 +7,56 @@ import pydantic as pyd from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class StringBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.String - bl_label = "String" - + bl_label = 'String' + #################### # - Properties #################### raw_value: bpy.props.StringProperty( - name="String", - description="Represents a string", - default="", - update=(lambda self, context: self.sync_prop("raw_value", context)), + name='String', + description='Represents a string', + default='', + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - Socket UI #################### - def draw_label_row(self, label_col_row: bpy.types.UILayout, text: str) -> None: - label_col_row.prop(self, "raw_value", text=text) - + def draw_label_row( + self, label_col_row: bpy.types.UILayout, text: str + ) -> None: + label_col_row.prop(self, 'raw_value', text=text) + #################### # - Computation of Default Value #################### @property def value(self) -> str: return self.raw_value - + @value.setter def value(self, value: str) -> None: self.raw_value = value + #################### # - Socket Configuration #################### class StringSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.String - - default_text: str = "" - + + default_text: str = '' + def init(self, bl_socket: StringBLSocket) -> None: bl_socket.value = self.default_text + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/__init__.py index 3d41ae7..e18eff0 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/__init__.py @@ -1,20 +1,22 @@ from . import object as object_socket from . import collection + BlenderObjectSocketDef = object_socket.BlenderObjectSocketDef BlenderCollectionSocketDef = collection.BlenderCollectionSocketDef from . import image + BlenderImageSocketDef = image.BlenderImageSocketDef from . import geonodes from . import text + BlenderGeoNodesSocketDef = geonodes.BlenderGeoNodesSocketDef BlenderTextSocketDef = text.BlenderTextSocketDef BL_REGISTER = [ *object_socket.BL_REGISTER, *collection.BL_REGISTER, - *text.BL_REGISTER, *image.BL_REGISTER, *geonodes.BL_REGISTER, diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/collection.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/collection.py index d454a25..c8e39fb 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/collection.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/collection.py @@ -6,52 +6,53 @@ import pydantic as pyd from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class BlenderCollectionBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.BlenderCollection - bl_label = "Blender Collection" - + bl_label = 'Blender Collection' + #################### # - Properties #################### raw_value: bpy.props.PointerProperty( - name="Blender Collection", - description="A Blender collection", + name='Blender Collection', + description='A Blender collection', type=bpy.types.Collection, - update=(lambda self, context: self.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "raw_value", text="") - + col.prop(self, 'raw_value', text='') + #################### # - Default Value #################### @property def value(self) -> bpy.types.Collection | None: return self.raw_value - + @value.setter def value(self, value: bpy.types.Collection) -> None: self.raw_value = value + #################### # - Socket Configuration #################### class BlenderCollectionSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.BlenderCollection - + def init(self, bl_socket: BlenderCollectionBLSocket) -> None: pass + #################### # - Blender Registration #################### -BL_REGISTER = [ - BlenderCollectionBLSocket -] +BL_REGISTER = [BlenderCollectionBLSocket] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/geonodes.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/geonodes.py index 266f6d2..8f88bd4 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/geonodes.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/geonodes.py @@ -6,25 +6,26 @@ import pydantic as pyd from .. import base from ... import contracts as ct + #################### # - Operators #################### class BlenderMaxwellResetGeoNodesSocket(bpy.types.Operator): - bl_idname = "blender_maxwell.reset_geo_nodes_socket" - bl_label = "Reset GeoNodes Socket" - - node_tree_name: bpy.props.StringProperty(name="Node Tree Name") - node_name: bpy.props.StringProperty(name="Node Name") - socket_name: bpy.props.StringProperty(name="Socket Name") - + bl_idname = 'blender_maxwell.reset_geo_nodes_socket' + bl_label = 'Reset GeoNodes Socket' + + node_tree_name: bpy.props.StringProperty(name='Node Tree Name') + node_name: bpy.props.StringProperty(name='Node Name') + socket_name: bpy.props.StringProperty(name='Socket Name') + def execute(self, context): node_tree = bpy.data.node_groups[self.node_tree_name] node = node_tree.nodes[self.node_name] socket = node.inputs[self.socket_name] - + # Report as though the GeoNodes Tree Changed - socket.sync_prop("raw_value", context) - + socket.sync_prop('raw_value', context) + return {'FINISHED'} @@ -33,61 +34,63 @@ class BlenderMaxwellResetGeoNodesSocket(bpy.types.Operator): #################### class BlenderGeoNodesBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.BlenderGeoNodes - bl_label = "Geometry Node Tree" - + bl_label = 'Geometry Node Tree' + #################### # - Properties #################### raw_value: bpy.props.PointerProperty( - name="Blender GeoNodes Tree", - description="Represents a Blender GeoNodes Tree", + name='Blender GeoNodes Tree', + description='Represents a Blender GeoNodes Tree', type=bpy.types.NodeTree, - poll=(lambda self, obj: obj.bl_idname == "GeometryNodeTree"), - update=(lambda self, context: self.sync_prop("raw_value", context)), + poll=(lambda self, obj: obj.bl_idname == 'GeometryNodeTree'), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - UI #################### - #def draw_label_row(self, label_col_row, text): - # label_col_row.label(text=text) - # if not self.raw_value: return - # - # op = label_col_row.operator( - # BlenderMaxwellResetGeoNodesSocket.bl_idname, - # text="", - # icon="FILE_REFRESH", - # ) - # op.socket_name = self.name - # op.node_name = self.node.name - # op.node_tree_name = self.node.id_data.name - + # def draw_label_row(self, label_col_row, text): + # label_col_row.label(text=text) + # if not self.raw_value: return + # + # op = label_col_row.operator( + # BlenderMaxwellResetGeoNodesSocket.bl_idname, + # text="", + # icon="FILE_REFRESH", + # ) + # op.socket_name = self.name + # op.node_name = self.node.name + # op.node_tree_name = self.node.id_data.name + #################### # - UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "raw_value", text="") - + col.prop(self, 'raw_value', text='') + #################### # - Default Value #################### @property def value(self) -> bpy.types.NodeTree | None: return self.raw_value - + @value.setter def value(self, value: bpy.types.NodeTree) -> None: self.raw_value = value + #################### # - Socket Configuration #################### class BlenderGeoNodesSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.BlenderGeoNodes - + def init(self, bl_socket: BlenderGeoNodesBLSocket) -> None: pass + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/image.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/image.py index ebc7f1c..44c3b69 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/image.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/image.py @@ -6,52 +6,53 @@ import pydantic as pyd from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class BlenderImageBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.BlenderImage - bl_label = "Blender Image" - + bl_label = 'Blender Image' + #################### # - Properties #################### raw_value: bpy.props.PointerProperty( - name="Blender Image", - description="Represents a Blender Image", + name='Blender Image', + description='Represents a Blender Image', type=bpy.types.Image, - update=(lambda self, context: self.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "raw_value", text="") - + col.prop(self, 'raw_value', text='') + #################### # - Default Value #################### @property def value(self) -> bpy.types.Image | None: return self.raw_value - + @value.setter def value(self, value: bpy.types.Image) -> None: self.raw_value = value + #################### # - Socket Configuration #################### class BlenderImageSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.BlenderImage - + def init(self, bl_socket: BlenderImageBLSocket) -> None: pass + #################### # - Blender Registration #################### -BL_REGISTER = [ - BlenderImageBLSocket -] +BL_REGISTER = [BlenderImageBLSocket] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/object.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/object.py index 8f0ef80..178e9ea 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/object.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/object.py @@ -6,93 +6,97 @@ import pydantic as pyd from .. import base from ... import contracts as ct + #################### # - Create and Assign BL Object #################### class BlenderMaxwellCreateAndAssignBLObject(bpy.types.Operator): - bl_idname = "blender_maxwell.create_and_assign_bl_object" - bl_label = "Create and Assign BL Object" - - node_tree_name = bpy.props.StringProperty(name="Node Tree Name") - node_name = bpy.props.StringProperty(name="Node Name") - socket_name = bpy.props.StringProperty(name="Socket Name") - + bl_idname = 'blender_maxwell.create_and_assign_bl_object' + bl_label = 'Create and Assign BL Object' + + node_tree_name = bpy.props.StringProperty(name='Node Tree Name') + node_name = bpy.props.StringProperty(name='Node Name') + socket_name = bpy.props.StringProperty(name='Socket Name') + def execute(self, context): node_tree = bpy.data.node_groups[self.node_tree_name] node = node_tree.nodes[self.node_name] socket = node.inputs[self.socket_name] - + socket.create_and_assign_bl_object() - + return {'FINISHED'} + #################### # - Blender Socket #################### class BlenderObjectBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.BlenderObject - bl_label = "Blender Object" - + bl_label = 'Blender Object' + #################### # - Properties #################### raw_value: bpy.props.PointerProperty( - name="Blender Object", - description="Represents a Blender object", + name='Blender Object', + description='Represents a Blender object', type=bpy.types.Object, - update=(lambda self, context: self.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - UI #################### def draw_label_row(self, label_col_row, text): label_col_row.label(text=text) - + op = label_col_row.operator( - "blender_maxwell.create_and_assign_bl_object", - text="", - icon="ADD", + 'blender_maxwell.create_and_assign_bl_object', + text='', + icon='ADD', ) op.socket_name = self.name op.node_name = self.node.name op.node_tree_name = self.node.id_data.name - + def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "raw_value", text="") - + col.prop(self, 'raw_value', text='') + #################### # - Methods #################### def create_and_assign_bl_object(self): node_tree = self.node.id_data - mesh = bpy.data.meshes.new("MaxwellMesh") - new_bl_object = bpy.data.objects.new("MaxwellObject", mesh) - + mesh = bpy.data.meshes.new('MaxwellMesh') + new_bl_object = bpy.data.objects.new('MaxwellObject', mesh) + bpy.context.collection.objects.link(new_bl_object) - + self.value = new_bl_object - + #################### # - Default Value #################### @property def value(self) -> bpy.types.Object | None: return self.raw_value - + @value.setter def value(self, value: bpy.types.Object) -> None: self.raw_value = value + #################### # - Socket Configuration #################### class BlenderObjectSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.BlenderObject - + def init(self, bl_socket: BlenderObjectBLSocket) -> None: pass + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/text.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/text.py index 53d1ef0..8c10a17 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/text.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/text.py @@ -6,52 +6,53 @@ import pydantic as pyd from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class BlenderTextBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.BlenderText - bl_label = "Blender Text" - + bl_label = 'Blender Text' + #################### # - Properties #################### raw_value: bpy.props.PointerProperty( - name="Blender Text", - description="Represents a Blender text datablock", + name='Blender Text', + description='Represents a Blender text datablock', type=bpy.types.Text, - update=(lambda self, context: self.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "raw_value", text="") - + col.prop(self, 'raw_value', text='') + #################### # - Default Value #################### @property def value(self) -> bpy.types.Text: return self.raw_value - + @value.setter def value(self, value: bpy.types.Text) -> None: self.raw_value = value + #################### # - Socket Configuration #################### class BlenderTextSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.BlenderText - + def init(self, bl_socket: BlenderTextBLSocket) -> None: pass + #################### # - Blender Registration #################### -BL_REGISTER = [ - BlenderTextBLSocket -] +BL_REGISTER = [BlenderTextBLSocket] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/__init__.py index 5fff900..e17aebc 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/__init__.py @@ -1,22 +1,29 @@ from . import bound_cond from . import bound_conds + MaxwellBoundCondSocketDef = bound_cond.MaxwellBoundCondSocketDef MaxwellBoundCondsSocketDef = bound_conds.MaxwellBoundCondsSocketDef from . import medium from . import medium_non_linearity + MaxwellMediumSocketDef = medium.MaxwellMediumSocketDef -MaxwellMediumNonLinearitySocketDef = medium_non_linearity.MaxwellMediumNonLinearitySocketDef +MaxwellMediumNonLinearitySocketDef = ( + medium_non_linearity.MaxwellMediumNonLinearitySocketDef +) from . import source from . import temporal_shape + MaxwellSourceSocketDef = source.MaxwellSourceSocketDef MaxwellTemporalShapeSocketDef = temporal_shape.MaxwellTemporalShapeSocketDef from . import structure + MaxwellStructureSocketDef = structure.MaxwellStructureSocketDef from . import monitor + MaxwellMonitorSocketDef = monitor.MaxwellMonitorSocketDef from . import fdtd_sim @@ -24,6 +31,7 @@ from . import fdtd_sim_data from . import sim_grid from . import sim_grid_axis from . import sim_domain + MaxwellFDTDSimSocketDef = fdtd_sim.MaxwellFDTDSimSocketDef MaxwellFDTDSimDataSocketDef = fdtd_sim_data.MaxwellFDTDSimDataSocketDef MaxwellSimGridSocketDef = sim_grid.MaxwellSimGridSocketDef diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_cond.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_cond.py index 5f877ef..cf04ebb 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_cond.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_cond.py @@ -8,59 +8,66 @@ import tidy3d as td from .. import base from ... import contracts as ct + class MaxwellBoundCondBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.MaxwellBoundCond - bl_label = "Maxwell Bound Face" - + bl_label = 'Maxwell Bound Face' + #################### # - Properties #################### default_choice: bpy.props.EnumProperty( - name="Bound Face", - description="A choice of default boundary face", + name='Bound Face', + description='A choice of default boundary face', items=[ - ("PML", "PML", "Perfectly matched layer"), - ("PEC", "PEC", "Perfect electrical conductor"), - ("PMC", "PMC", "Perfect magnetic conductor"), - ("PERIODIC", "Periodic", "Infinitely periodic layer"), + ('PML', 'PML', 'Perfectly matched layer'), + ('PEC', 'PEC', 'Perfect electrical conductor'), + ('PMC', 'PMC', 'Perfect magnetic conductor'), + ('PERIODIC', 'Periodic', 'Infinitely periodic layer'), ], - default="PML", - update=(lambda self, context: self.sync_prop("default_choice", context)), + default='PML', + update=( + lambda self, context: self.sync_prop('default_choice', context) + ), ) - + #################### # - UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "default_choice", text="") - + col.prop(self, 'default_choice', text='') + #################### # - Computation of Default Value #################### @property def value(self) -> td.BoundarySpec: return { - "PML": td.PML(num_layers=12), - "PEC": td.PECBoundary(), - "PMC": td.PMCBoundary(), - "PERIODIC": td.Periodic(), + 'PML': td.PML(num_layers=12), + 'PEC': td.PECBoundary(), + 'PMC': td.PMCBoundary(), + 'PERIODIC': td.Periodic(), }[self.default_choice] - + @value.setter - def value(self, value: typx.Literal["PML", "PEC", "PMC", "PERIODIC"]) -> None: + def value( + self, value: typx.Literal['PML', 'PEC', 'PMC', 'PERIODIC'] + ) -> None: self.default_choice = value + #################### # - Socket Configuration #################### class MaxwellBoundCondSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.MaxwellBoundCond - - default_choice: typx.Literal["PML", "PEC", "PMC", "PERIODIC"] = "PML" - + + default_choice: typx.Literal['PML', 'PEC', 'PMC', 'PERIODIC'] = 'PML' + def init(self, bl_socket: MaxwellBoundCondBLSocket) -> None: bl_socket.value = self.default_choice + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_conds.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_conds.py index 873830a..35b0ad5 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_conds.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_conds.py @@ -8,101 +8,104 @@ from .. import base from ... import contracts as ct BOUND_FACE_ITEMS = [ - ("PML", "PML", "Perfectly matched layer"), - ("PEC", "PEC", "Perfect electrical conductor"), - ("PMC", "PMC", "Perfect magnetic conductor"), - ("PERIODIC", "Periodic", "Infinitely periodic layer"), + ('PML', 'PML', 'Perfectly matched layer'), + ('PEC', 'PEC', 'Perfect electrical conductor'), + ('PMC', 'PMC', 'Perfect magnetic conductor'), + ('PERIODIC', 'Periodic', 'Infinitely periodic layer'), ] BOUND_MAP = { - "PML": td.PML(), - "PEC": td.PECBoundary(), - "PMC": td.PMCBoundary(), - "PERIODIC": td.Periodic(), + 'PML': td.PML(), + 'PEC': td.PECBoundary(), + 'PMC': td.PMCBoundary(), + 'PERIODIC': td.Periodic(), } + class MaxwellBoundCondsBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.MaxwellBoundConds - bl_label = "Maxwell Bound Box" - + bl_label = 'Maxwell Bound Box' + #################### # - Properties #################### show_definition: bpy.props.BoolProperty( - name="Show Bounds Definition", - description="Toggle to show bound faces", + name='Show Bounds Definition', + description='Toggle to show bound faces', default=False, - update=(lambda self, context: self.sync_prop("show_definition", context)), + update=( + lambda self, context: self.sync_prop('show_definition', context) + ), ) - + x_pos: bpy.props.EnumProperty( - name="+x Bound Face", - description="+x choice of default boundary face", + name='+x Bound Face', + description='+x choice of default boundary face', items=BOUND_FACE_ITEMS, - default="PML", - update=(lambda self, context: self.sync_prop("x_pos", context)), + default='PML', + update=(lambda self, context: self.sync_prop('x_pos', context)), ) x_neg: bpy.props.EnumProperty( - name="-x Bound Face", - description="-x choice of default boundary face", + name='-x Bound Face', + description='-x choice of default boundary face', items=BOUND_FACE_ITEMS, - default="PML", - update=(lambda self, context: self.sync_prop("x_neg", context)), + default='PML', + update=(lambda self, context: self.sync_prop('x_neg', context)), ) y_pos: bpy.props.EnumProperty( - name="+y Bound Face", - description="+y choice of default boundary face", + name='+y Bound Face', + description='+y choice of default boundary face', items=BOUND_FACE_ITEMS, - default="PML", - update=(lambda self, context: self.sync_prop("y_pos", context)), + default='PML', + update=(lambda self, context: self.sync_prop('y_pos', context)), ) y_neg: bpy.props.EnumProperty( - name="-y Bound Face", - description="-y choice of default boundary face", + name='-y Bound Face', + description='-y choice of default boundary face', items=BOUND_FACE_ITEMS, - default="PML", - update=(lambda self, context: self.sync_prop("y_neg", context)), + default='PML', + update=(lambda self, context: self.sync_prop('y_neg', context)), ) z_pos: bpy.props.EnumProperty( - name="+z Bound Face", - description="+z choice of default boundary face", + name='+z Bound Face', + description='+z choice of default boundary face', items=BOUND_FACE_ITEMS, - default="PML", - update=(lambda self, context: self.sync_prop("z_pos", context)), + default='PML', + update=(lambda self, context: self.sync_prop('z_pos', context)), ) z_neg: bpy.props.EnumProperty( - name="-z Bound Face", - description="-z choice of default boundary face", + name='-z Bound Face', + description='-z choice of default boundary face', items=BOUND_FACE_ITEMS, - default="PML", - update=(lambda self, context: self.sync_prop("z_neg", context)), + default='PML', + update=(lambda self, context: self.sync_prop('z_neg', context)), ) - + #################### # - UI #################### def draw_label_row(self, row: bpy.types.UILayout, text) -> None: row.label(text=text) row.prop( - self, "show_definition", toggle=True, text="", icon="MOD_LENGTH" + self, 'show_definition', toggle=True, text='', icon='MOD_LENGTH' ) - + def draw_value(self, col: bpy.types.UILayout) -> None: - if not self.show_definition: return - - for axis in ["x", "y", "z"]: + if not self.show_definition: + return + + for axis in ['x', 'y', 'z']: row = col.row(align=False) split = row.split(factor=0.2, align=False) - + _col = split.column(align=True) - _col.alignment = "RIGHT" - _col.label(text=axis + " -") - _col.label(text=" +") - + _col.alignment = 'RIGHT' + _col.label(text=axis + ' -') + _col.label(text=' +') + _col = split.column(align=True) - _col.prop(self, axis + "_neg", text="") - _col.prop(self, axis + "_pos", text="") - - + _col.prop(self, axis + '_neg', text='') + _col.prop(self, axis + '_pos', text='') + #################### # - Computation of Default Value #################### @@ -123,15 +126,17 @@ class MaxwellBoundCondsBLSocket(base.MaxwellSimSocket): ), ) + #################### # - Socket Configuration #################### class MaxwellBoundCondsSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.MaxwellBoundConds - + def init(self, bl_socket: MaxwellBoundCondsBLSocket) -> None: pass + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/fdtd_sim.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/fdtd_sim.py index a4458a4..230ca85 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/fdtd_sim.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/fdtd_sim.py @@ -7,23 +7,26 @@ import tidy3d as td from .. import base from ... import contracts as ct + class MaxwellFDTDSimBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.MaxwellFDTDSim - bl_label = "Maxwell FDTD Simulation" - + bl_label = 'Maxwell FDTD Simulation' + @property def value(self) -> None: return None + #################### # - Socket Configuration #################### class MaxwellFDTDSimSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.MaxwellFDTDSim - + def init(self, bl_socket: MaxwellFDTDSimBLSocket) -> None: pass + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/fdtd_sim_data.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/fdtd_sim_data.py index 0989340..50e0b5d 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/fdtd_sim_data.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/fdtd_sim_data.py @@ -7,23 +7,26 @@ import tidy3d as td from .. import base from ... import contracts as ct + class MaxwellFDTDSimDataBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.MaxwellFDTDSimData - bl_label = "Maxwell FDTD Simulation" - + bl_label = 'Maxwell FDTD Simulation' + @property def value(self): return None + #################### # - Socket Configuration #################### class MaxwellFDTDSimDataSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.MaxwellFDTDSimData - + def init(self, bl_socket: MaxwellFDTDSimDataBLSocket) -> None: pass + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/medium.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/medium.py index 944e41d..7ff6691 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/medium.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/medium.py @@ -7,110 +7,116 @@ import sympy.physics.units as spu import tidy3d as td import scipy as sc -from .....utils.pydantic_sympy import ConstrSympyExpr, Complex +from .....utils.pydantic_sympy import ConstrSympyExpr, Complex from .. import base from ... import contracts as ct -VAC_SPEED_OF_LIGHT = ( - sc.constants.speed_of_light - * spu.meter/spu.second -) +VAC_SPEED_OF_LIGHT = sc.constants.speed_of_light * spu.meter / spu.second + class MaxwellMediumBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.MaxwellMedium - bl_label = "Maxwell Medium" + bl_label = 'Maxwell Medium' use_units = True - + #################### # - Properties #################### wl: bpy.props.FloatProperty( - name="WL", - description="WL to evaluate conductivity at", + name='WL', + description='WL to evaluate conductivity at', default=500.0, precision=4, step=50, - update=(lambda self, context: self.sync_prop("wl", context)), + update=(lambda self, context: self.sync_prop('wl', context)), ) - + rel_permittivity: bpy.props.FloatVectorProperty( - name="Relative Permittivity", - description="Represents a simple, complex permittivity", + name='Relative Permittivity', + description='Represents a simple, complex permittivity', size=2, default=(1.0, 0.0), precision=2, - update=(lambda self, context: self.sync_prop("rel_permittivity", context)), + update=( + lambda self, context: self.sync_prop('rel_permittivity', context) + ), ) - + #################### # - Socket UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "wl", text="λ") + col.prop(self, 'wl', text='λ') col.separator(factor=1.0) - + split = col.split(factor=0.35, align=False) - + col = split.column(align=True) - col.label(text="ϵ_r (ℂ)") - + col.label(text='ϵ_r (ℂ)') + col = split.column(align=True) - col.prop(self, "rel_permittivity", text="") - + col.prop(self, 'rel_permittivity', text='') + #################### # - Computation of Default Value #################### @property def value(self) -> td.Medium: - freq = spu.convert_to( - VAC_SPEED_OF_LIGHT / (self.wl*self.unit), - spu.hertz, - ) / spu.hertz + freq = ( + spu.convert_to( + VAC_SPEED_OF_LIGHT / (self.wl * self.unit), + spu.hertz, + ) + / spu.hertz + ) return td.Medium.from_nk( n=self.rel_permittivity[0], k=self.rel_permittivity[1], freq=freq, ) - + @value.setter def value( - self, - value: tuple[ConstrSympyExpr(allow_variables=False), complex] + self, value: tuple[ConstrSympyExpr(allow_variables=False), complex] ) -> None: _wl, rel_permittivity = value - + wl = float( spu.convert_to( _wl, self.unit, - ) / self.unit + ) + / self.unit ) self.wl = wl self.rel_permittivity = (rel_permittivity.real, rel_permittivity.imag) - + def sync_unit_change(self): """Override unit change to only alter frequency unit.""" - + self.value = ( self.wl * self.prev_unit, - complex(*self.rel_permittivity) + complex(*self.rel_permittivity), ) self.prev_active_unit = self.active_unit + #################### # - Socket Configuration #################### class MaxwellMediumSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.MaxwellMedium - + default_permittivity_real: float = 1.0 default_permittivity_imag: float = 0.0 - + def init(self, bl_socket: MaxwellMediumBLSocket) -> None: bl_socket.rel_permittivity = ( - self.default_permittivity_real, self.default_permittivity_imag + self.default_permittivity_real, + self.default_permittivity_imag, ) + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/medium_non_linearity.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/medium_non_linearity.py index 85a3289..9ba3230 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/medium_non_linearity.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/medium_non_linearity.py @@ -7,19 +7,22 @@ import tidy3d as td from .. import base from ... import contracts as ct + class MaxwellMediumNonLinearityBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.MaxwellMediumNonLinearity - bl_label = "Medium Non-Linearity" + bl_label = 'Medium Non-Linearity' + #################### # - Socket Configuration #################### class MaxwellMediumNonLinearitySocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.MaxwellMediumNonLinearity - + def init(self, bl_socket: MaxwellMediumNonLinearityBLSocket) -> None: pass + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/monitor.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/monitor.py index 70fdf03..9472e40 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/monitor.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/monitor.py @@ -9,21 +9,24 @@ import scipy as sc from .. import base from ... import contracts as ct + class MaxwellMonitorBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.MaxwellMonitor - bl_label = "Maxwell Monitor" + bl_label = 'Maxwell Monitor' + #################### # - Socket Configuration #################### class MaxwellMonitorSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.MaxwellMonitor - + is_list: bool = False - + def init(self, bl_socket: MaxwellMonitorBLSocket) -> None: bl_socket.is_list = self.is_list + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_domain.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_domain.py index 8de522e..32fab1f 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_domain.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_domain.py @@ -7,19 +7,22 @@ import tidy3d as td from .. import base from ... import contracts as ct + class MaxwellSimDomainBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.MaxwellSimDomain - bl_label = "Sim Domain" + bl_label = 'Sim Domain' + #################### # - Socket Configuration #################### class MaxwellSimDomainSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.MaxwellSimDomain - + def init(self, bl_socket: MaxwellSimDomainBLSocket) -> None: pass + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_grid.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_grid.py index 4b4f2fe..8dabddb 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_grid.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_grid.py @@ -7,35 +7,38 @@ import tidy3d as td from .. import base from ... import contracts as ct + class MaxwellSimGridBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.MaxwellSimGrid - bl_label = "Maxwell Sim Grid" - + bl_label = 'Maxwell Sim Grid' + #################### # - Properties #################### min_steps_per_wl: bpy.props.FloatProperty( - name="Minimum Steps per Wavelength", - description="How many grid steps to ensure per wavelength", + name='Minimum Steps per Wavelength', + description='How many grid steps to ensure per wavelength', default=10.0, min=0.01, - #step=10, + # step=10, precision=2, - update=(lambda self, context: self.sync_prop("min_steps_per_wl", context)), + update=( + lambda self, context: self.sync_prop('min_steps_per_wl', context) + ), ) - + #################### # - Socket UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: split = col.split(factor=0.5, align=False) - + col = split.column(align=True) - col.label(text="min. stp/λ") - + col.label(text='min. stp/λ') + col = split.column(align=True) - col.prop(self, "min_steps_per_wl", text="") - + col.prop(self, 'min_steps_per_wl', text='') + #################### # - Computation of Default Value #################### @@ -45,17 +48,19 @@ class MaxwellSimGridBLSocket(base.MaxwellSimSocket): min_steps_per_wvl=self.min_steps_per_wl, ) + #################### # - Socket Configuration #################### class MaxwellSimGridSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.MaxwellSimGrid - + min_steps_per_wl: float = 10.0 - + def init(self, bl_socket: MaxwellSimGridBLSocket) -> None: bl_socket.min_steps_per_wl = self.min_steps_per_wl + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_grid_axis.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_grid_axis.py index 1cf325f..b326e30 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_grid_axis.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/sim_grid_axis.py @@ -7,19 +7,22 @@ import tidy3d as td from .. import base from ... import contracts as ct + class MaxwellSimGridAxisBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.MaxwellSimGridAxis - bl_label = "Maxwell Bound Box" + bl_label = 'Maxwell Bound Box' + #################### # - Socket Configuration #################### class MaxwellSimGridAxisSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.MaxwellSimGridAxis - + def init(self, bl_socket: MaxwellSimGridAxisBLSocket) -> None: pass + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/source.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/source.py index d28a0dc..f587a3f 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/source.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/source.py @@ -7,21 +7,24 @@ import tidy3d as td from .. import base from ... import contracts as ct + class MaxwellSourceBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.MaxwellSource - bl_label = "Maxwell Source" + bl_label = 'Maxwell Source' + #################### # - Socket Configuration #################### class MaxwellSourceSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.MaxwellSource - + is_list: bool = False - + def init(self, bl_socket: MaxwellSourceBLSocket) -> None: bl_socket.is_list = self.is_list + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/structure.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/structure.py index 4415464..5340719 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/structure.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/structure.py @@ -6,21 +6,24 @@ import pydantic as pyd from .. import base from ... import contracts as ct + class MaxwellStructureBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.MaxwellStructure - bl_label = "Maxwell Structure" + bl_label = 'Maxwell Structure' + #################### # - Socket Configuration #################### class MaxwellStructureSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.MaxwellStructure - + is_list: bool = False - + def init(self, bl_socket: MaxwellStructureBLSocket) -> None: bl_socket.is_list = self.is_list + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/temporal_shape.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/temporal_shape.py index 29676e1..85f4086 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/temporal_shape.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/temporal_shape.py @@ -7,19 +7,22 @@ import tidy3d as td from .. import base from ... import contracts as ct + class MaxwellTemporalShapeBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.MaxwellTemporalShape - bl_label = "Maxwell Temporal Shape" + bl_label = 'Maxwell Temporal Shape' + #################### # - Socket Configuration #################### class MaxwellTemporalShapeSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.MaxwellTemporalShape - + def init(self, bl_socket: MaxwellTemporalShapeBLSocket) -> None: pass + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/__init__.py index 8c763ad..8df878d 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/__init__.py @@ -1,13 +1,17 @@ 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 diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/complex_number.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/complex_number.py index 9be8249..80de02f 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/complex_number.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/complex_number.py @@ -8,35 +8,48 @@ from .....utils.pydantic_sympy import SympyExpr from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class ComplexNumberBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.ComplexNumber - bl_label = "Complex Number" - + bl_label = 'Complex Number' + #################### # - Properties #################### raw_value: bpy.props.FloatVectorProperty( - name="Complex Number", - description="Represents a complex number (real, imaginary)", + name='Complex Number', + description='Represents a complex number (real, imaginary)', size=2, default=(0.0, 0.0), subtype='NONE', - update=(lambda self, context: self.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) coord_sys: bpy.props.EnumProperty( - name="Coordinate System", - description="Choose between cartesian and polar form", + 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), + ( + 'CARTESIAN', + 'Cartesian', + 'Use Cartesian Coordinates', + 'EMPTY_AXIS', + 0, + ), + ( + 'POLAR', + 'Polar', + 'Use Polar Coordinates', + 'DRIVER_ROTATIONAL_DIFFERENCE', + 1, + ), ], - default="CARTESIAN", + default='CARTESIAN', update=lambda self, context: self._sync_coord_sys(context), ) - + #################### # - Socket UI #################### @@ -45,9 +58,9 @@ class ComplexNumberBLSocket(base.MaxwellSimSocket): specifying the active coordinate system. """ col_row = col.row() - col_row.prop(self, "raw_value", text="") - col.prop(self, "coord_sys", text="") - + col_row.prop(self, 'raw_value', text='') + col.prop(self, 'coord_sys', text='') + #################### # - Computation of Default Value #################### @@ -55,68 +68,70 @@ class ComplexNumberBLSocket(base.MaxwellSimSocket): def value(self) -> SympyExpr: """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 sympy expression representing the complex number. """ - + v1, v2 = self.raw_value - + return { - "CARTESIAN": v1 + sp.I*v2, - "POLAR": v1 * sp.exp(sp.I*v2), + 'CARTESIAN': v1 + sp.I * v2, + 'POLAR': v1 * sp.exp(sp.I * v2), }[self.coord_sys] - + @value.setter def value(self, value: SympyExpr) -> None: """Set the complex number from a sympy expression, using an internal representation determined by the coordinate system. - + - Cartesian: a,b -> a + ib - Polar: r,t -> re^(it) """ - + self.raw_value = { - "CARTESIAN": (sp.re(value), sp.im(value)), - "POLAR": (sp.Abs(value), sp.arg(value)), + 'CARTESIAN': (sp.re(value), sp.im(value)), + 'POLAR': (sp.Abs(value), sp.arg(value)), }[self.coord_sys] - + #################### # - Internal Update Methods #################### def _sync_coord_sys(self, context: bpy.types.Context): - if self.coord_sys == "CARTESIAN": + 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": + elif self.coord_sys == 'POLAR': x, y = self.raw_value - cart_value = x + sp.I*y + cart_value = x + sp.I * y self.raw_value = ( sp.Abs(cart_value), sp.arg(cart_value) if y != 0 else 0, ) - - self.sync_prop("coord_sys", context) + + self.sync_prop('coord_sys', context) + #################### # - Socket Configuration #################### class ComplexNumberSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.ComplexNumber - + default_value: SympyExpr = sp.S(0 + 0j) - coord_sys: typ.Literal["CARTESIAN", "POLAR"] = "CARTESIAN" - + 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 #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/integer_number.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/integer_number.py index d964161..b08255f 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/integer_number.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/integer_number.py @@ -6,55 +6,56 @@ import pydantic as pyd from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class IntegerNumberBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.IntegerNumber - bl_label = "Integer Number" - + bl_label = 'Integer Number' + #################### # - Properties #################### raw_value: bpy.props.IntProperty( - name="Integer", - description="Represents an integer", + name='Integer', + description='Represents an integer', default=0, - update=(lambda self, context: self.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('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="") - + 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(pyd.BaseModel): 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 -] +BL_REGISTER = [IntegerNumberBLSocket] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/rational_number.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/rational_number.py index d916fe6..d8da3e5 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/rational_number.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/rational_number.py @@ -8,32 +8,33 @@ from .....utils.pydantic_sympy import SympyExpr from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class RationalNumberBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.RationalNumber - bl_label = "Rational Number" - + bl_label = 'Rational Number' + #################### # - Properties #################### raw_value: bpy.props.IntVectorProperty( - name="Rational Number", - description="Represents a rational number (int / int)", + name='Rational Number', + description='Represents a rational number (int / int)', size=2, default=(1, 1), subtype='NONE', - update=(lambda self, context: self.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('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="") - + col_row.prop(self, 'raw_value', text='') + #################### # - Default Value #################### @@ -41,7 +42,7 @@ class RationalNumberBLSocket(base.MaxwellSimSocket): 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): @@ -52,17 +53,19 @@ class RationalNumberBLSocket(base.MaxwellSimSocket): else: self.raw_value = (value.p, value.q) + #################### # - Socket Configuration #################### class RationalNumberSocketDef(pyd.BaseModel): 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 #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/real_number.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/real_number.py index 53df059..16a5225 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/real_number.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/real_number.py @@ -8,38 +8,39 @@ from .....utils.pydantic_sympy import SympyExpr from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class RealNumberBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.RealNumber - bl_label = "Real Number" - + bl_label = 'Real Number' + #################### # - Properties #################### raw_value: bpy.props.FloatProperty( - name="Real Number", - description="Represents a real number", + name='Real Number', + description='Represents a real number', default=0.0, precision=6, - update=(lambda self, context: self.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('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="") - + 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): @@ -47,17 +48,19 @@ class RealNumberBLSocket(base.MaxwellSimSocket): else: float(value.n()) + #################### # - Socket Configuration #################### class RealNumberSocketDef(pyd.BaseModel): 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 #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/__init__.py index 4adf964..75d4d21 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/__init__.py @@ -1,64 +1,65 @@ from . import unit_system + PhysicalUnitSystemSocketDef = unit_system.PhysicalUnitSystemSocketDef from . import time + PhysicalTimeSocketDef = time.PhysicalTimeSocketDef from . import angle + PhysicalAngleSocketDef = angle.PhysicalAngleSocketDef from . import length from . import area from . import 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 speed from . import accel_scalar from . import force_scalar + PhysicalSpeedSocketDef = speed.PhysicalSpeedSocketDef PhysicalAccelScalarSocketDef = accel_scalar.PhysicalAccelScalarSocketDef PhysicalForceScalarSocketDef = force_scalar.PhysicalForceScalarSocketDef from . import pol + PhysicalPolSocketDef = pol.PhysicalPolSocketDef from . import freq + PhysicalFreqSocketDef = freq.PhysicalFreqSocketDef BL_REGISTER = [ *unit_system.BL_REGISTER, - *time.BL_REGISTER, - *angle.BL_REGISTER, - *length.BL_REGISTER, *area.BL_REGISTER, *volume.BL_REGISTER, - *point_3d.BL_REGISTER, - *size_3d.BL_REGISTER, - *mass.BL_REGISTER, - *speed.BL_REGISTER, *accel_scalar.BL_REGISTER, *force_scalar.BL_REGISTER, - *pol.BL_REGISTER, - *freq.BL_REGISTER, ] diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/accel_scalar.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/accel_scalar.py index 7ea03ef..fe51641 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/accel_scalar.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/accel_scalar.py @@ -8,54 +8,57 @@ from .....utils.pydantic_sympy import SympyExpr from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class PhysicalAccelScalarBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.PhysicalAccelScalar - bl_label = "Accel Scalar" + bl_label = 'Accel Scalar' use_units = True - + #################### # - Properties #################### raw_value: bpy.props.FloatProperty( - name="Unitless Acceleration", - description="Represents the unitless part of the acceleration", + name='Unitless Acceleration', + description='Represents the unitless part of the acceleration', default=0.0, precision=6, - update=(lambda self, context: self.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - Socket UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "raw_value", text="") - + 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(pyd.BaseModel): 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 #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/angle.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/angle.py index 25706ac..ee67b5f 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/angle.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/angle.py @@ -8,54 +8,57 @@ from .....utils.pydantic_sympy import SympyExpr from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class PhysicalAngleBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.PhysicalAngle - bl_label = "Physical Angle" + bl_label = 'Physical Angle' use_units = True - + #################### # - Properties #################### raw_value: bpy.props.FloatProperty( - name="Unitless Acceleration", - description="Represents the unitless part of the acceleration", + name='Unitless Acceleration', + description='Represents the unitless part of the acceleration', default=0.0, precision=4, - update=(lambda self, context: self.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - Socket UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "raw_value", text="") - + 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(pyd.BaseModel): 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 #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/area.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/area.py index 44d91c2..b9ea199 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/area.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/area.py @@ -9,28 +9,29 @@ from .....utils.pydantic_sympy import SympyExpr from .. import base from ... import contracts as ct + class PhysicalAreaBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.PhysicalArea - bl_label = "Physical Area" + bl_label = 'Physical Area' use_units = True - + #################### # - Properties #################### raw_value: bpy.props.FloatProperty( - name="Unitless Area", - description="Represents the unitless part of the area", + name='Unitless Area', + description='Represents the unitless part of the area', default=0.0, precision=6, - update=(lambda self, context: self.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - Socket UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "raw_value", text="") - + col.prop(self, 'raw_value', text='') + #################### # - Computation of Default Value #################### @@ -38,34 +39,36 @@ class PhysicalAreaBLSocket(base.MaxwellSimSocket): 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(pyd.BaseModel): 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 #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/force_scalar.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/force_scalar.py index d12947e..3ae7ed0 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/force_scalar.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/force_scalar.py @@ -8,54 +8,57 @@ from .....utils.pydantic_sympy import SympyExpr from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class PhysicalForceScalarBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.PhysicalForceScalar - bl_label = "Force Scalar" + bl_label = 'Force Scalar' use_units = True - + #################### # - Properties #################### raw_value: bpy.props.FloatProperty( - name="Unitless Force", - description="Represents the unitless part of the force", + name='Unitless Force', + description='Represents the unitless part of the force', default=0.0, precision=6, - update=(lambda self, context: self.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - Socket UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "raw_value", text="") - + 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(pyd.BaseModel): 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 #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/freq.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/freq.py index 9c2f42f..7a9548b 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/freq.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/freq.py @@ -12,122 +12,120 @@ from .....utils.pydantic_sympy import SympyExpr from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class PhysicalFreqBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.PhysicalFreq - bl_label = "Frequency" + bl_label = 'Frequency' use_units = True - + #################### # - Properties #################### raw_value: bpy.props.FloatProperty( - name="Unitless Frequency", - description="Represents the unitless part of the frequency", + name='Unitless Frequency', + description='Represents the unitless part of the frequency', default=0.0, precision=6, - update=(lambda self, context: self.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + min_freq: bpy.props.FloatProperty( - name="Min Frequency", - description="Lowest frequency", + name='Min Frequency', + description='Lowest frequency', default=0.0, precision=4, - update=(lambda self, context: self.sync_prop("min_freq", context)), + update=(lambda self, context: self.sync_prop('min_freq', context)), ) max_freq: bpy.props.FloatProperty( - name="Max Frequency", - description="Highest frequency", + name='Max Frequency', + description='Highest frequency', default=0.0, precision=4, - update=(lambda self, context: self.sync_prop("max_freq", context)), + update=(lambda self, context: self.sync_prop('max_freq', context)), ) steps: bpy.props.IntProperty( - name="Frequency Steps", - description="# of steps between min and max", + name='Frequency Steps', + description='# of steps between min and max', default=2, - update=(lambda self, context: self.sync_prop("steps", context)), + update=(lambda self, context: self.sync_prop('steps', context)), ) - + #################### # - Socket UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "raw_value", text="") - + col.prop(self, 'raw_value', text='') + def draw_value_list(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") - + 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 = spu.convert_to(value, self.unit) / self.unit - + @property def value_list(self) -> list[SympyExpr]: return [ el * self.unit for el in np.linspace(self.min_freq, self.max_freq, self.steps) ] + @value_list.setter def value_list(self, value: tuple[SympyExpr, SympyExpr, int]): - self.min_freq, self.max_freq, self.steps = [ - spu.convert_to(el, self.unit) / self.unit - for el in value[:2] + self.min_freq, self.max_freq, self.steps = [ + spu.convert_to(el, self.unit) / self.unit for el in value[:2] ] + [value[2]] - + def sync_unit_change(self) -> None: if self.is_list: self.value_list = ( - spu.convert_to( - self.min_freq * self.prev_unit, - self.unit - ), - spu.convert_to( - self.max_freq * self.prev_unit, - self.unit - ), + spu.convert_to(self.min_freq * self.prev_unit, self.unit), + spu.convert_to(self.max_freq * self.prev_unit, self.unit), self.steps, ) else: self.value = self.value / self.unit * self.prev_unit - + self.prev_active_unit = self.active_unit + #################### # - Socket Configuration #################### class PhysicalFreqSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.PhysicalFreq - - default_value: SympyExpr = 500*spux.terahertz + + default_value: SympyExpr = 500 * spux.terahertz default_unit: SympyExpr | None = None is_list: bool = False - - min_freq: SympyExpr = 400.0*spux.terahertz - max_freq: SympyExpr = 600.0*spux.terahertz + + min_freq: SympyExpr = 400.0 * spux.terahertz + max_freq: SympyExpr = 600.0 * spux.terahertz steps: SympyExpr = 50 - + def init(self, bl_socket: PhysicalFreqBLSocket) -> None: bl_socket.value = self.default_value bl_socket.is_list = self.is_list - + if self.default_unit: bl_socket.unit = self.default_unit - + if self.is_list: bl_socket.value_list = (self.min_freq, self.max_freq, self.steps) + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/length.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/length.py index 547ed64..757f9f1 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/length.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/length.py @@ -9,122 +9,120 @@ from .....utils.pydantic_sympy import SympyExpr from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class PhysicalLengthBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.PhysicalLength - bl_label = "PhysicalLength" + bl_label = 'PhysicalLength' use_units = True - + #################### # - Properties #################### raw_value: bpy.props.FloatProperty( - name="Unitless Force", - description="Represents the unitless part of the force", + name='Unitless Force', + description='Represents the unitless part of the force', default=0.0, precision=6, - update=(lambda self, context: self.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + min_len: bpy.props.FloatProperty( - name="Min Length", - description="Lowest length", + name='Min Length', + description='Lowest length', default=0.0, precision=4, - update=(lambda self, context: self.sync_prop("min_len", context)), + update=(lambda self, context: self.sync_prop('min_len', context)), ) max_len: bpy.props.FloatProperty( - name="Max Length", - description="Highest length", + name='Max Length', + description='Highest length', default=0.0, precision=4, - update=(lambda self, context: self.sync_prop("max_len", context)), + update=(lambda self, context: self.sync_prop('max_len', context)), ) steps: bpy.props.IntProperty( - name="Length Steps", - description="# of steps between min and max", + name='Length Steps', + description='# of steps between min and max', default=2, - update=(lambda self, context: self.sync_prop("steps", context)), + update=(lambda self, context: self.sync_prop('steps', context)), ) - + #################### # - Socket UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "raw_value", text="") - + col.prop(self, 'raw_value', text='') + def draw_value_list(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") - + 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 = spu.convert_to(value, self.unit) / self.unit - + @property def value_list(self) -> list[SympyExpr]: return [ el * self.unit for el in np.linspace(self.min_len, self.max_len, self.steps) ] + @value_list.setter def value_list(self, value: tuple[SympyExpr, SympyExpr, int]): self.min_len, self.max_len, self.steps = [ - spu.convert_to(el, self.unit) / self.unit - for el in value[:2] + spu.convert_to(el, self.unit) / self.unit for el in value[:2] ] + [value[2]] - + def sync_unit_change(self) -> None: if self.is_list: self.value_list = ( - spu.convert_to( - self.min_len * self.prev_unit, - self.unit - ), - spu.convert_to( - self.max_len * self.prev_unit, - self.unit - ), + spu.convert_to(self.min_len * self.prev_unit, self.unit), + spu.convert_to(self.max_len * self.prev_unit, self.unit), self.steps, ) else: self.value = self.value / self.unit * self.prev_unit - + self.prev_active_unit = self.active_unit + #################### # - Socket Configuration #################### class PhysicalLengthSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.PhysicalLength - - default_value: SympyExpr = 1*spu.um + + default_value: SympyExpr = 1 * spu.um default_unit: SympyExpr | None = None is_list: bool = False - - min_len: SympyExpr = 400.0*spu.nm - max_len: SympyExpr = 600.0*spu.nm + + min_len: SympyExpr = 400.0 * spu.nm + max_len: SympyExpr = 600.0 * spu.nm steps: SympyExpr = 50 - + def init(self, bl_socket: PhysicalLengthBLSocket) -> None: bl_socket.value = self.default_value bl_socket.is_list = self.is_list - + if self.default_unit: bl_socket.unit = self.default_unit - + if self.is_list: bl_socket.value_list = (self.min_len, self.max_len, self.steps) + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/mass.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/mass.py index fd16ba2..ab90818 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/mass.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/mass.py @@ -8,38 +8,39 @@ from .....utils.pydantic_sympy import SympyExpr from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class PhysicalMassBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.PhysicalMass - bl_label = "Mass" + bl_label = 'Mass' use_units = True - + #################### # - Properties #################### raw_value: bpy.props.FloatProperty( - name="Unitless Mass", - description="Represents the unitless part of mass", + name='Unitless Mass', + description='Represents the unitless part of mass', default=0.0, precision=6, - update=(lambda self, context: self.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - Socket UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "raw_value", text="") - + 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 @@ -50,12 +51,13 @@ class PhysicalMassBLSocket(base.MaxwellSimSocket): #################### class PhysicalMassSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.PhysicalMass - + default_unit: SympyExpr | None = None - + def init(self, bl_socket: PhysicalMassBLSocket) -> None: pass + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/point_3d.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/point_3d.py index fd9d73d..396b2bd 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/point_3d.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/point_3d.py @@ -9,52 +9,55 @@ from .....utils.pydantic_sympy import SympyExpr from .. import base from ... import contracts as ct + class PhysicalPoint3DBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.PhysicalPoint3D - bl_label = "Volume" + 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", + 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.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - Socket UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "raw_value", text="") - + 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(pyd.BaseModel): 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 #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/pol.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/pol.py index 8dcb8a1..5a6eca9 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/pol.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/pol.py @@ -12,155 +12,161 @@ from ... import contracts as ct StokesVector = SympyExpr + class PhysicalPolBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.PhysicalPol - bl_label = "Polarization" + bl_label = 'Polarization' use_units = True - - + def radianize(self, ang): - return spu.convert_to( - ang * self.unit, - spu.radian, - ) / spu.radian - + return ( + spu.convert_to( + ang * self.unit, + spu.radian, + ) + / spu.radian + ) + #################### # - Properties #################### model: bpy.props.EnumProperty( - name="Polarization Model", - description="A choice of polarization representation", + name='Polarization Model', + description='A choice of polarization representation', items=[ - ("UNPOL", "Unpolarized", "Unpolarized"), - ("LIN_ANG", "Linear", "Linearly polarized at angle"), - ("CIRC", "Circular", "Linearly polarized at angle"), - ("JONES", "Jones", "Polarized waves described by Jones vector"), - ("STOKES", "Stokes", "Linear x-pol of field"), + ('UNPOL', 'Unpolarized', 'Unpolarized'), + ('LIN_ANG', 'Linear', 'Linearly polarized at angle'), + ('CIRC', 'Circular', 'Linearly polarized at angle'), + ('JONES', 'Jones', 'Polarized waves described by Jones vector'), + ('STOKES', 'Stokes', 'Linear x-pol of field'), ], - default="UNPOL", - update=(lambda self, context: self.sync_prop("model", context)), + default='UNPOL', + update=(lambda self, context: self.sync_prop('model', context)), ) - + ## Lin Ang lin_ang: bpy.props.FloatProperty( - name="Pol. Angle", - description="Angle to polarize linearly along", + name='Pol. Angle', + description='Angle to polarize linearly along', default=0.0, - update=(lambda self, context: self.sync_prop("lin_ang", context)), + update=(lambda self, context: self.sync_prop('lin_ang', context)), ) ## Circ circ: bpy.props.EnumProperty( - name="Pol. Orientation", - description="LCP or RCP", + name='Pol. Orientation', + description='LCP or RCP', items=[ - ("LCP", "LCP", "'Left Circular Polarization'"), - ("RCP", "RCP", "'Right Circular Polarization'"), + ('LCP', 'LCP', "'Left Circular Polarization'"), + ('RCP', 'RCP', "'Right Circular Polarization'"), ], - default="LCP", - update=(lambda self, context: self.sync_prop("circ", context)), + default='LCP', + update=(lambda self, context: self.sync_prop('circ', context)), ) ## Jones jones_psi: bpy.props.FloatProperty( - name="Jones X-Axis Angle", - description="Angle of the ellipse to the x-axis", + name='Jones X-Axis Angle', + description='Angle of the ellipse to the x-axis', default=0.0, precision=2, - update=(lambda self, context: self.sync_prop("jones_psi", context)), + update=(lambda self, context: self.sync_prop('jones_psi', context)), ) jones_chi: bpy.props.FloatProperty( - name="Jones Major-Axis-Adjacent Angle", - description="Angle of adjacent to the ellipse major axis", + name='Jones Major-Axis-Adjacent Angle', + description='Angle of adjacent to the ellipse major axis', default=0.0, precision=2, - update=(lambda self, context: self.sync_prop("jones_chi", context)), + update=(lambda self, context: self.sync_prop('jones_chi', context)), ) - + ## Stokes stokes_psi: bpy.props.FloatProperty( - name="Stokes X-Axis Angle", - description="Angle of the ellipse to the x-axis", + name='Stokes X-Axis Angle', + description='Angle of the ellipse to the x-axis', default=0.0, precision=2, - update=(lambda self, context: self.sync_prop("stokes_psi", context)), + update=(lambda self, context: self.sync_prop('stokes_psi', context)), ) stokes_chi: bpy.props.FloatProperty( - name="Stokes Major-Axis-Adjacent Angle", - description="Angle of adjacent to the ellipse major axis", + name='Stokes Major-Axis-Adjacent Angle', + description='Angle of adjacent to the ellipse major axis', default=0.0, precision=2, - update=(lambda self, context: self.sync_prop("stokes_chi", context)), + update=(lambda self, context: self.sync_prop('stokes_chi', context)), ) stokes_p: bpy.props.FloatProperty( - name="Stokes Polarization Degree", - description="The degree of polarization", + name='Stokes Polarization Degree', + description='The degree of polarization', default=0.0, precision=2, - update=(lambda self, context: self.sync_prop("stokes_p", context)), + update=(lambda self, context: self.sync_prop('stokes_p', context)), ) stokes_I: bpy.props.FloatProperty( - name="Stokes Field Intensity", - description="The intensity of the polarized field", + name='Stokes Field Intensity', + description='The intensity of the polarized field', default=0.0, precision=2, - update=(lambda self, context: self.sync_prop("stokes_I", context)), + update=(lambda self, context: self.sync_prop('stokes_I', context)), ) ## TODO: Units? - + #################### # - UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "model", text="") - - if self.model == "LIN_ANG": - col.prop(self, "lin_ang", text="") - - elif self.model == "CIRC": - col.prop(self, "circ", text="") - - elif self.model == "JONES": + col.prop(self, 'model', text='') + + if self.model == 'LIN_ANG': + col.prop(self, 'lin_ang', text='') + + elif self.model == 'CIRC': + col.prop(self, 'circ', text='') + + elif self.model == 'JONES': split = col.split(factor=0.2, align=True) - + col = split.column(align=False) - col.label(text="ψ,χ") - + col.label(text='ψ,χ') + col = split.row(align=True) - col.prop(self, "jones_psi", text="") - col.prop(self, "jones_chi", text="") - - elif self.model == "STOKES": + col.prop(self, 'jones_psi', text='') + col.prop(self, 'jones_chi', text='') + + elif self.model == 'STOKES': split = col.split(factor=0.2, align=True) # Split #1 col = split.column(align=False) - col.label(text="ψ,χ") - col.label(text="p,I") - + col.label(text='ψ,χ') + col.label(text='p,I') + # Split #2 col = split.column(align=False) row = col.row(align=True) - row.prop(self, "stokes_psi", text="") - row.prop(self, "stokes_chi", text="") - + row.prop(self, 'stokes_psi', text='') + row.prop(self, 'stokes_chi', text='') + row = col.row(align=True) - row.prop(self, "stokes_p", text="") - row.prop(self, "stokes_I", text="") - + row.prop(self, 'stokes_p', text='') + row.prop(self, 'stokes_I', text='') + ## TODO: Visualize stokes vector as oriented plane wave shape in plot (direct), in 3D (maybe in combination with a source), and on Poincare sphere. - + #################### # - Default Value #################### @property def _value_unpol(self) -> StokesVector: return spo_pol.stokes_vector(0, 0, 0) + @property def _value_lin_ang(self) -> StokesVector: return spo_pol.stokes_vector(self.radianize(self.lin_ang), 0, 0) + @property def _value_circ(self) -> StokesVector: return { - "RCP": spo_pol.stokes_vector(0, sp.pi/4, 0), - "LCP": spo_pol.stokes_vector(0, -sp.pi/4, 0), + 'RCP': spo_pol.stokes_vector(0, sp.pi / 4, 0), + 'LCP': spo_pol.stokes_vector(0, -sp.pi / 4, 0), }[self.circ] + @property def _value_jones(self) -> StokesVector: return spo_pol.jones_2_stokes( @@ -169,6 +175,7 @@ class PhysicalPolBLSocket(base.MaxwellSimSocket): self.radianize(self.jones_chi), ) ) + @property def _value_stokes(self) -> StokesVector: return spo_pol.stokes_vector( @@ -177,51 +184,68 @@ class PhysicalPolBLSocket(base.MaxwellSimSocket): self.stokes_p, self.stokes_I, ) - + @property def value(self) -> StokesVector: return { - "UNPOL": self._value_unpol, - "LIN_ANG": self._value_lin_ang, - "CIRC": self._value_circ, - "JONES": self._value_jones, - "STOKES": self._value_stokes, + 'UNPOL': self._value_unpol, + 'LIN_ANG': self._value_lin_ang, + 'CIRC': self._value_circ, + 'JONES': self._value_jones, + 'STOKES': self._value_stokes, }[self.model] - + def sync_unit_change(self) -> None: """We don't have a setter, so we need to manually implement the unit change operation.""" - self.lin_ang = spu.convert_to( - self.lin_ang * self.prev_unit, - self.unit, - ) / self.unit - self.jones_psi = spu.convert_to( - self.jones_psi * self.prev_unit, - self.unit, - ) / self.unit - self.jones_chi = spu.convert_to( - self.jones_chi * self.prev_unit, - self.unit, - ) / self.unit - self.stokes_psi = spu.convert_to( - self.stokes_psi * self.prev_unit, - self.unit, - ) / self.unit - self.stokes_chi = spu.convert_to( - self.stokes_chi * self.prev_unit, - self.unit, - ) / self.unit - + self.lin_ang = ( + spu.convert_to( + self.lin_ang * self.prev_unit, + self.unit, + ) + / self.unit + ) + self.jones_psi = ( + spu.convert_to( + self.jones_psi * self.prev_unit, + self.unit, + ) + / self.unit + ) + self.jones_chi = ( + spu.convert_to( + self.jones_chi * self.prev_unit, + self.unit, + ) + / self.unit + ) + self.stokes_psi = ( + spu.convert_to( + self.stokes_psi * self.prev_unit, + self.unit, + ) + / self.unit + ) + self.stokes_chi = ( + spu.convert_to( + self.stokes_chi * self.prev_unit, + self.unit, + ) + / self.unit + ) + self.prev_active_unit = self.active_unit + #################### # - Socket Configuration #################### class PhysicalPolSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.PhysicalPol - + def init(self, bl_socket: PhysicalPolBLSocket) -> None: pass + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/size_3d.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/size_3d.py index 9b9e068..110df09 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/size_3d.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/size_3d.py @@ -9,54 +9,57 @@ from .....utils.pydantic_sympy import SympyExpr from .. import base from ... import contracts as ct + class PhysicalSize3DBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.PhysicalSize3D - bl_label = "3D Size" + 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", + 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.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - Socket UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "raw_value", text="") - + 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(pyd.BaseModel): 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 #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/speed.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/speed.py index 1e5d2b7..1240904 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/speed.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/speed.py @@ -8,54 +8,57 @@ from .....utils.pydantic_sympy import SympyExpr from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class PhysicalSpeedBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.PhysicalSpeed - bl_label = "Speed" + bl_label = 'Speed' use_units = True - + #################### # - Properties #################### raw_value: bpy.props.FloatProperty( - name="Unitless Speed", - description="Represents the unitless part of the speed", + name='Unitless Speed', + description='Represents the unitless part of the speed', default=0.0, precision=6, - update=(lambda self, context: self.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - Socket UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "raw_value", text="") - + 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(pyd.BaseModel): 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 #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/time.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/time.py index 25820b6..244d124 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/time.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/time.py @@ -8,57 +8,60 @@ from .....utils.pydantic_sympy import SympyExpr from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class PhysicalTimeBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.PhysicalTime - bl_label = "Time" + bl_label = 'Time' use_units = True - + #################### # - Properties #################### raw_value: bpy.props.FloatProperty( - name="Unitless Time", - description="Represents the unitless part of time", + name='Unitless Time', + description='Represents the unitless part of time', default=0.0, precision=4, - update=(lambda self, context: self.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - Socket UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "raw_value", text="") - + 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(pyd.BaseModel): 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 #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/unit_system.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/unit_system.py index 3066c5b..4fb985f 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/unit_system.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/unit_system.py @@ -10,235 +10,244 @@ from .. import base from ... import contracts as ct ST = ct.SocketType -SU = lambda socket_type: ct.SOCKET_UNITS[ - socket_type -]["values"] +SU = lambda socket_type: ct.SOCKET_UNITS[socket_type]['values'] + #################### # - Utilities #################### -def contract_units_to_items( - socket_type: ST -) -> list[tuple[str, str, str]]: - """For a given socket type, get a bpy.props.EnumProperty-compatible list-tuple of items to display in the enum dropdown menu. - """ +def contract_units_to_items(socket_type: ST) -> list[tuple[str, str, str]]: + """For a given socket type, get a bpy.props.EnumProperty-compatible list-tuple of items to display in the enum dropdown menu.""" return [ ( unit_key, str(unit), - f"{socket_type}-compatible unit", + f'{socket_type}-compatible unit', ) for unit_key, unit in SU(socket_type).items() ] + def default_unit_key_for(socket_type: ST) -> str: - """For a given socket type, return the default key corresponding to the default unit. - """ - return ct.SOCKET_UNITS[ - socket_type - ]["default"] + """For a given socket type, return the default key corresponding to the default unit.""" + return ct.SOCKET_UNITS[socket_type]['default'] + #################### # - Blender Socket #################### class PhysicalUnitSystemBLSocket(base.MaxwellSimSocket): socket_type = ST.PhysicalUnitSystem - bl_label = "Unit System" - + bl_label = 'Unit System' + #################### # - Properties #################### show_definition: bpy.props.BoolProperty( - name="Show Unit System Definition", - description="Toggle to show unit system definition", + name='Show Unit System Definition', + description='Toggle to show unit system definition', default=False, - update=(lambda self, context: self.sync_prop("show_definition", context)), + update=( + lambda self, context: self.sync_prop('show_definition', context) + ), ) - + unit_time: bpy.props.EnumProperty( - name="Time Unit", - description="Unit of time", + name='Time Unit', + description='Unit of time', items=contract_units_to_items(ST.PhysicalTime), default=default_unit_key_for(ST.PhysicalTime), - update=(lambda self, context: self.sync_prop("unit_time", context)), + update=(lambda self, context: self.sync_prop('unit_time', context)), ) - + unit_angle: bpy.props.EnumProperty( - name="Angle Unit", - description="Unit of angle", + name='Angle Unit', + description='Unit of angle', items=contract_units_to_items(ST.PhysicalAngle), default=default_unit_key_for(ST.PhysicalAngle), - update=(lambda self, context: self.sync_prop("unit_angle", context)), + update=(lambda self, context: self.sync_prop('unit_angle', context)), ) - + unit_length: bpy.props.EnumProperty( - name="Length Unit", - description="Unit of length", + name='Length Unit', + description='Unit of length', items=contract_units_to_items(ST.PhysicalLength), default=default_unit_key_for(ST.PhysicalLength), - update=(lambda self, context: self.sync_prop("unit_length", context)), + update=(lambda self, context: self.sync_prop('unit_length', context)), ) unit_area: bpy.props.EnumProperty( - name="Area Unit", - description="Unit of area", + name='Area Unit', + description='Unit of area', items=contract_units_to_items(ST.PhysicalArea), default=default_unit_key_for(ST.PhysicalArea), - update=(lambda self, context: self.sync_prop("unit_area", context)), + update=(lambda self, context: self.sync_prop('unit_area', context)), ) unit_volume: bpy.props.EnumProperty( - name="Volume Unit", - description="Unit of time", + name='Volume Unit', + description='Unit of time', items=contract_units_to_items(ST.PhysicalVolume), default=default_unit_key_for(ST.PhysicalVolume), - update=(lambda self, context: self.sync_prop("unit_volume", context)), + update=(lambda self, context: self.sync_prop('unit_volume', context)), ) - + unit_point_2d: bpy.props.EnumProperty( - name="Point2D Unit", - description="Unit of 2D points", + name='Point2D Unit', + description='Unit of 2D points', items=contract_units_to_items(ST.PhysicalPoint2D), default=default_unit_key_for(ST.PhysicalPoint2D), - update=(lambda self, context: self.sync_prop("unit_point_2d", context)), + update=( + lambda self, context: self.sync_prop('unit_point_2d', context) + ), ) unit_point_3d: bpy.props.EnumProperty( - name="Point3D Unit", - description="Unit of 3D points", + name='Point3D Unit', + description='Unit of 3D points', items=contract_units_to_items(ST.PhysicalPoint3D), default=default_unit_key_for(ST.PhysicalPoint3D), - update=(lambda self, context: self.sync_prop("unit_point_3d", context)), + update=( + lambda self, context: self.sync_prop('unit_point_3d', context) + ), ) - + unit_size_2d: bpy.props.EnumProperty( - name="Size2D Unit", - description="Unit of 2D sizes", + name='Size2D Unit', + description='Unit of 2D sizes', items=contract_units_to_items(ST.PhysicalSize2D), default=default_unit_key_for(ST.PhysicalSize2D), - update=(lambda self, context: self.sync_prop("unit_size_2d", context)), + update=(lambda self, context: self.sync_prop('unit_size_2d', context)), ) unit_size_3d: bpy.props.EnumProperty( - name="Size3D Unit", - description="Unit of 3D sizes", + name='Size3D Unit', + description='Unit of 3D sizes', items=contract_units_to_items(ST.PhysicalSize3D), default=default_unit_key_for(ST.PhysicalSize3D), - update=(lambda self, context: self.sync_prop("unit_size_3d", context)), + update=(lambda self, context: self.sync_prop('unit_size_3d', context)), ) - + unit_mass: bpy.props.EnumProperty( - name="Mass Unit", - description="Unit of mass", + name='Mass Unit', + description='Unit of mass', items=contract_units_to_items(ST.PhysicalMass), default=default_unit_key_for(ST.PhysicalMass), - update=(lambda self, context: self.sync_prop("unit_mass", context)), + update=(lambda self, context: self.sync_prop('unit_mass', context)), ) - + unit_speed: bpy.props.EnumProperty( - name="Speed Unit", - description="Unit of speed", + name='Speed Unit', + description='Unit of speed', items=contract_units_to_items(ST.PhysicalSpeed), default=default_unit_key_for(ST.PhysicalSpeed), - update=(lambda self, context: self.sync_prop("unit_speed", context)), + update=(lambda self, context: self.sync_prop('unit_speed', context)), ) unit_accel_scalar: bpy.props.EnumProperty( - name="Accel Unit", - description="Unit of acceleration", + name='Accel Unit', + description='Unit of acceleration', items=contract_units_to_items(ST.PhysicalAccelScalar), default=default_unit_key_for(ST.PhysicalAccelScalar), - update=(lambda self, context: self.sync_prop("unit_accel_scalar", context)), + update=( + lambda self, context: self.sync_prop('unit_accel_scalar', context) + ), ) unit_force_scalar: bpy.props.EnumProperty( - name="Force Scalar Unit", - description="Unit of scalar force", + name='Force Scalar Unit', + description='Unit of scalar force', items=contract_units_to_items(ST.PhysicalForceScalar), default=default_unit_key_for(ST.PhysicalForceScalar), - update=(lambda self, context: self.sync_prop("unit_force_scalar", context)), + update=( + lambda self, context: self.sync_prop('unit_force_scalar', context) + ), ) unit_accel_3d: bpy.props.EnumProperty( - name="Accel3D Unit", - description="Unit of 3D vector acceleration", + name='Accel3D Unit', + description='Unit of 3D vector acceleration', items=contract_units_to_items(ST.PhysicalAccel3D), default=default_unit_key_for(ST.PhysicalAccel3D), - update=(lambda self, context: self.sync_prop("unit_accel_3d", context)), + update=( + lambda self, context: self.sync_prop('unit_accel_3d', context) + ), ) unit_force_3d: bpy.props.EnumProperty( - name="Force3D Unit", - description="Unit of 3D vector force", + name='Force3D Unit', + description='Unit of 3D vector force', items=contract_units_to_items(ST.PhysicalForce3D), default=default_unit_key_for(ST.PhysicalForce3D), - update=(lambda self, context: self.sync_prop("unit_force_3d", context)), + update=( + lambda self, context: self.sync_prop('unit_force_3d', context) + ), ) - + unit_freq: bpy.props.EnumProperty( - name="Freq Unit", - description="Unit of frequency", + name='Freq Unit', + description='Unit of frequency', items=contract_units_to_items(ST.PhysicalFreq), default=default_unit_key_for(ST.PhysicalFreq), - update=(lambda self, context: self.sync_prop("unit_freq", context)), + update=(lambda self, context: self.sync_prop('unit_freq', context)), ) - + #################### # - UI #################### def draw_label_row(self, row: bpy.types.UILayout, text) -> None: row.label(text=text) row.prop( - self, "show_definition", toggle=True, text="", icon="MOD_LENGTH" + self, 'show_definition', toggle=True, text='', icon='MOD_LENGTH' ) - + def draw_value(self, col: bpy.types.UILayout) -> None: if self.show_definition: # TODO: We need panels instead of rows!! - col_row=col.row(align=False) - col_row.alignment = "EXPAND" - col_row.prop(self, "unit_time", text="") - col_row.prop(self, "unit_angle", text="") - - col_row=col.row(align=False) - col_row.alignment = "EXPAND" - col_row.prop(self, "unit_length", text="") - col_row.prop(self, "unit_area", text="") - col_row.prop(self, "unit_volume", text="") - - col_row=col.row(align=True) - col_row.alignment = "EXPAND" - col_row.label(text="Point") - col_row.prop(self, "unit_point_2d", text="") - col_row.prop(self, "unit_point_3d", text="") - - col_row=col.row(align=True) - col_row.alignment = "EXPAND" - col_row.label(text="Size") - col_row.prop(self, "unit_size_2d", text="") - col_row.prop(self, "unit_size_3d", text="") - - col_row=col.row(align=True) - col_row.alignment = "EXPAND" - col_row.label(text="Mass") - col_row.prop(self, "unit_mass", text="") - - col_row=col.row(align=True) - col_row.alignment = "EXPAND" - col_row.label(text="Vel") - col_row.prop(self, "unit_speed", text="") - #col_row.prop(self, "unit_vel_2d_vector", text="") - #col_row.prop(self, "unit_vel_3d_vector", text="") - - col_row=col.row(align=True) - col_row.label(text="Accel") - col_row.prop(self, "unit_accel_scalar", text="") - #col_row.prop(self, "unit_accel_2d_vector", text="") - col_row.prop(self, "unit_accel_3d", text="") - - col_row=col.row(align=True) - col_row.label(text="Force") - col_row.prop(self, "unit_force_scalar", text="") - #col_row.prop(self, "unit_force_2d_vector", text="") - col_row.prop(self, "unit_force_3d", text="") - - col_row=col.row(align=True) - col_row.alignment = "EXPAND" - col_row.label(text="Freq") - col_row.prop(self, "unit_freq", text="") - + col_row = col.row(align=False) + col_row.alignment = 'EXPAND' + col_row.prop(self, 'unit_time', text='') + col_row.prop(self, 'unit_angle', text='') + + col_row = col.row(align=False) + col_row.alignment = 'EXPAND' + col_row.prop(self, 'unit_length', text='') + col_row.prop(self, 'unit_area', text='') + col_row.prop(self, 'unit_volume', text='') + + col_row = col.row(align=True) + col_row.alignment = 'EXPAND' + col_row.label(text='Point') + col_row.prop(self, 'unit_point_2d', text='') + col_row.prop(self, 'unit_point_3d', text='') + + col_row = col.row(align=True) + col_row.alignment = 'EXPAND' + col_row.label(text='Size') + col_row.prop(self, 'unit_size_2d', text='') + col_row.prop(self, 'unit_size_3d', text='') + + col_row = col.row(align=True) + col_row.alignment = 'EXPAND' + col_row.label(text='Mass') + col_row.prop(self, 'unit_mass', text='') + + col_row = col.row(align=True) + col_row.alignment = 'EXPAND' + col_row.label(text='Vel') + col_row.prop(self, 'unit_speed', text='') + # col_row.prop(self, "unit_vel_2d_vector", text="") + # col_row.prop(self, "unit_vel_3d_vector", text="") + + col_row = col.row(align=True) + col_row.label(text='Accel') + col_row.prop(self, 'unit_accel_scalar', text='') + # col_row.prop(self, "unit_accel_2d_vector", text="") + col_row.prop(self, 'unit_accel_3d', text='') + + col_row = col.row(align=True) + col_row.label(text='Force') + col_row.prop(self, 'unit_force_scalar', text='') + # col_row.prop(self, "unit_force_2d_vector", text="") + col_row.prop(self, 'unit_force_3d', text='') + + col_row = col.row(align=True) + col_row.alignment = 'EXPAND' + col_row.label(text='Freq') + col_row.prop(self, 'unit_freq', text='') + #################### # - Default Value #################### @@ -249,40 +258,36 @@ class PhysicalUnitSystemBLSocket(base.MaxwellSimSocket): for socket_type, socket_unit_prop in [ (ST.PhysicalTime, self.unit_time), (ST.PhysicalAngle, self.unit_angle), - (ST.PhysicalLength, self.unit_length), (ST.PhysicalArea, self.unit_area), (ST.PhysicalVolume, self.unit_volume), - (ST.PhysicalPoint2D, self.unit_point_2d), (ST.PhysicalPoint3D, self.unit_point_3d), - (ST.PhysicalSize2D, self.unit_size_2d), (ST.PhysicalSize3D, self.unit_size_3d), - (ST.PhysicalMass, self.unit_mass), - (ST.PhysicalSpeed, self.unit_speed), (ST.PhysicalAccelScalar, self.unit_accel_scalar), (ST.PhysicalForceScalar, self.unit_force_scalar), (ST.PhysicalAccel3D, self.unit_accel_3d), (ST.PhysicalForce3D, self.unit_force_3d), - (ST.PhysicalFreq, self.unit_freq), ] } + #################### # - Socket Configuration #################### class PhysicalUnitSystemSocketDef(pyd.BaseModel): socket_type: ST = ST.PhysicalUnitSystem - + show_by_default: bool = False - + def init(self, bl_socket: PhysicalUnitSystemBLSocket) -> None: bl_socket.show_definition = self.show_by_default + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/volume.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/volume.py index 2d2f300..2883123 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/volume.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/volume.py @@ -9,50 +9,54 @@ from .....utils.pydantic_sympy import SympyExpr from .. import base from ... import contracts as ct + class PhysicalVolumeBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.PhysicalVolume - bl_label = "Volume" + bl_label = 'Volume' use_units = True - + #################### # - Properties #################### raw_value: bpy.props.FloatProperty( - name="Unitless Volume", - description="Represents the unitless part of the area", + name='Unitless Volume', + description='Represents the unitless part of the area', default=0.0, precision=6, - update=(lambda self, context: self.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - Socket UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "raw_value", text="") + 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(pyd.BaseModel): 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 #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/tidy3d/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/tidy3d/__init__.py index 4183e17..34695b0 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/tidy3d/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/tidy3d/__init__.py @@ -1,4 +1,5 @@ from . import cloud_task + Tidy3DCloudTaskSocketDef = cloud_task.Tidy3DCloudTaskSocketDef diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/tidy3d/cloud_task.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/tidy3d/cloud_task.py index 674d23c..f3388ef 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/tidy3d/cloud_task.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/tidy3d/cloud_task.py @@ -10,124 +10,130 @@ from .....utils import tdcloud from .. import base from ... import contracts as ct + #################### # - Operators #################### class ReloadFolderList(bpy.types.Operator): - bl_idname = "blender_maxwell.sockets__reload_folder_list" - bl_label = "Reload Tidy3D Folder List" - bl_description = "Reload the the cached Tidy3D folder list" - + bl_idname = 'blender_maxwell.sockets__reload_folder_list' + bl_label = 'Reload Tidy3D Folder List' + bl_description = 'Reload the the cached Tidy3D folder list' + @classmethod def poll(cls, context): space = context.space_data return ( tdcloud.IS_AUTHENTICATED - - and hasattr(context, "socket") - and hasattr(context.socket, "socket_type") + and hasattr(context, 'socket') + and hasattr(context.socket, 'socket_type') and context.socket.socket_type == ct.SocketType.Tidy3DCloudTask ) def execute(self, context): socket = context.socket - + tdcloud.TidyCloudFolders.update_folders() tdcloud.TidyCloudTasks.update_tasks(socket.existing_folder_id) - + return {'FINISHED'} + class Authenticate(bpy.types.Operator): - bl_idname = "blender_maxwell.sockets__authenticate" - bl_label = "Authenticate Tidy3D" - bl_description = "Authenticate the Tidy3D Web API from a Cloud Task socket" - + bl_idname = 'blender_maxwell.sockets__authenticate' + bl_label = 'Authenticate Tidy3D' + bl_description = 'Authenticate the Tidy3D Web API from a Cloud Task socket' + @classmethod def poll(cls, context): return ( not tdcloud.IS_AUTHENTICATED - - and hasattr(context, "socket") - and hasattr(context.socket, "socket_type") + and hasattr(context, 'socket') + and hasattr(context.socket, 'socket_type') and context.socket.socket_type == ct.SocketType.Tidy3DCloudTask ) def execute(self, context): bl_socket = context.socket - + if not tdcloud.check_authentication(): tdcloud.authenticate_with_api_key(bl_socket.api_key) - bl_socket.api_key = "" - + bl_socket.api_key = '' + return {'FINISHED'} + #################### # - Socket #################### class Tidy3DCloudTaskBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.Tidy3DCloudTask - bl_label = "Tidy3D Cloud Task" - + bl_label = 'Tidy3D Cloud Task' + use_prelock = True - + #################### # - Properties #################### # Authentication api_key: bpy.props.StringProperty( - name="API Key", - description="API Key for the Tidy3D Cloud", - default="", - options={"SKIP_SAVE"}, - subtype="PASSWORD", + name='API Key', + description='API Key for the Tidy3D Cloud', + default='', + options={'SKIP_SAVE'}, + subtype='PASSWORD', ) - + # Task Existance Presumption should_exist: bpy.props.BoolProperty( - name="Cloud Task Should Exist", - description="Whether or not the cloud task should already exist", + name='Cloud Task Should Exist', + description='Whether or not the cloud task should already exist', default=False, ) - + # Identifiers existing_folder_id: bpy.props.EnumProperty( - name="Folder of Cloud Tasks", - description="An existing folder on the Tidy3D Cloud", + name='Folder of Cloud Tasks', + description='An existing folder on the Tidy3D Cloud', items=lambda self, context: self.retrieve_folders(context), - update=(lambda self, context: self.sync_prop("existing_folder_id", context)), + update=( + lambda self, context: self.sync_prop('existing_folder_id', context) + ), ) existing_task_id: bpy.props.EnumProperty( - name="Existing Cloud Task", - description="An existing task on the Tidy3D Cloud, within the given folder", + name='Existing Cloud Task', + description='An existing task on the Tidy3D Cloud, within the given folder', items=lambda self, context: self.retrieve_tasks(context), - update=(lambda self, context: self.sync_prop("existing_task_id", context)), + update=( + lambda self, context: self.sync_prop('existing_task_id', context) + ), ) - + # (Potential) New Task new_task_name: bpy.props.StringProperty( - name="New Cloud Task Name", - description="Name of a new task to submit to the Tidy3D Cloud", - default="", - update=(lambda self, context: self.sync_prop("new_task_name", context)), + name='New Cloud Task Name', + description='Name of a new task to submit to the Tidy3D Cloud', + default='', + update=( + lambda self, context: self.sync_prop('new_task_name', context) + ), ) - - + #################### # - Property Methods #################### def sync_existing_folder_id(self, context): folder_task_ids = self.retrieve_tasks(context) - + self.existing_task_id = folder_task_ids[0][0] ## There's guaranteed to at least be one element, even if it's "NONE". - - self.sync_prop("existing_folder_id", context) - + + self.sync_prop('existing_folder_id', context) + def retrieve_folders(self, context) -> list[tuple]: folders = tdcloud.TidyCloudFolders.folders() if not folders: - return [("NONE", "None", "No folders")] - + return [('NONE', 'None', 'No folders')] + return [ ( cloud_folder.folder_id, @@ -136,45 +142,51 @@ class Tidy3DCloudTaskBLSocket(base.MaxwellSimSocket): ) for folder_id, cloud_folder in folders.items() ] - + def retrieve_tasks(self, context) -> list[tuple]: - if (cloud_folder := tdcloud.TidyCloudFolders.folders().get( - self.existing_folder_id - )) is None: - return [("NONE", "None", "Folder doesn't exist")] - + if ( + cloud_folder := tdcloud.TidyCloudFolders.folders().get( + self.existing_folder_id + ) + ) is None: + return [('NONE', 'None', "Folder doesn't exist")] + tasks = tdcloud.TidyCloudTasks.tasks(cloud_folder) if not tasks: - return [("NONE", "None", "No tasks in folder")] - + return [('NONE', 'None', 'No tasks in folder')] + return [ ( ## Task ID task.task_id, - ## Task Dropdown Names - " ".join([ - task.taskName, - "(" + task.created_at.astimezone().strftime( - '%y-%m-%d @ %H:%M %Z' - ) + ")", - ]), - + ' '.join( + [ + task.taskName, + '(' + + task.created_at.astimezone().strftime( + '%y-%m-%d @ %H:%M %Z' + ) + + ')', + ] + ), ## Task Description - f"Task Status: {task.status}", - + f'Task Status: {task.status}', ## Status Icon - _icon if (_icon := { - "draft": "SEQUENCE_COLOR_08", - "initialized": "SHADING_SOLID", - "queued": "SEQUENCE_COLOR_03", - "preprocessing": "SEQUENCE_COLOR_02", - "running": "SEQUENCE_COLOR_05", - "postprocessing": "SEQUENCE_COLOR_06", - "success": "SEQUENCE_COLOR_04", - "error": "SEQUENCE_COLOR_01", - }.get(task.status)) else "SEQUENCE_COLOR_09", - + _icon + if ( + _icon := { + 'draft': 'SEQUENCE_COLOR_08', + 'initialized': 'SHADING_SOLID', + 'queued': 'SEQUENCE_COLOR_03', + 'preprocessing': 'SEQUENCE_COLOR_02', + 'running': 'SEQUENCE_COLOR_05', + 'postprocessing': 'SEQUENCE_COLOR_06', + 'success': 'SEQUENCE_COLOR_04', + 'error': 'SEQUENCE_COLOR_01', + }.get(task.status) + ) + else 'SEQUENCE_COLOR_09', ## Unique Number i, ) @@ -186,55 +198,61 @@ class Tidy3DCloudTaskBLSocket(base.MaxwellSimSocket): ) ) ] - + #################### # - Task Sync Methods #################### def sync_created_new_task(self, cloud_task): """Called whenever the task specified in `new_task_name` has been actually created. - + This changes the socket somewhat: Folder/task IDs are set, and the socket is switched to presume that the task exists. - + If the socket is linked, then an error is raised. """ # Propagate along Link if self.is_linked: - msg = f"Cannot sync newly created task to linked Cloud Task socket." + msg = ( + f'Cannot sync newly created task to linked Cloud Task socket.' + ) raise ValueError(msg) ## TODO: A little aggressive. Is there a good use case? - + # Synchronize w/New Task Information self.existing_folder_id = cloud_task.folder_id self.existing_task_id = cloud_task.task_id self.should_exist = True - + def sync_prepare_new_task(self): """Called to switch the socket to no longer presume that the task it specifies exists (yet). - + If the socket is linked, then an error is raised. """ # Propagate along Link if self.is_linked: - msg = f"Cannot sync newly created task to linked Cloud Task socket." + msg = ( + f'Cannot sync newly created task to linked Cloud Task socket.' + ) raise ValueError(msg) ## TODO: A little aggressive. Is there a good use case? - + # Synchronize w/New Task Information self.should_exist = False - + #################### # - Socket UI #################### def draw_label_row(self, row: bpy.types.UILayout, text: str): row.label(text=text) - - auth_icon = "LOCKVIEW_ON" if tdcloud.IS_AUTHENTICATED else "LOCKVIEW_OFF" + + auth_icon = ( + 'LOCKVIEW_ON' if tdcloud.IS_AUTHENTICATED else 'LOCKVIEW_OFF' + ) row.operator( Authenticate.bl_idname, - text="", + text='', icon=auth_icon, ) - + def draw_prelock( self, context: bpy.types.Context, @@ -244,84 +262,96 @@ class Tidy3DCloudTaskBLSocket(base.MaxwellSimSocket): ) -> None: if not tdcloud.IS_AUTHENTICATED: row = col.row() - row.alignment = "CENTER" - row.label(text="Tidy3D API Key") - + row.alignment = 'CENTER' + row.label(text='Tidy3D API Key') + row = col.row() - row.prop(self, "api_key", text="") - + row.prop(self, 'api_key', text='') + row = col.row() row.operator( Authenticate.bl_idname, - text="Connect", + text='Connect', ) - - - + def draw_value(self, col: bpy.types.UILayout) -> None: - if not tdcloud.IS_AUTHENTICATED: return - + if not tdcloud.IS_AUTHENTICATED: + return + # Cloud Folder Selector row = col.row() - row.label(icon="FILE_FOLDER") - row.prop(self, "existing_folder_id", text="") + row.label(icon='FILE_FOLDER') + row.prop(self, 'existing_folder_id', text='') row.operator( ReloadFolderList.bl_idname, - text="", - icon="FILE_REFRESH", + text='', + icon='FILE_REFRESH', ) - + # New Task Name Selector row = col.row() if not self.should_exist: row = col.row() - row.label(icon="NETWORK_DRIVE") - row.prop(self, "new_task_name", text="") - + row.label(icon='NETWORK_DRIVE') + row.prop(self, 'new_task_name', text='') + col.separator(factor=1.0) - + box = col.box() row = box.row() - - row.prop(self, "existing_task_id", text="") - + + row.prop(self, 'existing_task_id', text='') + @property - def value(self) -> tuple[tdcloud.CloudTaskName, tdcloud.CloudFolder] | tdcloud.CloudTask | None: + def value( + self, + ) -> ( + tuple[tdcloud.CloudTaskName, tdcloud.CloudFolder] + | tdcloud.CloudTask + | None + ): # Retrieve Folder ## Authentication is presumed OK - if (cloud_folder := tdcloud.TidyCloudFolders.folders().get( - self.existing_folder_id - )) is None: + if ( + cloud_folder := tdcloud.TidyCloudFolders.folders().get( + self.existing_folder_id + ) + ) is None: msg = "Selected folder doesn't exist (it was probably deleted elsewhere)" raise RuntimeError(msg) - + # No Tasks in Folder ## The UI should set to "NONE" when there are no tasks in a folder - if self.existing_task_id == "NONE": return None - + if self.existing_task_id == 'NONE': + return None + # Retrieve Task if self.should_exist: - if (cloud_task := tdcloud.TidyCloudTasks.tasks( - cloud_folder - ).get(self.existing_task_id)) is None: + if ( + cloud_task := tdcloud.TidyCloudTasks.tasks(cloud_folder).get( + self.existing_task_id + ) + ) is None: msg = "Selected task doesn't exist (it was probably deleted elsewhere)" raise RuntimeError(msg) - + return cloud_task - + return (self.new_task_name, cloud_folder) + #################### # - Socket Configuration #################### class Tidy3DCloudTaskSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.Tidy3DCloudTask - + should_exist: bool - + def init(self, bl_socket: Tidy3DCloudTaskBLSocket) -> None: bl_socket.should_exist = self.should_exist + #################### # - Blender Registration #################### @@ -330,4 +360,3 @@ BL_REGISTER = [ Authenticate, Tidy3DCloudTaskBLSocket, ] - diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/__init__.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/__init__.py index 45f1ccf..d1951bc 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/__init__.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/__init__.py @@ -1,11 +1,13 @@ from . import real_2d_vector from . import complex_2d_vector + Real2DVectorSocketDef = real_2d_vector.Real2DVectorSocketDef Complex2DVectorSocketDef = complex_2d_vector.Complex2DVectorSocketDef from . import integer_3d_vector from . import real_3d_vector from . import complex_3d_vector + Integer3DVectorSocketDef = integer_3d_vector.Integer3DVectorSocketDef Real3DVectorSocketDef = real_3d_vector.Real3DVectorSocketDef Complex3DVectorSocketDef = complex_3d_vector.Complex3DVectorSocketDef @@ -14,7 +16,6 @@ Complex3DVectorSocketDef = complex_3d_vector.Complex3DVectorSocketDef BL_REGISTER = [ *real_2d_vector.BL_REGISTER, *complex_2d_vector.BL_REGISTER, - *integer_3d_vector.BL_REGISTER, *real_3d_vector.BL_REGISTER, *complex_3d_vector.BL_REGISTER, diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_2d_vector.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_2d_vector.py index 6d60fac..ff5226e 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_2d_vector.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_2d_vector.py @@ -5,22 +5,25 @@ import pydantic as pyd from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class Complex2DVectorBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.Complex2DVector - bl_label = "Complex 2D Vector" + bl_label = 'Complex 2D Vector' + #################### # - Socket Configuration #################### class Complex2DVectorSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.Complex2DVector - + def init(self, bl_socket: Complex2DVectorBLSocket) -> None: pass + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_3d_vector.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_3d_vector.py index 15ae9ef..c824ae0 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_3d_vector.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/complex_3d_vector.py @@ -5,22 +5,25 @@ import pydantic as pyd from .. import base from ... import contracts as ct + #################### # - Blender Socket #################### class Complex3DVectorBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.Complex3DVector - bl_label = "Complex 3D Vector" + bl_label = 'Complex 3D Vector' + #################### # - Socket Configuration #################### class Complex3DVectorSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.Complex3DVector - + def init(self, bl_socket: Complex3DVectorBLSocket) -> None: pass + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/integer_3d_vector.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/integer_3d_vector.py index 70ece6f..08e816b 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/integer_3d_vector.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/integer_3d_vector.py @@ -11,57 +11,60 @@ from ... import contracts as ct Integer3DVector = ConstrSympyExpr( allow_variables=False, allow_units=False, - allowed_sets={"integer"}, - allowed_structures={"matrix"}, + 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" - + bl_label = 'Integer 3D Vector' + #################### # - Properties #################### raw_value: bpy.props.IntVectorProperty( - name="Int 3D Vector", - description="Represents an integer 3D (coordinate) vector", + name='Int 3D Vector', + description='Represents an integer 3D (coordinate) vector', size=3, default=(0, 0, 0), - update=(lambda self, context: self.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - Socket UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "raw_value", text="") - + 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(pyd.BaseModel): 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 #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_2d_vector.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_2d_vector.py index 25df158..8c75978 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_2d_vector.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_2d_vector.py @@ -11,57 +11,61 @@ from ... import contracts as ct Real2DVector = ConstrSympyExpr( allow_variables=False, allow_units=False, - allowed_sets={"integer", "rational", "real"}, - allowed_structures={"matrix"}, + 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" - + bl_label = 'Real2DVector' + #################### # - Properties #################### raw_value: bpy.props.FloatVectorProperty( - name="Unitless 2D Vector (global coordinate system)", - description="Represents a real 2D (coordinate) vector", + 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.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - Socket UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "raw_value", text="") - + 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(pyd.BaseModel): 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 #################### diff --git a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_3d_vector.py b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_3d_vector.py index 1bdced1..e9ebf14 100644 --- a/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_3d_vector.py +++ b/src/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_3d_vector.py @@ -11,58 +11,61 @@ from ... import contracts as ct Real3DVector = ConstrSympyExpr( allow_variables=False, allow_units=False, - allowed_sets={"integer", "rational", "real"}, - allowed_structures={"matrix"}, + allowed_sets={'integer', 'rational', 'real'}, + allowed_structures={'matrix'}, allowed_matrix_shapes={(3, 1)}, ) + #################### # - Blender Socket #################### class Real3DVectorBLSocket(base.MaxwellSimSocket): socket_type = ct.SocketType.Real3DVector - bl_label = "Real 3D Vector" - + bl_label = 'Real 3D Vector' + #################### # - Properties #################### raw_value: bpy.props.FloatVectorProperty( - name="Real 3D Vector", - description="Represents a real 3D (coordinate) vector", + 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.sync_prop("raw_value", context)), + update=(lambda self, context: self.sync_prop('raw_value', context)), ) - + #################### # - Socket UI #################### def draw_value(self, col: bpy.types.UILayout) -> None: - col.prop(self, "raw_value", text="") - + col.prop(self, 'raw_value', text='') + #################### # - Computation of Default Value #################### @property def value(self) -> Real3DVector: return sp.Matrix(tuple(self.raw_value)) - + @value.setter def value(self, value: Real3DVector) -> None: self.raw_value = tuple(value) + #################### # - Socket Configuration #################### class Real3DVectorSocketDef(pyd.BaseModel): socket_type: ct.SocketType = ct.SocketType.Real3DVector - + default_value: Real3DVector = sp.Matrix([0.0, 0.0, 0.0]) - + def init(self, bl_socket: Real3DVectorBLSocket) -> None: bl_socket.value = self.default_value + #################### # - Blender Registration #################### diff --git a/src/blender_maxwell/operators/bl_append.py b/src/blender_maxwell/operators/bl_append.py index fc26dd0..af6d2ed 100644 --- a/src/blender_maxwell/operators/bl_append.py +++ b/src/blender_maxwell/operators/bl_append.py @@ -7,19 +7,21 @@ import bpy from . import types + class BlenderMaxwellUninstallDependenciesOperator(bpy.types.Operator): bl_idname = types.BlenderMaxwellUninstallDependencies - bl_label = "Uninstall Dependencies for Blender Maxwell Addon" + bl_label = 'Uninstall Dependencies for Blender Maxwell Addon' def execute(self, context): addon_dir = Path(__file__).parent.parent - #addon_specific_folder = addon_dir / '.dependencies' - addon_specific_folder = Path("/home/sofus/src/college/bsc_ge/thesis/code/.cached-dependencies") - - shutil.rmtree(addon_specific_folder) - - return {'FINISHED'} + # addon_specific_folder = addon_dir / '.dependencies' + addon_specific_folder = Path( + '/home/sofus/src/college/bsc_ge/thesis/code/.cached-dependencies' + ) + shutil.rmtree(addon_specific_folder) + + return {'FINISHED'} #################### diff --git a/src/blender_maxwell/registration.py b/src/blender_maxwell/registration.py index 5599b32..3d4200a 100644 --- a/src/blender_maxwell/registration.py +++ b/src/blender_maxwell/registration.py @@ -71,7 +71,6 @@ def register_keymap_items(keymap_item_defs: list[dict]): alt=keymap_item_def['alt'], ) REG__KEYMAP_ITEMS.append(keymap_item) - def unregister_keymap_items(): diff --git a/src/blender_maxwell/utils/analyze_geonodes.py b/src/blender_maxwell/utils/analyze_geonodes.py index d836132..91a9ef3 100644 --- a/src/blender_maxwell/utils/analyze_geonodes.py +++ b/src/blender_maxwell/utils/analyze_geonodes.py @@ -1,11 +1,13 @@ import typing_extensions as typx INVALID_BL_SOCKET_TYPES = { - "NodeSocketGeometry", + 'NodeSocketGeometry', } + + def interface( geo_nodes, - direc: typx.Literal["INPUT", "OUTPUT"], + direc: typx.Literal['INPUT', 'OUTPUT'], ): """Returns 'valid' GeoNodes interface sockets, meaning that: - The Blender socket type is not something invalid (ex. "Geometry"). @@ -17,9 +19,11 @@ def interface( for interface_item_name, bl_interface_socket in ( geo_nodes.interface.items_tree.items() ) - if all([ - bl_interface_socket.socket_type not in INVALID_BL_SOCKET_TYPES, - hasattr(bl_interface_socket, "default_value"), - bl_interface_socket.in_out == direc, - ]) + if all( + [ + bl_interface_socket.socket_type not in INVALID_BL_SOCKET_TYPES, + hasattr(bl_interface_socket, 'default_value'), + bl_interface_socket.in_out == direc, + ] + ) } diff --git a/src/blender_maxwell/utils/blender_type_enum.py b/src/blender_maxwell/utils/blender_type_enum.py index 194321b..3c8a086 100644 --- a/src/blender_maxwell/utils/blender_type_enum.py +++ b/src/blender_maxwell/utils/blender_type_enum.py @@ -5,25 +5,33 @@ class BlenderTypeEnum(str, enum.Enum): def _generate_next_value_(name, start, count, last_values): return name + def append_cls_name_to_values(cls): # Construct Set w/Modified Member Names - new_members = {name: f"{name}{cls.__name__}" for name, member in cls.__members__.items()} - + new_members = { + name: f'{name}{cls.__name__}' + for name, member in cls.__members__.items() + } + # Dynamically Declare New Enum Class w/Modified Members new_cls = enum.Enum(cls.__name__, new_members, type=BlenderTypeEnum) new_cls.__module__ = cls.__module__ - + # Return New (Replacing) Enum Class return new_cls + def wrap_values_in_MT(cls): # Construct Set w/Modified Member Names - new_members = {name: f"BLENDER_MAXWELL_MT_{name}" for name, member in cls.__members__.items()} - + new_members = { + name: f'BLENDER_MAXWELL_MT_{name}' + for name, member in cls.__members__.items() + } + # Dynamically Declare New Enum Class w/Modified Members new_cls = enum.Enum(cls.__name__, new_members, type=BlenderTypeEnum) new_cls.__module__ = cls.__module__ new_cls.get_tree = cls.get_tree ## TODO: This is wildly specific... - + # Return New (Replacing) Enum Class return new_cls diff --git a/src/blender_maxwell/utils/extra_sympy_units.py b/src/blender_maxwell/utils/extra_sympy_units.py index 5a811ff..cb34274 100644 --- a/src/blender_maxwell/utils/extra_sympy_units.py +++ b/src/blender_maxwell/utils/extra_sympy_units.py @@ -9,26 +9,28 @@ import sympy.physics.units as spu #################### def uses_units(expression: sp.Expr) -> bool: """Checks if an expression uses any units (`Quantity`).""" - + for arg in sp.preorder_traversal(expression): if isinstance(arg, spu.Quantity): return True return False + # Function to return a set containing all units used in the expression def get_units(expression: sp.Expr): """Gets all the units of an expression (as `Quantity`).""" - + return { arg for arg in sp.preorder_traversal(expression) if isinstance(arg, spu.Quantity) } + #################### # - Time #################### -femtosecond = fs = spu.Quantity("femtosecond", abbrev="fs") +femtosecond = fs = spu.Quantity('femtosecond', abbrev='fs') femtosecond.set_global_relative_scale_factor(spu.femto, spu.second) @@ -36,35 +38,35 @@ femtosecond.set_global_relative_scale_factor(spu.femto, spu.second) # - Force #################### # Newton -nanonewton = nN = spu.Quantity("nanonewton", abbrev="nN") +nanonewton = nN = spu.Quantity('nanonewton', abbrev='nN') nanonewton.set_global_relative_scale_factor(spu.nano, spu.newton) -micronewton = uN = spu.Quantity("micronewton", abbrev="μN") +micronewton = uN = spu.Quantity('micronewton', abbrev='μN') micronewton.set_global_relative_scale_factor(spu.micro, spu.newton) -millinewton = mN = spu.Quantity("micronewton", abbrev="mN") +millinewton = mN = spu.Quantity('micronewton', abbrev='mN') micronewton.set_global_relative_scale_factor(spu.milli, spu.newton) #################### # - Frequency #################### # Hertz -kilohertz = kHz = spu.Quantity("kilohertz", abbrev="kHz") +kilohertz = kHz = spu.Quantity('kilohertz', abbrev='kHz') kilohertz.set_global_relative_scale_factor(spu.kilo, spu.hertz) -megahertz = MHz = spu.Quantity("megahertz", abbrev="MHz") +megahertz = MHz = spu.Quantity('megahertz', abbrev='MHz') kilohertz.set_global_relative_scale_factor(spu.kilo, spu.hertz) -gigahertz = GHz = spu.Quantity("gigahertz", abbrev="GHz") +gigahertz = GHz = spu.Quantity('gigahertz', abbrev='GHz') gigahertz.set_global_relative_scale_factor(spu.giga, spu.hertz) -terahertz = THz = spu.Quantity("terahertz", abbrev="THz") +terahertz = THz = spu.Quantity('terahertz', abbrev='THz') terahertz.set_global_relative_scale_factor(spu.tera, spu.hertz) -petahertz = PHz = spu.Quantity("petahertz", abbrev="PHz") +petahertz = PHz = spu.Quantity('petahertz', abbrev='PHz') petahertz.set_global_relative_scale_factor(spu.peta, spu.hertz) -exahertz = EHz = spu.Quantity("exahertz", abbrev="EHz") +exahertz = EHz = spu.Quantity('exahertz', abbrev='EHz') exahertz.set_global_relative_scale_factor(spu.exa, spu.hertz) #################### @@ -80,21 +82,23 @@ ALL_UNIT_SYMBOLS = { if isinstance(unit, spu.Quantity) } + @functools.lru_cache(maxsize=1024) def parse_abbrev_symbols_to_units(expr: sp.Basic) -> sp.Basic: - print("IN ABBREV", expr) + print('IN ABBREV', expr) return expr.subs(ALL_UNIT_SYMBOLS) -#def has_units(expr: sp.Expr): -# return any( -# symbol in ALL_UNIT_SYMBOLS -# for symbol in expr.atoms(sp.Symbol) -# ) -#def is_exactly_expressed_as_unit(expr: sp.Expr, unit) -> bool: -# #try: -# converted_expr = expr / unit -# -# return ( -# converted_expr.is_number -# and not converted_expr.has(spu.Quantity) -# ) + +# def has_units(expr: sp.Expr): +# return any( +# symbol in ALL_UNIT_SYMBOLS +# for symbol in expr.atoms(sp.Symbol) +# ) +# def is_exactly_expressed_as_unit(expr: sp.Expr, unit) -> bool: +# #try: +# converted_expr = expr / unit +# +# return ( +# converted_expr.is_number +# and not converted_expr.has(spu.Quantity) +# ) diff --git a/src/blender_maxwell/utils/logger.py b/src/blender_maxwell/utils/logger.py index 9f56633..9791757 100644 --- a/src/blender_maxwell/utils/logger.py +++ b/src/blender_maxwell/utils/logger.py @@ -7,20 +7,25 @@ def get(): if LOGGER is None: # Set Sensible Defaults LOGGER.setLevel(logging.DEBUG) - #FORMATTER = logging.Formatter( - # '%(asctime)-15s %(levelname)8s %(name)s %(message)s' - #) + # FORMATTER = logging.Formatter( + # '%(asctime)-15s %(levelname)8s %(name)s %(message)s' + # ) # Add Stream Handler STREAM_HANDLER = logging.StreamHandler() - #STREAM_HANDLER.setFormatter(FORMATTER) + # STREAM_HANDLER.setFormatter(FORMATTER) LOGGER.addHandler(STREAM_HANDLER) return LOGGER + def set_level(level): LOGGER.setLevel(level) + + def enable_logfile(): raise NotImplementedError + + def disable_logfile(): raise NotImplementedError diff --git a/src/blender_maxwell/utils/pydantic_sympy.py b/src/blender_maxwell/utils/pydantic_sympy.py index 454c5e5..904a6eb 100644 --- a/src/blender_maxwell/utils/pydantic_sympy.py +++ b/src/blender_maxwell/utils/pydantic_sympy.py @@ -19,6 +19,7 @@ Complex = typx.Annotated[ ), ] + #################### # - Custom Pydantic Type for sp.Expr #################### @@ -32,29 +33,33 @@ class _SympyExpr: def validate_from_str(value: str) -> AllowedSympyExprs: if not isinstance(value, str): return value - + try: expr = sp.sympify(value) except ValueError as ex: - msg = f"Value {value} is not a `sympify`able string" + msg = f'Value {value} is not a `sympify`able string' raise ValueError(msg) from ex - + return expr.subs(spux.ALL_UNIT_SYMBOLS) - + def validate_from_expr(value: AllowedSympyExprs) -> AllowedSympyExprs: - if not ( - isinstance(value, sp.Expr | sp.MatrixBase) - ): - msg = f"Value {value} is not a `sympy` expression" + if not (isinstance(value, sp.Expr | sp.MatrixBase)): + msg = f'Value {value} is not a `sympy` expression' raise ValueError(msg) - + return value - - sympy_expr_schema = pyd_core_schema.chain_schema([ - pyd_core_schema.no_info_plain_validator_function(validate_from_str), - pyd_core_schema.no_info_plain_validator_function(validate_from_expr), - pyd_core_schema.is_instance_schema(AllowedSympyExprs), - ]) + + sympy_expr_schema = pyd_core_schema.chain_schema( + [ + pyd_core_schema.no_info_plain_validator_function( + validate_from_str + ), + pyd_core_schema.no_info_plain_validator_function( + validate_from_expr + ), + pyd_core_schema.is_instance_schema(AllowedSympyExprs), + ] + ) return pyd_core_schema.json_or_python_schema( json_schema=sympy_expr_schema, python_schema=sympy_expr_schema, @@ -63,6 +68,7 @@ class _SympyExpr: ), ) + #################### # - Configurable Expression Validation #################### @@ -71,81 +77,96 @@ SympyExpr = typx.Annotated[ _SympyExpr, ] + def ConstrSympyExpr( # Feature Class allow_variables: bool = True, allow_units: bool = True, - # Structure Class - allowed_sets: set[typx.Literal[ - "integer", "rational", "real", "complex" - ]] | None = None, - allowed_structures: set[typx.Literal[ - "scalar", "matrix" - ]] | None = None, - + allowed_sets: set[typx.Literal['integer', 'rational', 'real', 'complex']] + | None = None, + allowed_structures: set[typx.Literal['scalar', 'matrix']] | None = None, # Element Class allowed_symbols: set[sp.Symbol] | None = None, allowed_units: set[spu.Quantity] | None = None, - # Shape Class allowed_matrix_shapes: set[tuple[int, int]] | None = None, ): ## See `sympy` predicates: ## - def validate_expr(expr: AllowedSympyExprs): - if not ( - isinstance(expr, sp.Expr | sp.MatrixBase), - ): + if not (isinstance(expr, sp.Expr | sp.MatrixBase),): ## NOTE: Must match AllowedSympyExprs union elements. msg = f"expr '{expr}' is not an allowed Sympy expression ({AllowedSympyExprs})" raise ValueError(msg) - + msgs = set() - + # Validate Feature Class if (not allow_variables) and (len(expr.free_symbols) > 0): - msgs.add(f"allow_variables={allow_variables} does not match expression {expr}.") + msgs.add( + f'allow_variables={allow_variables} does not match expression {expr}.' + ) if (not allow_units) and spux.uses_units(expr): - msgs.add(f"allow_units={allow_units} does not match expression {expr}.") - + msgs.add( + f'allow_units={allow_units} does not match expression {expr}.' + ) + # Validate Structure Class - if allowed_sets and isinstance(expr, sp.Expr) and not any([ - { - "integer": expr.is_integer, - "rational": expr.is_rational, - "real": expr.is_real, - "complex": expr.is_complex, - }[allowed_set] - for allowed_set in allowed_sets - ]): - msgs.add(f"allowed_sets={allowed_sets} does not match expression {expr} (remember to add assumptions to symbols, ex. `x = sp.Symbol('x', real=True))") - if allowed_structures and not any([ - { - "matrix": isinstance(expr, sp.MatrixBase), - }[allowed_set] - for allowed_set in allowed_structures - if allowed_structures != "scalar" - ]): - msgs.add(f"allowed_structures={allowed_structures} does not match expression {expr} (remember to add assumptions to symbols, ex. `x = sp.Symbol('x', real=True))") - + if ( + allowed_sets + and isinstance(expr, sp.Expr) + and not any( + [ + { + 'integer': expr.is_integer, + 'rational': expr.is_rational, + 'real': expr.is_real, + 'complex': expr.is_complex, + }[allowed_set] + for allowed_set in allowed_sets + ] + ) + ): + msgs.add( + f"allowed_sets={allowed_sets} does not match expression {expr} (remember to add assumptions to symbols, ex. `x = sp.Symbol('x', real=True))" + ) + if allowed_structures and not any( + [ + { + 'matrix': isinstance(expr, sp.MatrixBase), + }[allowed_set] + for allowed_set in allowed_structures + if allowed_structures != 'scalar' + ] + ): + msgs.add( + f"allowed_structures={allowed_structures} does not match expression {expr} (remember to add assumptions to symbols, ex. `x = sp.Symbol('x', real=True))" + ) + # Validate Element Class if allowed_symbols and expr.free_symbols.issubset(allowed_symbols): - msgs.add(f"allowed_symbols={allowed_symbols} does not match expression {expr}") + msgs.add( + f'allowed_symbols={allowed_symbols} does not match expression {expr}' + ) if allowed_units and spux.get_units(expr).issubset(allowed_units): - msgs.add(f"allowed_units={allowed_units} does not match expression {expr}") - + msgs.add( + f'allowed_units={allowed_units} does not match expression {expr}' + ) + # Validate Shape Class if ( - allowed_matrix_shapes - and isinstance(expr, sp.MatrixBase) + allowed_matrix_shapes and isinstance(expr, sp.MatrixBase) ) and expr.shape not in allowed_matrix_shapes: - msgs.add(f"allowed_matrix_shapes={allowed_matrix_shapes} does not match expression {expr} with shape {expr.shape}") - + msgs.add( + f'allowed_matrix_shapes={allowed_matrix_shapes} does not match expression {expr} with shape {expr.shape}' + ) + # Error or Return - if msgs: raise ValueError(str(msgs)) + if msgs: + raise ValueError(str(msgs)) return expr - + return typx.Annotated[ AllowedSympyExprs, _SympyExpr, diff --git a/src/blender_maxwell/utils/pydeps.py b/src/blender_maxwell/utils/pydeps.py index 27cc79c..e651d4b 100644 --- a/src/blender_maxwell/utils/pydeps.py +++ b/src/blender_maxwell/utils/pydeps.py @@ -83,18 +83,22 @@ def _check_pydeps( } # Assemble and Return Issues - return [ - f'{name}: Have {inst_ver}, Need {req_ver}' - for name, (req_ver, inst_ver) in conflicts.items() - ] + [ - f'Missing {deplock}' - for deplock in req_not_inst - if deplock.split('==')[0] not in conflicts - ] + [ - f'Superfluous {deplock}' - for deplock in inst_not_req - if deplock.split('==')[0] not in conflicts - ] + return ( + [ + f'{name}: Have {inst_ver}, Need {req_ver}' + for name, (req_ver, inst_ver) in conflicts.items() + ] + + [ + f'Missing {deplock}' + for deplock in req_not_inst + if deplock.split('==')[0] not in conflicts + ] + + [ + f'Superfluous {deplock}' + for deplock in inst_not_req + if deplock.split('==')[0] not in conflicts + ] + ) #################### @@ -105,8 +109,8 @@ def check_pydeps(path_deps: Path): global DEPS_ISSUES # noqa: PLW0603 if len(_issues := _check_pydeps(PATH_REQS, path_deps)) > 0: - #log.debug('Package Check Failed:', end='\n\t') - #log.debug(*_issues, sep='\n\t') + # log.debug('Package Check Failed:', end='\n\t') + # log.debug(*_issues, sep='\n\t') DEPS_OK = False DEPS_ISSUES = _issues diff --git a/src/blender_maxwell/utils/tdcloud.py b/src/blender_maxwell/utils/tdcloud.py index 5c07c0b..ee80376 100644 --- a/src/blender_maxwell/utils/tdcloud.py +++ b/src/blender_maxwell/utils/tdcloud.py @@ -2,6 +2,7 @@ - SimulationTask: - Tidy3D Stub: """ + import datetime as dt import functools import typing as typ @@ -27,31 +28,35 @@ FileUploadCallback = typ.Callable[[float], None] IS_ONLINE = False IS_AUTHENTICATED = False + def is_online(): global IS_ONLINE return IS_ONLINE + def set_online(): global IS_ONLINE IS_ONLINE = True + def set_offline(): global IS_ONLINE IS_ONLINE = False + #################### # - Cloud Authentication #################### def check_authentication() -> bool: global IS_AUTHENTICATED global IS_ONLINE - + # Check Previous Authentication ## If we authenticated once, we presume that it'll work again. ## TODO: API keys can change... It would just look like "offline" for now. if IS_AUTHENTICATED: return True - + api_key = td_web.core.http_util.api_key() if api_key is not None: try: @@ -60,113 +65,118 @@ def check_authentication() -> bool: except td.exceptions.WebError: set_offline() return False - + IS_AUTHENTICATED = True return True - + return False + def authenticate_with_api_key(api_key: str) -> bool: td_web.configure(api_key) return check_authentication() + #################### # - Cloud Folder #################### class TidyCloudFolders: cache_folders: dict[CloudFolderID, CloudFolder] | None = None - + #################### # - Folders #################### @classmethod def folders(cls) -> dict[CloudFolderID, CloudFolder]: - """Get all cloud folders as a dict, indexed by ID. - """ - if cls.cache_folders is not None: return cls.cache_folders - + """Get all cloud folders as a dict, indexed by ID.""" + if cls.cache_folders is not None: + return cls.cache_folders + try: cloud_folders = td_web.core.task_core.Folder.list() set_online() except td.exceptions.WebError: set_offline() - msg = "Tried to get cloud folders, but cannot connect to cloud" + msg = 'Tried to get cloud folders, but cannot connect to cloud' raise RuntimeError(msg) - + folders = { cloud_folder.folder_id: cloud_folder for cloud_folder in cloud_folders } cls.cache_folders = folders return folders - + @classmethod def mk_folder(cls, folder_name: CloudFolderName) -> CloudFolder: - """Create a cloud folder, raising an exception if it exists. - """ + """Create a cloud folder, raising an exception if it exists.""" folders = cls.update_folders() if folder_name not in { - cloud_folder.folder_name - for cloud_folder in folders.values() + cloud_folder.folder_name for cloud_folder in folders.values() }: try: cloud_folder = td_web.core.task_core.Folder.create(folder_name) set_online() except td.exceptions.WebError: set_offline() - msg = "Tried to create cloud folder, but cannot connect to cloud" + msg = ( + 'Tried to create cloud folder, but cannot connect to cloud' + ) raise RuntimeError(msg) - - if cls.cache_folders is None: cls.cache_folders = {} + + if cls.cache_folders is None: + cls.cache_folders = {} cls.cache_folders[cloud_folder.folder_id] = cloud_folder return cloud_folder - + msg = f"Cannot create cloud folder: Folder '{folder_name}' already exists" raise ValueError(msg) - + @classmethod def update_folders(cls) -> dict[CloudFolderID, CloudFolder]: - """Get all cloud folders as a dict, forcing a re-check with the web service. - """ + """Get all cloud folders as a dict, forcing a re-check with the web service.""" cls.cache_folders = None return cls.folders() - + ## TODO: Support removing folders. Unsure of the semantics (does it recursively delete tasks too?) - + + #################### # - Cloud Task #################### @dataclass class CloudTaskInfo: """Toned-down, simplified `dataclass` variant of TaskInfo. - + See TaskInfo for more: ) """ + task_name: str status: str created_at: dt.datetime - + cost_est: typ.Callable[[], float | None] run_info: typ.Callable[[], tuple[float | None, float | None] | None] - + # Timing completed_at: dt.datetime | None = None ## completedAt - + # Cost cost_real: float | None = None ## realCost - + # Sim Properties task_type: str | None = None ## solverVersion version_solver: str | None = None ## solverVersion callback_url: str | None = None ## callbackUrl + class TidyCloudTasks: """Greatly simplifies working with Tidy3D Tasks in the Cloud, specifically, via the lowish-level `tidy3d.web.core.task_core.SimulationTask` object. - + In particular, cache mechanics ensure that web-requests are only made when absolutely needed. This greatly improves performance in ex. UI functions. In particular, `update_task` updates only one task with a single request. - + Of particular note are the `SimulationTask` methods that are not abstracted: - `cloud_task.taskName`: Undocumented, but it works (?) - `cloud_task.submit()`: Starts the running of a drafted task. @@ -174,77 +184,78 @@ class TidyCloudTasks: - `cloud_task.get_running_info()`: GETs % and field-decay of a running task. - `cloud_task.get_log(path)`: GET the run log. Remember to use `NamedTemporaryFile` if a stringified log is desired. """ + cache_tasks: dict[CloudTaskID, CloudTask] = {} cache_folder_tasks: dict[CloudFolderID, set[CloudTaskID]] = {} cache_task_info: dict[CloudTaskID, CloudTaskInfo] = {} - + @classmethod def clear_cache(cls): cls.cache_tasks = {} - + #################### # - Task Getters #################### @classmethod def task(cls, task_id: CloudTaskID) -> CloudTask | None: return cls.cache_tasks.get(task_id) - + @classmethod def task_info(cls, task_id: CloudTaskID) -> CloudTaskInfo | None: return cls.cache_task_info.get(task_id) - + @classmethod def tasks(cls, cloud_folder: CloudFolder) -> dict[CloudTaskID, CloudTask]: - """Get all cloud tasks within a particular cloud folder as a set. - """ + """Get all cloud tasks within a particular cloud folder as a set.""" # Retrieve Cached Tasks - if (task_ids := cls.cache_folder_tasks.get(cloud_folder.folder_id)) is not None: - return { - task_id: cls.cache_tasks[task_id] - for task_id in task_ids - } - + if ( + task_ids := cls.cache_folder_tasks.get(cloud_folder.folder_id) + ) is not None: + return {task_id: cls.cache_tasks[task_id] for task_id in task_ids} + # Retrieve Tasks by-Folder try: folder_tasks = cloud_folder.list_tasks() set_online() except td.exceptions.WebError: set_offline() - msg = "Tried to get tasks of a cloud folder, but cannot access cloud" + msg = ( + 'Tried to get tasks of a cloud folder, but cannot access cloud' + ) raise RuntimeError(msg) - + # No Tasks: Empty Set if folder_tasks is None: cls.cache_folder_tasks[cloud_folder.folder_id] = set() return {} - + # Populate Caches ## Direct Task Cache cloud_tasks = { - cloud_task.task_id: cloud_task - for cloud_task in folder_tasks + cloud_task.task_id: cloud_task for cloud_task in folder_tasks } cls.cache_tasks |= cloud_tasks - + ## Task Info Cache for task_id, cloud_task in cloud_tasks.items(): cls.cache_task_info[task_id] = CloudTaskInfo( task_name=cloud_task.taskName, status=cloud_task.status, created_at=cloud_task.created_at, - cost_est=functools.partial(td_web.estimate_cost, cloud_task.task_id), + cost_est=functools.partial( + td_web.estimate_cost, cloud_task.task_id + ), run_info=cloud_task.get_running_info, callback_url=cloud_task.callback_url, ) - + ## Task by-Folder Cache cls.cache_folder_tasks[cloud_folder.folder_id] = { - task_id - for task_id in cloud_tasks + task_id for task_id in cloud_tasks } - + return cloud_tasks - + #################### # - Task Create/Delete #################### @@ -253,36 +264,34 @@ class TidyCloudTasks: cls, task_name: CloudTaskName, cloud_folder: CloudFolder, - sim: td.Simulation, - upload_progress_cb: FileUploadCallback | None = None, verbose: bool = True, ) -> CloudTask: """Creates a `CloudTask` of the given `td.Simulation`. - + Presume that `sim.validate_pre_upload()` has already been run, so that the simulation is good to go. """ # Create "Stub" ## Minimal Tidy3D object that can be turned into a file for upload ## Has "type" in {"Simulation", "ModeSolver", "HeatSimulation"} stub = td_web.api.tidy3d_stub.Tidy3dStub(simulation=sim) - + # Create Cloud Task ## So far, this is a boring, empty task with no data ## May overlay by name with other tasks - then makes a new "version" try: cloud_task = td_web.core.task_core.SimulationTask.create( - task_type=stub.get_type(), + task_type=stub.get_type(), task_name=task_name, folder_name=cloud_folder.folder_name, ) set_online() except td.exceptions.WebError: set_offline() - msg = "Tried to create cloud task, but cannot access cloud" + msg = 'Tried to create cloud task, but cannot access cloud' raise RuntimeError(msg) - + # Upload Simulation to Cloud Task if upload_progress_cb is not None: upload_progress_cb = lambda uploaded_bytes: None @@ -290,36 +299,40 @@ class TidyCloudTasks: cloud_task.upload_simulation( stub, verbose=verbose, - #progress_callback=upload_progress_cb, + # progress_callback=upload_progress_cb, ) set_online() except td.exceptions.WebError: set_offline() - msg = "Tried to upload simulation to cloud task, but cannot access cloud" + msg = 'Tried to upload simulation to cloud task, but cannot access cloud' raise RuntimeError(msg) - + # Populate Caches ## Direct Task Cache cls.cache_tasks[cloud_task.task_id] = cloud_task - + ## Task Info Cache cls.cache_task_info[cloud_task.task_id] = CloudTaskInfo( task_name=cloud_task.taskName, status=cloud_task.status, created_at=cloud_task.created_at, - cost_est=functools.partial(td_web.estimate_cost, cloud_task.task_id), + cost_est=functools.partial( + td_web.estimate_cost, cloud_task.task_id + ), run_info=cloud_task.get_running_info, callback_url=cloud_task.callback_url, ) - + ## Task by-Folder Cache if cls.cache_folder_tasks.get(cloud_task.folder_id): - cls.cache_folder_tasks[cloud_task.folder_id].add(cloud_task.task_id) + cls.cache_folder_tasks[cloud_task.folder_id].add( + cloud_task.task_id + ) else: cls.cache_folder_tasks[cloud_task.folder_id] = {cloud_task.task_id} - + return cloud_task - + #################### # - Task Update/Delete #################### @@ -328,8 +341,7 @@ class TidyCloudTasks: cls, cloud_task: CloudTask, ) -> CloudTask: - """Deletes a cloud task. - """ + """Deletes a cloud task.""" ## TODO: Abort first? task_id = cloud_task.task_id folder_id = cloud_task.folder_id @@ -338,62 +350,61 @@ class TidyCloudTasks: set_online() except td.exceptions.WebError: set_offline() - msg = "Tried to delete cloud task, but cannot access cloud" + msg = 'Tried to delete cloud task, but cannot access cloud' raise RuntimeError(msg) - + # Populate Caches ## Direct Task Cache cls.cache_tasks.pop(task_id, None) - + ## Task Info Cache cls.cache_task_info.pop(task_id, None) - + ## Task by-Folder Cache cls.cache_folder_tasks[folder_id].remove(task_id) - + @classmethod def update_task(cls, cloud_task: CloudTask) -> CloudTask: - """Updates the CloudTask to the latest ex. status attributes. - """ + """Updates the CloudTask to the latest ex. status attributes.""" # BUG: td_web.core.task_core.SimulationTask.get(task_id) doesn't return the `created_at` field. ## Therefore, we unfortunately need to get all tasks for the folder ID just to update one. - + # Retrieve Folder task_id = cloud_task.task_id folder_id = cloud_task.folder_id cloud_folder = TidyCloudFolders.folders()[folder_id] - + # Repopulate All Caches ## By deleting the folder ID, all tasks within will be reloaded del cls.cache_folder_tasks[folder_id] folder_tasks = cls.tasks(cloud_folder) - + return cls.tasks(cloud_folder)[task_id] - + @classmethod - def update_tasks(cls, folder_id: CloudFolderID) -> dict[CloudTaskID, CloudTask]: - """Updates the CloudTask to the latest ex. status attributes. - """ + def update_tasks( + cls, folder_id: CloudFolderID + ) -> dict[CloudTaskID, CloudTask]: + """Updates the CloudTask to the latest ex. status attributes.""" # BUG: td_web.core.task_core.SimulationTask.get(task_id) doesn't return the `created_at` field. ## Therefore, we unfortunately need to get all tasks for the folder ID just to update one. - + # Retrieve Folder cloud_folder = TidyCloudFolders.folders()[folder_id] - + # Repopulate All Caches ## By deleting the folder ID, all tasks within will be reloaded del cls.cache_folder_tasks[folder_id] folder_tasks = cls.tasks(cloud_folder) - + return { task_id: cls.cache_tasks[task_id] for task_id in cls.cache_folder_tasks[folder_id] } - + @classmethod def abort_task(cls, cloud_task: CloudTask) -> CloudTask: - """Aborts a running CloudTask to the latest ex. status attributes. - """ + """Aborts a running CloudTask to the latest ex. status attributes.""" ## TODO: Check status? new_cloud_task = cls.update_task(cloud_task) try: @@ -401,7 +412,7 @@ class TidyCloudTasks: set_online() except td.exceptions.WebError: set_offline() - msg = "Tried to abort cloud task, but cannot access cloud" + msg = 'Tried to abort cloud task, but cannot access cloud' raise RuntimeError(msg) - + return cls.update_task(cloud_task)