From 28e6760dfb27efaa1c0ae66b81e83139c81b448f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sofus=20Albert=20H=C3=B8gsbro=20Rose?= Date: Mon, 26 Feb 2024 16:16:06 +0100 Subject: [PATCH] feat: Various features (some very prototype). It's very prototype-y. Cleanup pending. --- .../node_trees/maxwell_sim_nodes/contracts.py | 170 +++++++--- .../node_trees/maxwell_sim_nodes/node_tree.py | 35 +++ .../maxwell_sim_nodes/nodes/base.py | 67 ++-- .../nodes/bounds/bound_box.py | 81 ++++- .../nodes/inputs/__init__.py | 9 +- .../nodes/inputs/constants/__init__.py | 16 +- .../inputs/constants/blender_constant.py | 67 +++- .../nodes/inputs/constants/number_constant.py | 5 + .../nodes/inputs/constants/wave_constant.py | 86 +++++ .../nodes/inputs/parameters/__init__.py | 11 - .../inputs/parameters/number_parameter.py | 5 - .../inputs/parameters/physical_parameter.py | 5 - .../nodes/inputs/unit_system.py | 44 +++ .../nodes/mediums/drude_lorentz_medium.py | 80 ++++- .../nodes/mediums/library_medium.py | 160 +++++++++- .../outputs/exporters/json_file_exporter.py | 26 +- .../nodes/outputs/viewers/__init__.py | 3 + .../nodes/outputs/viewers/viewer_3d.py | 50 +++ .../nodes/simulations/fdtd_sim.py | 12 +- .../nodes/sources/plane_wave_source.py | 94 +++++- .../nodes/sources/point_dipole_source.py | 21 +- .../continuous_wave_temporal_shape.py | 77 ++++- .../data_driven_temporal_shape.py | 6 - .../gaussian_pulse_temporal_shape.py | 1 + .../nodes/structures/geonodes_structure.py | 165 ++++++++-- .../nodes/structures/object_structure.py | 6 +- .../structures/primitives/box_structure.py | 2 +- .../primitives/cylinder_structure.py | 72 ++++- .../structures/primitives/sphere_structure.py | 66 +++- .../nodes/utilities/__init__.py | 12 + .../nodes/utilities/combine.py | 114 +++++++ .../nodes/utilities/converter/__init__.py | 8 + .../nodes/utilities/converter/separate.py | 82 +++++ .../utilities/converter/wave_converter.py | 82 +++++ .../maxwell_sim_nodes/sockets/__init__.py | 3 + .../maxwell_sim_nodes/sockets/base.py | 21 +- .../sockets/blender/__init__.py | 3 + .../sockets/blender/geonodes_socket.py | 28 +- .../sockets/blender/object_socket.py | 37 ++- .../sockets/blender/target_socket.py | 268 ++++++++++++++++ .../sockets/maxwell/bound_box_socket.py | 73 +++++ .../sockets/maxwell/bound_face_socket.py | 36 ++- .../sockets/number/integer_number_socket.py | 19 +- .../sockets/physical/__init__.py | 8 + .../sockets/physical/pol_socket.py | 37 ++- .../sockets/physical/size_3d_socket.py | 11 - .../sockets/physical/unit_system_socket.py | 296 ++++++++++++++++++ .../sockets/physical/vac_wl_socket.py | 54 ++++ .../sockets/vector/real_2d_vector_socket.py | 22 +- .../sockets/vector/real_3d_vector_socket.py | 22 +- 50 files changed, 2481 insertions(+), 197 deletions(-) create mode 100644 blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/wave_constant.py delete mode 100644 blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/parameters/__init__.py delete mode 100644 blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/parameters/number_parameter.py delete mode 100644 blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/parameters/physical_parameter.py create mode 100644 blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/unit_system.py create mode 100644 blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/viewers/viewer_3d.py delete mode 100644 blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/data_driven_temporal_shape.py create mode 100644 blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/combine.py create mode 100644 blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/converter/__init__.py create mode 100644 blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/converter/separate.py create mode 100644 blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/converter/wave_converter.py create mode 100644 blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/target_socket.py create mode 100644 blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/unit_system_socket.py create mode 100644 blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/vac_wl_socket.py diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts.py b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts.py index c710744..03323d5 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/contracts.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/contracts.py @@ -97,6 +97,7 @@ class SocketType(BlenderTypeEnum): Complex3DVector = enum.auto() # Physical + PhysicalUnitSystem = enum.auto() PhysicalTime = enum.auto() PhysicalAngle = enum.auto() @@ -122,6 +123,7 @@ class SocketType(BlenderTypeEnum): PhysicalPol = enum.auto() PhysicalFreq = enum.auto() + PhysicalVacWL = enum.auto() PhysicalSpecPowerDist = enum.auto() PhysicalSpecRelPermDist = enum.auto() @@ -135,6 +137,8 @@ class SocketType(BlenderTypeEnum): BlenderGeoNodes = enum.auto() BlenderText = enum.auto() + BlenderPreviewTarget = enum.auto() + # Maxwell MaxwellSource = enum.auto() MaxwellTemporalShape = enum.auto() @@ -376,14 +380,19 @@ SocketType_to_units = { "GHZ": spuex.gigahertz, "THZ": spuex.terahertz, "PHZ": spuex.petahertz, - #"EHZ": spu.exahertz, - "VAC_PM": spu.picometer, ## c(vac) = wl*freq - "VAC_A": spu.angstrom, - "VAC_NM": spu.nanometer, - "VAC_UM": spu.micrometer, - "VAC_MM": spu.millimeter, - "VAC_CM": spu.centimeter, - "VAC_M": spu.meter, + "EHZ": spuex.exahertz, + }, + }, + SocketType.PhysicalVacWL: { + "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, }, }, } @@ -406,8 +415,9 @@ SocketType_to_color = { SocketType.Complex2DVector: (0.4, 0.9, 0.4, 1.0), # Medium Light Green SocketType.Real3DVector: (0.3, 0.8, 0.3, 1.0), # Medium Green SocketType.Complex3DVector: (0.2, 0.7, 0.2, 1.0), # Dark Green - + # Physical + SocketType.PhysicalUnitSystem: (1.0, 0.5, 0.5, 1.0), # Light Red SocketType.PhysicalTime: (1.0, 0.5, 0.5, 1.0), # Light Red SocketType.PhysicalAngle: (0.9, 0.45, 0.45, 1.0), # Medium Light Red SocketType.PhysicalLength: (0.8, 0.4, 0.4, 1.0), # Medium Red @@ -425,6 +435,7 @@ SocketType_to_color = { SocketType.PhysicalForce3DVector: (0.6, 0.45, 0.25, 1.0), # Medium Dark Orange SocketType.PhysicalPol: (0.5, 0.4, 0.2, 1.0), # Dark Orange SocketType.PhysicalFreq: (1.0, 0.7, 0.5, 1.0), # Light Peach + SocketType.PhysicalVacWL: (1.0, 0.7, 0.5, 1.0), # Light Peach SocketType.PhysicalSpecPowerDist: (0.9, 0.65, 0.45, 1.0), # Medium Light Peach SocketType.PhysicalSpecRelPermDist: (0.8, 0.6, 0.4, 1.0), # Medium Peach @@ -435,6 +446,7 @@ SocketType_to_color = { SocketType.BlenderVolume: (0.4, 0.35, 0.7, 1.0), # Medium Dark Purple SocketType.BlenderGeoNodes: (0.3, 0.3, 0.6, 1.0), # Dark Purple SocketType.BlenderText: (0.5, 0.5, 0.75, 1.0), # Light Lavender + SocketType.BlenderPreviewTarget: (0.5, 0.5, 0.75, 1.0), # Light Lavender # Maxwell SocketType.MaxwellSource: (1.0, 1.0, 0.5, 1.0), # Light Yellow @@ -451,40 +463,104 @@ SocketType_to_color = { } BLNodeSocket_to_SocketType = { - "NodeSocketBool": SocketType.Bool, - "NodeSocketCollection": SocketType.BlenderCollection, - "NodeSocketColor": SocketType.Real3DVector, - "NodeSocketFloat": SocketType.RealNumber, - "NodeSocketFloatAngle": SocketType.RealNumber, - "NodeSocketFloatDistance": SocketType.RealNumber, - "NodeSocketFloatFactor": SocketType.RealNumber, - "NodeSocketFloatPercentage": SocketType.RealNumber, - "NodeSocketFloatTime": SocketType.RealNumber, - "NodeSocketFloatTimeAbsolute": SocketType.RealNumber, - "NodeSocketFloatUnsigned": SocketType.RealNumber, - "NodeSocketGeometry": SocketType.Any, - "NodeSocketImage": SocketType.BlenderImage, - "NodeSocketInt": SocketType.IntegerNumber, - "NodeSocketIntFactor": SocketType.IntegerNumber, - "NodeSocketIntPercentage": SocketType.IntegerNumber, - "NodeSocketIntUnsigned": SocketType.IntegerNumber, - "NodeSocketMaterial": SocketType.Any, - "NodeSocketObject": SocketType.BlenderObject, - "NodeSocketRotation": SocketType.Real3DVector, - "NodeSocketShader": SocketType.Any, - "NodeSocketStandard": SocketType.Any, - "NodeSocketString": SocketType.Text, - "NodeSocketTexture": SocketType.Any, - "NodeSocketVector": SocketType.Real3DVector, - "NodeSocketVectorAcceleration": SocketType.Real3DVector, - "NodeSocketVectorDirection": SocketType.Real3DVector, - "NodeSocketVectorEuler": SocketType.Real3DVector, - "NodeSocketVectorTranslation": SocketType.Real3DVector, - "NodeSocketVectorVelocity": SocketType.Real3DVector, - "NodeSocketVectorXYZ": SocketType.Real3DVector, - "NodeSocketVirtual": SocketType.Any, + 1: { + "NodeSocketStandard": SocketType.Any, + "NodeSocketVirtual": SocketType.Any, + "NodeSocketGeometry": SocketType.Any, + "NodeSocketTexture": SocketType.Any, + "NodeSocketShader": SocketType.Any, + "NodeSocketMaterial": SocketType.Any, + + "NodeSocketString": SocketType.Text, + "NodeSocketBool": SocketType.Bool, + "NodeSocketCollection": SocketType.BlenderCollection, + "NodeSocketImage": SocketType.BlenderImage, + "NodeSocketObject": SocketType.BlenderObject, + + "NodeSocketFloat": SocketType.RealNumber, + "NodeSocketFloatAngle": SocketType.PhysicalAngle, + "NodeSocketFloatDistance": SocketType.PhysicalLength, + "NodeSocketFloatFactor": SocketType.RealNumber, + "NodeSocketFloatPercentage": SocketType.RealNumber, + "NodeSocketFloatTime": SocketType.PhysicalTime, + "NodeSocketFloatTimeAbsolute": SocketType.RealNumber, + "NodeSocketFloatUnsigned": SocketType.RealNumber, + + "NodeSocketInt": SocketType.IntegerNumber, + "NodeSocketIntFactor": SocketType.IntegerNumber, + "NodeSocketIntPercentage": SocketType.IntegerNumber, + "NodeSocketIntUnsigned": SocketType.IntegerNumber, + }, + 2: { + "NodeSocketVector": SocketType.Real3DVector, + "NodeSocketVectorAcceleration": SocketType.Real3DVector, + "NodeSocketVectorDirection": SocketType.Real3DVector, + "NodeSocketVectorEuler": SocketType.Real3DVector, + "NodeSocketVectorTranslation": SocketType.Real3DVector, + "NodeSocketVectorVelocity": SocketType.Real3DVector, + "NodeSocketVectorXYZ": SocketType.Real3DVector, + #"NodeSocketVector": SocketType.Real2DVector, + #"NodeSocketVectorAcceleration": SocketType.PhysicalAccel2D, + #"NodeSocketVectorDirection": SocketType.PhysicalDir2D, + #"NodeSocketVectorEuler": SocketType.PhysicalEuler2D, + #"NodeSocketVectorTranslation": SocketType.PhysicalDispl2D, + #"NodeSocketVectorVelocity": SocketType.PhysicalVel2D, + #"NodeSocketVectorXYZ": SocketType.Real2DPoint, + }, + 3: { + "NodeSocketRotation": SocketType.Real3DVector, + + "NodeSocketColor": SocketType.Any, + + "NodeSocketVector": SocketType.Real3DVector, + #"NodeSocketVectorAcceleration": SocketType.PhysicalAccel3D, + #"NodeSocketVectorDirection": SocketType.PhysicalDir3D, + #"NodeSocketVectorEuler": SocketType.PhysicalEuler3D, + #"NodeSocketVectorTranslation": SocketType.PhysicalDispl3D, + "NodeSocketVectorTranslation": SocketType.PhysicalPoint3D, + #"NodeSocketVectorVelocity": SocketType.PhysicalVel3D, + "NodeSocketVectorXYZ": SocketType.PhysicalPoint3D, + }, } +BLNodeSocket_to_SocketType_by_desc = { + 1: { + "Angle": SocketType.PhysicalAngle, + + "Length": SocketType.PhysicalLength, + "Area": SocketType.PhysicalArea, + "Volume": SocketType.PhysicalVolume, + + "Mass": SocketType.PhysicalMass, + + "Speed": SocketType.PhysicalSpeed, + "Accel": SocketType.PhysicalAccelScalar, + "Force": SocketType.PhysicalForceScalar, + + "Freq": SocketType.PhysicalFreq, + }, + 2: { + #"2DCount": SocketType.Int2DVector, + + #"2DPoint": SocketType.PhysicalPoint2D, + #"2DSize": SocketType.PhysicalSize2D, + #"2DPol": SocketType.PhysicalPol, + "2DPoint": SocketType.PhysicalPoint3D, + "2DSize": SocketType.PhysicalSize3D, + }, + 3: { + #"Count": SocketType.Int3DVector, + + "Point": SocketType.PhysicalPoint3D, + "Size": SocketType.PhysicalSize3D, + + #"Force": SocketType.PhysicalForce3D, + + "Freq": SocketType.PhysicalSize3D, + }, +} + + #################### # - Node Types #################### @@ -493,15 +569,17 @@ class NodeType(BlenderTypeEnum): KitchenSink = enum.auto() # Inputs + UnitSystem = enum.auto() + ## Inputs / Scene Time = enum.auto() - UnitSystem = enum.auto() ## Inputs / Parameters NumberParameter = enum.auto() PhysicalParameter = enum.auto() ## Inputs / Constants + WaveConstant = enum.auto() ScientificConstant = enum.auto() NumberConstant = enum.auto() PhysicalConstant = enum.auto() @@ -517,6 +595,7 @@ class NodeType(BlenderTypeEnum): # Outputs ## Outputs / Viewers + Viewer3D = enum.auto() ValueViewer = enum.auto() ConsoleViewer = enum.auto() @@ -612,8 +691,13 @@ class NodeType(BlenderTypeEnum): # Utilities + Combine = enum.auto() + Separate = enum.auto() Math = enum.auto() + ## Utilities / Converters + WaveConverter = enum.auto() + ## Utilities / Operations ArrayOperation = enum.auto() @@ -663,6 +747,7 @@ class NodeCategory(BlenderTypeEnum): # Utilities/ MAXWELLSIM_UTILITIES = enum.auto() + MAXWELLSIM_UTILITIES_CONVERTERS = enum.auto() MAXWELLSIM_UTILITIES_OPERATIONS = enum.auto() @classmethod @@ -727,6 +812,7 @@ NodeCategory_to_category_label = { # Utilities/ NodeCategory.MAXWELLSIM_UTILITIES: "Utilities", + NodeCategory.MAXWELLSIM_UTILITIES_CONVERTERS: "Converters", NodeCategory.MAXWELLSIM_UTILITIES_OPERATIONS: "Operations", } diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/node_tree.py b/blender_maxwell/node_trees/maxwell_sim_nodes/node_tree.py index 3494b83..1b7e335 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/node_tree.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/node_tree.py @@ -4,6 +4,25 @@ from . import contracts ICON_SIM_TREE = 'MOD_SIMPLEDEFORM' + +class BLENDER_MAXWELL_PT_MaxwellSimTreePanel(bpy.types.Panel): + bl_label = "Node Tree Custom Prop" + bl_idname = "NODE_PT_custom_prop" + bl_space_type = 'NODE_EDITOR' + bl_region_type = 'UI' + bl_category = 'Item' + + @classmethod + def poll(cls, context): + return context.space_data.tree_type == contracts.TreeType.MaxwellSim.value + + def draw(self, context): + layout = self.layout + node_tree = context.space_data.node_tree + + layout.prop(node_tree, "preview_collection") + layout.prop(node_tree, "non_preview_collection") + #################### # - Node Tree Definition #################### @@ -11,6 +30,22 @@ class MaxwellSimTree(bpy.types.NodeTree): bl_idname = contracts.TreeType.MaxwellSim bl_label = "Maxwell Sim Editor" bl_icon = contracts.Icon.MaxwellSimTree + + preview_collection: bpy.props.PointerProperty( + name="Preview Collection", + description="Collection of Blender objects that will be previewed", + type=bpy.types.Collection, + update=(lambda self, context: self.trigger_updates()) + ) + non_preview_collection: bpy.props.PointerProperty( + name="Non-Preview Collection", + description="Collection of Blender objects that will NOT be previewed", + type=bpy.types.Collection, + update=(lambda self, context: self.trigger_updates()) + ) + + def trigger_updates(self): + pass #################### # - Blender Registration diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/base.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/base.py index 06a2084..d5f494b 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/base.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/base.py @@ -165,14 +165,19 @@ class MaxwellSimTreeNode(bpy.types.Node): if output_socket_set_key not in socket_set_keys ] + def labeller(socket_set_key): + return " ".join( + word.capitalize() + for word in socket_set_key.split("_") + ) cls.__annotations__["socket_set"] = bpy.props.EnumProperty( name="", description="Select a node socket configuration", items=[ ( socket_set_key, - socket_set_key.capitalize(), - socket_set_key.capitalize(), + labeller(socket_set_key), + labeller(socket_set_key), ) for socket_set_key in socket_set_keys ], @@ -217,32 +222,28 @@ class MaxwellSimTreeNode(bpy.types.Node): `NodeTypeProtocol` specification, and initializes each as described by user-provided `SocketDefProtocol`s. """ - # Initialize Input Sockets + # Create Input Sockets for socket_name, socket_def in self.input_sockets.items(): self.inputs.new( - socket_def.socket_type.value, ## strenum.value => a real str + socket_def.socket_type.value, socket_def.label, ) - - # Retrieve the Blender Socket (bpy.types.NodeSocket) - ## We could use self.g_input_bl_socket()... - ## ...but that would rely on implicit semi-initialized state. - bl_socket = self.inputs[ - self.input_sockets[socket_name].label - ] - - # Initialize the Socket from the Socket Definition - ## `bl_socket` knows whether it's an input or output socket... - ## ...via its `.is_output` attribute. - socket_def.init(bl_socket) - # Initialize Output Sockets + # Create Output Sockets for socket_name, socket_def in self.output_sockets.items(): self.outputs.new( socket_def.socket_type.value, socket_def.label, ) + # Initialize Sockets + for socket_name, socket_def in self.input_sockets.items(): + bl_socket = self.inputs[ + self.input_sockets[socket_name].label + ] + socket_def.init(bl_socket) + + for socket_name, socket_def in self.output_sockets.items(): bl_socket = self.outputs[ self.output_sockets[socket_name].label ] @@ -273,6 +274,9 @@ class MaxwellSimTreeNode(bpy.types.Node): # Sync Default Preset to Input Socket Values if self.preset is not None: sync_selected_preset(self) + + if hasattr(self, "init_cb"): + self.init_cb() @classmethod def poll(cls, ntree: bpy.types.NodeTree) -> bool: @@ -365,6 +369,12 @@ class MaxwellSimTreeNode(bpy.types.Node): if hasattr(self, "draw_operators"): self.draw_operators(context, layout) + + if hasattr(self, "draw_props"): + self.draw_props(context, layout) + + if hasattr(self, "draw_info"): + self.draw_info(context, layout) #################### # - Socket Getters @@ -392,13 +402,12 @@ class MaxwellSimTreeNode(bpy.types.Node): return self.inputs[self.input_sockets[input_socket_name].label] elif hasattr(self, "socket_set"): - # You're on your own, chump - return self.inputs[next( socket_def.label for socket_set, socket_dict in self.input_socket_sets.items() for socket_name, socket_def in socket_dict.items() if socket_name == input_socket_name + if socket_set == self.socket_set )] def g_output_bl_socket( @@ -436,21 +445,21 @@ class MaxwellSimTreeNode(bpy.types.Node): self, output_bl_socket_name: contracts.BLSocketName, ) -> contracts.SocketName: + output_socket_names = [ + output_socket_name + for output_socket_name in self.output_sockets.keys() + if self.output_sockets[ + output_socket_name + ].label == output_bl_socket_name + ] if hasattr(self, "socket_set"): - return next( + output_socket_names += [ socket_name for socket_set, socket_dict in self.output_socket_sets.items() for socket_name, socket_def in socket_dict.items() if socket_def.label == output_bl_socket_name - ) - else: - return next( - output_socket_name - for output_socket_name in self.output_sockets.keys() - if self.output_sockets[ - output_socket_name - ].label == output_bl_socket_name - ) + ] + return output_socket_names[0] #################### # - Socket Setters diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/bounds/bound_box.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/bounds/bound_box.py index 41fac16..a715dac 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/bounds/bound_box.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/bounds/bound_box.py @@ -1,5 +1,82 @@ +import tidy3d as td +import sympy as sp +import sympy.physics.units as spu + +from ... import contracts +from ... import sockets +from .. import base + +class BoundBoxNode(base.MaxwellSimTreeNode): + node_type = contracts.NodeType.BoundBox + bl_label = "Bound Box" + #bl_icon = ... + + #################### + # - Sockets + #################### + input_sockets = { + "x_pos": sockets.MaxwellBoundFaceSocketDef( + label="+x", + ), + "x_neg": sockets.MaxwellBoundFaceSocketDef( + label="-x", + ), + "y_pos": sockets.MaxwellBoundFaceSocketDef( + label="+y", + ), + "y_neg": sockets.MaxwellBoundFaceSocketDef( + label="-y", + ), + "z_pos": sockets.MaxwellBoundFaceSocketDef( + label="+z", + ), + "z_neg": sockets.MaxwellBoundFaceSocketDef( + label="-z", + ), + } + output_sockets = { + "bound": sockets.MaxwellBoundBoxSocketDef( + label="Bound", + ), + } + + #################### + # - Output Socket Computation + #################### + @base.computes_output_socket("bound") + def compute_simulation(self: contracts.NodeTypeProtocol) -> td.BoundarySpec: + x_pos = self.compute_input("x_pos") + x_neg = self.compute_input("x_neg") + y_pos = self.compute_input("x_pos") + y_neg = self.compute_input("x_neg") + z_pos = self.compute_input("x_pos") + z_neg = self.compute_input("x_neg") + + return td.BoundarySpec( + x=td.Boundary( + plus=x_pos, + minus=x_neg, + ), + y=td.Boundary( + plus=y_pos, + minus=y_neg, + ), + z=td.Boundary( + plus=z_pos, + minus=z_neg, + ), + ) + + + #################### # - Blender Registration #################### -BL_REGISTER = [] -BL_NODES = {} +BL_REGISTER = [ + BoundBoxNode, +] +BL_NODES = { + contracts.NodeType.BoundBox: ( + contracts.NodeCategory.MAXWELLSIM_BOUNDS + ) +} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/__init__.py index dbcf5ae..d37b595 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/__init__.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/__init__.py @@ -1,17 +1,20 @@ +from . import unit_system + from . import constants from . import lists -from . import parameters from . import scene BL_REGISTER = [ + *unit_system.BL_REGISTER, + *scene.BL_REGISTER, *constants.BL_REGISTER, - *parameters.BL_REGISTER, *lists.BL_REGISTER, ] BL_NODES = { + **unit_system.BL_NODES, + **scene.BL_NODES, **constants.BL_NODES, - **parameters.BL_NODES, **lists.BL_NODES, } diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/__init__.py index eefdd8f..760a729 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/__init__.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/__init__.py @@ -1,17 +1,23 @@ -from . import number_constant -from . import blender_constant -from . import physical_constant +from . import wave_constant from . import scientific_constant +from . import number_constant +from . import physical_constant +from . import blender_constant + BL_REGISTER = [ + *wave_constant.BL_REGISTER, *scientific_constant.BL_REGISTER, + *number_constant.BL_REGISTER, - *blender_constant.BL_REGISTER, *physical_constant.BL_REGISTER, + *blender_constant.BL_REGISTER, ] BL_NODES = { + **wave_constant.BL_NODES, **scientific_constant.BL_NODES, + **number_constant.BL_NODES, - **blender_constant.BL_NODES, **physical_constant.BL_NODES, + **blender_constant.BL_NODES, } diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/blender_constant.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/blender_constant.py index 41fac16..2a44773 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/blender_constant.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/blender_constant.py @@ -1,5 +1,68 @@ +import typing as typ + +from .... import contracts +from .... import sockets +from ... import base + +class BlenderConstantNode(base.MaxwellSimTreeNode): + node_type = contracts.NodeType.BlenderConstant + + bl_label = "Blender Constant" + #bl_icon = constants.ICON_SIM_INPUT + + input_sockets = {} + input_socket_sets = { + "object": { + "value": sockets.BlenderObjectSocketDef( + label="Object", + ), + }, + "collection": { + "value": sockets.BlenderCollectionSocketDef( + label="Collection", + ), + }, + "image": { + "value": sockets.BlenderImageSocketDef( + label="Image", + ), + }, + "volume": { + "value": sockets.BlenderVolumeSocketDef( + label="Volume", + ), + }, + "text": { + "value": sockets.BlenderTextSocketDef( + label="Text", + ), + }, + "geonodes": { + "value": sockets.BlenderGeoNodesSocketDef( + label="GeoNodes", + ), + }, + } + output_sockets = {} + output_socket_sets = input_socket_sets + + #################### + # - Callbacks + #################### + @base.computes_output_socket("value") + def compute_value(self: contracts.NodeTypeProtocol) -> typ.Any: + return self.compute_input("value") + + + #################### # - Blender Registration #################### -BL_REGISTER = [] -BL_NODES = {} +BL_REGISTER = [ + BlenderConstantNode, +] +BL_NODES = { + contracts.NodeType.BlenderConstant: ( + contracts.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS + ) +} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/number_constant.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/number_constant.py index 16d0e36..7f5d904 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/number_constant.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/number_constant.py @@ -13,6 +13,11 @@ class NumberConstantNode(base.MaxwellSimTreeNode): input_sockets = {} input_socket_sets = { + "integer": { + "value": sockets.IntegerNumberSocketDef( + label="Integer", + ), + }, "real": { "value": sockets.RealNumberSocketDef( label="Real", diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/wave_constant.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/wave_constant.py new file mode 100644 index 0000000..13cd8a5 --- /dev/null +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/constants/wave_constant.py @@ -0,0 +1,86 @@ +import bpy +import sympy as sp +import sympy.physics.units as spu +import scipy as sc + +from .... import contracts +from .... import sockets +from ... import base + +vac_speed_of_light = ( + sc.constants.speed_of_light + * spu.meter/spu.second +) + +class WaveConstantNode(base.MaxwellSimTreeNode): + node_type = contracts.NodeType.WaveConstant + + bl_label = "Wave Constant" + + input_sockets = {} + input_socket_sets = { + "vac_wl": { + "vac_wl": sockets.PhysicalVacWLSocketDef( + label="Vac WL", + ), + }, + "freq": { + "freq": sockets.PhysicalFreqSocketDef( + label="Freq", + ), + }, + } + output_sockets = { + "vac_wl": sockets.PhysicalVacWLSocketDef( + label="Vac WL", + ), + "freq": sockets.PhysicalVacWLSocketDef( + label="Freq", + ), + } + output_socket_sets = {} + + #################### + # - Callbacks + #################### + @base.computes_output_socket("vac_wl") + def compute_vac_wl(self: contracts.NodeTypeProtocol) -> sp.Expr: + if self.socket_set == "vac_wl": + return self.compute_input("vac_wl") + + elif self.socket_set == "freq": + freq = self.compute_input("freq") + return spu.convert_to( + vac_speed_of_light / freq, + spu.meter, + ) + + raise ValueError("No valid socket set.") + + @base.computes_output_socket("freq") + def compute_freq(self: contracts.NodeTypeProtocol) -> sp.Expr: + if self.socket_set == "vac_wl": + vac_wl = self.compute_input("vac_wl") + return spu.convert_to( + vac_speed_of_light / vac_wl, + spu.hertz, + ) + + elif self.socket_set == "freq": + return self.compute_input("freq") + + raise ValueError("No valid socket set.") + + + +#################### +# - Blender Registration +#################### +BL_REGISTER = [ + WaveConstantNode, +] +BL_NODES = { + contracts.NodeType.WaveConstant: ( + contracts.NodeCategory.MAXWELLSIM_INPUTS_CONSTANTS + ) +} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/parameters/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/parameters/__init__.py deleted file mode 100644 index fb2c2c0..0000000 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/parameters/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -from . import number_parameter -from . import physical_parameter - -BL_REGISTER = [ - *number_parameter.BL_REGISTER, - *physical_parameter.BL_REGISTER, -] -BL_NODES = { - **number_parameter.BL_NODES, - **physical_parameter.BL_NODES, -} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/parameters/number_parameter.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/parameters/number_parameter.py deleted file mode 100644 index 41fac16..0000000 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/parameters/number_parameter.py +++ /dev/null @@ -1,5 +0,0 @@ -#################### -# - Blender Registration -#################### -BL_REGISTER = [] -BL_NODES = {} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/parameters/physical_parameter.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/parameters/physical_parameter.py deleted file mode 100644 index 41fac16..0000000 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/parameters/physical_parameter.py +++ /dev/null @@ -1,5 +0,0 @@ -#################### -# - Blender Registration -#################### -BL_REGISTER = [] -BL_NODES = {} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/unit_system.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/unit_system.py new file mode 100644 index 0000000..95417a8 --- /dev/null +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/inputs/unit_system.py @@ -0,0 +1,44 @@ +import bpy +import sympy as sp + +from ... import contracts +from ... import sockets +from .. import base + +class PhysicalUnitSystemNode(base.MaxwellSimTreeNode): + node_type = contracts.NodeType.UnitSystem + + bl_label = "Unit System Constant" + + input_sockets = { + "unit_system": sockets.PhysicalUnitSystemSocketDef( + label="Unit System", + show_by_default=True, + ), + } + output_sockets = { + "unit_system": sockets.PhysicalUnitSystemSocketDef( + label="Unit System", + ), + } + + #################### + # - Callbacks + #################### + @base.computes_output_socket("unit_system") + def compute_value(self: contracts.NodeTypeProtocol) -> sp.Expr: + return self.compute_input("unit_system") + + + +#################### +# - Blender Registration +#################### +BL_REGISTER = [ + PhysicalUnitSystemNode, +] +BL_NODES = { + contracts.NodeType.UnitSystem: ( + contracts.NodeCategory.MAXWELLSIM_INPUTS + ) +} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/drude_lorentz_medium.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/drude_lorentz_medium.py index 8f4a665..9d0fc25 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/drude_lorentz_medium.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/drude_lorentz_medium.py @@ -1,6 +1,80 @@ +import tidy3d as td +import sympy as sp +import sympy.physics.units as spu + +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 = ... + + #################### + # - 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] + } + output_sockets = { + "medium": sockets.MaxwellMediumSocketDef( + label="Medium" + ), + } + + #################### + # - Output Socket Computation + #################### + @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] + ] + ) + + + #################### # - Blender Registration #################### -BL_REGISTER = [] -BL_NODES = {} - +BL_REGISTER = [ + DrudeLorentzMediumNode, +] +BL_NODES = { + contracts.NodeType.DrudeLorentzMedium: ( + contracts.NodeCategory.MAXWELLSIM_MEDIUMS + ) +} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/library_medium.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/library_medium.py index 41fac16..86662f4 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/library_medium.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/mediums/library_medium.py @@ -1,5 +1,161 @@ +import bpy +import tidy3d as td +import sympy as sp +import sympy.physics.units as spu +import numpy as np +import scipy as sc + +from .....utils import extra_sympy_units as spuex +from ... import contracts +from ... import sockets +from .. import base + +class ExperimentOperator00(bpy.types.Operator): + bl_idname = "blender_maxwell.experiment_operator_00" + bl_label = "exp" + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + node = context.node + node.invoke_matplotlib_and_update_image() + return {'FINISHED'} + +class LibraryMediumNode(base.MaxwellSimTreeNode): + node_type = contracts.NodeType.LibraryMedium + + bl_label = "Library Medium" + #bl_icon = ... + + #################### + # - Sockets + #################### + input_sockets = {} + output_sockets = { + "medium": sockets.MaxwellMediumSocketDef( + label="Medium" + ), + } + + #################### + # - Properties + #################### + material: bpy.props.EnumProperty( + 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 + ]) + ) + for mat_key in td.material_library + if mat_key != "graphene" ## For some reason, it's unique... + ], + default="Au", + update=(lambda self,context: self.update()), + ) + + #################### + # - UI + #################### + def draw_props(self, context, layout): + layout.prop(self, "material", text="") + + def draw_info(self, context, layout): + layout.operator(ExperimentOperator00.bl_idname, text="Experiment") + vac_speed_of_light = sc.constants.speed_of_light * spu.meter/spu.second + + mat = td.material_library[self.material] + freq_range = [ + spu.convert_to( + val * spu.hertz, + spuex.terahertz, + ) / spuex.terahertz + for val in mat.medium.frequency_range + ] + nm_range = [ + spu.convert_to( + vac_speed_of_light / (val * spu.hertz), + spu.nanometer, + ) / spu.nanometer + for val in mat.medium.frequency_range + ] + + layout.label(text=f"nm: [{nm_range[1].n(2)}, {nm_range[0].n(2)}]") + layout.label(text=f"THz: [{freq_range[0].n(2)}, {freq_range[1].n(2)}]") + + #################### + # - Output Socket Computation + #################### + @base.computes_output_socket("medium") + def compute_medium(self: contracts.NodeTypeProtocol) -> td.AbstractMedium: + return td.material_library[self.material].medium + + #################### + # - Experiment + #################### + def invoke_matplotlib_and_update_image(self): + import matplotlib.pyplot as plt + mat = td.material_library[self.material] + + aspect_ratio = 1.0 + for area in bpy.context.screen.areas: + if area.type == 'IMAGE_EDITOR': + width = area.width + height = area.height + aspect_ratio = width / height + + # Generate a plot with matplotlib + fig_width = 6 + fig_height = fig_width / aspect_ratio + fig, ax = plt.subplots(figsize=(fig_width, fig_height)) + ax.set_aspect(aspect_ratio) + mat.medium.plot( + np.linspace(*mat.medium.frequency_range[:2], 50), + ax=ax, + ) + + # Save the plot to a temporary file + temp_plot_file = bpy.path.abspath('//temp_plot.png') + fig.savefig(temp_plot_file, bbox_inches='tight') + plt.close(fig) # Close the figure to free up memory + + # Load or reload the image in Blender + if "matplotlib_plot" in bpy.data.images: + image = bpy.data.images["matplotlib_plot"] + image.reload() + else: + image = bpy.data.images.load(temp_plot_file) + image.name = "matplotlib_plot" + + # Write the plot to an image datablock in Blender + for area in bpy.context.screen.areas: + if area.type == 'IMAGE_EDITOR': + for space in area.spaces: + if space.type == 'IMAGE_EDITOR': + space.image = image + return True + + + #################### # - Blender Registration #################### -BL_REGISTER = [] -BL_NODES = {} +BL_REGISTER = [ + ExperimentOperator00, + LibraryMediumNode, +] +BL_NODES = { + contracts.NodeType.LibraryMedium: ( + contracts.NodeCategory.MAXWELLSIM_MEDIUMS + ) +} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/json_file_exporter.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/json_file_exporter.py index 23bc9f0..1c2ab5b 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/json_file_exporter.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/exporters/json_file_exporter.py @@ -5,6 +5,7 @@ from pathlib import Path import bpy import sympy as sp import pydantic as pyd +import tidy3d as td from .... import contracts from .... import sockets @@ -26,6 +27,19 @@ class JSONFileExporterPrintJSON(bpy.types.Operator): print(node.linked_data_as_json()) return {'FINISHED'} +class JSONFileExporterMeshData(bpy.types.Operator): + bl_idname = "blender_maxwell.json_file_exporter_mesh_data" + bl_label = "Print any mesh data linked into a JSONFileExporterNode." + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + node = context.node + print(node.linked_mesh_data()) + return {'FINISHED'} + class JSONFileExporterSaveJSON(bpy.types.Operator): bl_idname = "blender_maxwell.json_file_exporter_save_json" bl_label = "Save the JSON of what's linked into a JSONFileExporterNode." @@ -69,6 +83,7 @@ class JSONFileExporterNode(base.MaxwellSimTreeNode): ) -> None: layout.operator(JSONFileExporterPrintJSON.bl_idname, text="Print") layout.operator(JSONFileExporterSaveJSON.bl_idname, text="Save") + layout.operator(JSONFileExporterMeshData.bl_idname, text="Mesh Info") #################### # - Methods @@ -85,10 +100,16 @@ class JSONFileExporterNode(base.MaxwellSimTreeNode): elif isinstance(data, pyd.BaseModel): return data.model_dump_json() - # Finally: Try json.dumps (might fail) else: json.dumps(data) + def linked_mesh_data(self) -> str | None: + if self.g_input_bl_socket("data").is_linked: + data: typ.Any = self.compute_input("data") + + if isinstance(data, td.Structure): + return data.geometry + def export_data_as_json(self) -> None: if (data := self.linked_data_as_json()): data_dict = json.loads(data) @@ -101,8 +122,9 @@ class JSONFileExporterNode(base.MaxwellSimTreeNode): #################### BL_REGISTER = [ JSONFileExporterPrintJSON, - JSONFileExporterNode, + JSONFileExporterMeshData, JSONFileExporterSaveJSON, + JSONFileExporterNode, ] BL_NODES = { contracts.NodeType.JSONFileExporter: ( diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/viewers/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/viewers/__init__.py index a520e38..23d3fcc 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/viewers/__init__.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/viewers/__init__.py @@ -1,11 +1,14 @@ +from . import viewer_3d from . import value_viewer from . import console_viewer BL_REGISTER = [ + *viewer_3d.BL_REGISTER, *value_viewer.BL_REGISTER, *console_viewer.BL_REGISTER, ] BL_NODES = { + **viewer_3d.BL_NODES, **value_viewer.BL_NODES, **console_viewer.BL_NODES, } diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/viewers/viewer_3d.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/viewers/viewer_3d.py new file mode 100644 index 0000000..8b1ab2c --- /dev/null +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/outputs/viewers/viewer_3d.py @@ -0,0 +1,50 @@ +import typing as typ +import json +from pathlib import Path + +import bpy +import sympy as sp +import pydantic as pyd +import tidy3d as td + +from .... import contracts +from .... import sockets +from ... import base + +INTERNAL_GEONODES = { + +} + +#################### +# - Node +#################### +class Viewer3DNode(base.MaxwellSimTreeNode): + node_type = contracts.NodeType.Viewer3D + + bl_label = "3D Viewer" + + input_sockets = { + "data": sockets.AnySocketDef( + label="Data", + ), + } + output_sockets = {} + + #################### + # - Update + #################### + def update_cb(self): + pass + + +#################### +# - Blender Registration +#################### +BL_REGISTER = [ + Viewer3DNode, +] +BL_NODES = { + contracts.NodeType.Viewer3D: ( + contracts.NodeCategory.MAXWELLSIM_OUTPUTS_VIEWERS + ) +} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/fdtd_sim.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/fdtd_sim.py index f08cbfc..10a9d57 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/fdtd_sim.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/simulations/fdtd_sim.py @@ -19,8 +19,8 @@ class FDTDSimNode(base.MaxwellSimTreeNode): "run_time": sockets.PhysicalTimeSocketDef( label="Run Time", ), - "size": sockets.PhysicalSize3DSocketDef( - label="Size", + "domain": sockets.PhysicalSize3DSocketDef( + label="Domain", ), "ambient_medium": sockets.MaxwellMediumSocketDef( label="Ambient Medium", @@ -37,7 +37,7 @@ class FDTDSimNode(base.MaxwellSimTreeNode): } output_sockets = { "fdtd_sim": sockets.MaxwellFDTDSimSocketDef( - label="Medium", + label="FDTD Sim", ), } @@ -47,17 +47,17 @@ class FDTDSimNode(base.MaxwellSimTreeNode): @base.computes_output_socket("fdtd_sim") def compute_simulation(self: contracts.NodeTypeProtocol) -> td.Simulation: _run_time = self.compute_input("run_time") - _size = self.compute_input("size") + _domain = self.compute_input("domain") ambient_medium = self.compute_input("ambient_medium") structures = [self.compute_input("structure")] sources = [self.compute_input("source")] bound = self.compute_input("bound") run_time = spu.convert_to(_run_time, spu.second) / spu.second - size = tuple(spu.convert_to(_size, spu.um) / spu.um) + domain = tuple(spu.convert_to(_domain, spu.um) / spu.um) return td.Simulation( - size=size, + size=domain, medium=ambient_medium, structures=structures, sources=sources, diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/plane_wave_source.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/plane_wave_source.py index 8f4a665..7898f6e 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/plane_wave_source.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/plane_wave_source.py @@ -1,6 +1,94 @@ +import math + +import tidy3d as td +import sympy as sp +import sympy.physics.units as spu + +from ... import contracts +from ... import sockets +from .. import base + +class PlaneWaveSourceNode(base.MaxwellSimTreeNode): + node_type = contracts.NodeType.PlaneWaveSource + + bl_label = "Plane Wave Source" + #bl_icon = ... + + #################### + # - Sockets + #################### + input_sockets = { + "temporal_shape": sockets.MaxwellTemporalShapeSocketDef( + label="Temporal Shape", + ), + "center": sockets.PhysicalPoint3DSocketDef( + label="Center", + ), + "size": sockets.PhysicalSize3DSocketDef( + label="Size", + ), + "direction": sockets.BoolSocketDef( + label="+ Direction?", + default_value=True, + ), + "angle_theta": sockets.PhysicalAngleSocketDef( + label="θ", + ), + "angle_phi": sockets.PhysicalAngleSocketDef( + label="φ", + ), + "angle_pol": sockets.PhysicalAngleSocketDef( + label="Pol Angle", + ), + } + output_sockets = { + "source": sockets.MaxwellSourceSocketDef( + label="Source", + ), + } + + #################### + # - Output Socket Computation + #################### + @base.computes_output_socket("source") + def compute_source(self: contracts.NodeTypeProtocol) -> td.PointDipole: + temporal_shape = self.compute_input("temporal_shape") + _center = self.compute_input("center") + _size = self.compute_input("size") + _direction = self.compute_input("direction") + _angle_theta = self.compute_input("angle_theta") + _angle_phi = self.compute_input("angle_phi") + _angle_pol = self.compute_input("angle_pol") + + center = tuple(spu.convert_to(_center, spu.um) / spu.um) + size = tuple( + 0 if val == 1.0 else math.inf + for val in spu.convert_to(_size, spu.um) / spu.um + ) + angle_theta = spu.convert_to(_angle_theta, spu.rad) / spu.rad + angle_phi = spu.convert_to(_angle_phi, spu.rad) / spu.rad + angle_pol = spu.convert_to(_angle_pol, spu.rad) / spu.rad + + return td.PlaneWave( + center=center, + size=size, + source_time=temporal_shape, + direction="+" if _direction else "-", + angle_theta=angle_theta, + angle_phi=angle_phi, + pol_angle=angle_pol, + ) + + + #################### # - Blender Registration #################### -BL_REGISTER = [] -BL_NODES = {} - +BL_REGISTER = [ + PlaneWaveSourceNode, +] +BL_NODES = { + contracts.NodeType.PlaneWaveSource: ( + contracts.NodeCategory.MAXWELLSIM_SOURCES + ) +} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/point_dipole_source.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/point_dipole_source.py index 608dcb3..80d72ba 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/point_dipole_source.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/point_dipole_source.py @@ -16,15 +16,19 @@ class PointDipoleSourceNode(base.MaxwellSimTreeNode): # - Sockets #################### input_sockets = { - #"polarization": sockets.PhysicalPolSocketDef( - # label="Polarization", - #), ## TODO: Exactly how to go about this... + "polarization": sockets.PhysicalPolSocketDef( + label="Polarization", + ), "temporal_shape": sockets.MaxwellTemporalShapeSocketDef( label="Temporal Shape", ), "center": sockets.PhysicalPoint3DSocketDef( label="Center", ), + "interpolate": sockets.BoolSocketDef( + label="Interpolate", + default_value=True, + ), } output_sockets = { "source": sockets.MaxwellSourceSocketDef( @@ -37,19 +41,18 @@ class PointDipoleSourceNode(base.MaxwellSimTreeNode): #################### @base.computes_output_socket("source") def compute_source(self: contracts.NodeTypeProtocol) -> td.PointDipole: + polarization = self.compute_input("polarization") temporal_shape = self.compute_input("temporal_shape") - _center = self.compute_input("center") - center = tuple(spu.convert_to(_center, spu.um) / spu.um) + interpolate = self.compute_input("interpolate") - cheating_pol = "Ex" - ## TODO: Fix + center = tuple(spu.convert_to(_center, spu.um) / spu.um) return td.PointDipole( center=center, source_time=temporal_shape, - interpolate=True, - polarization=cheating_pol, + interpolate=interpolate, + polarization=polarization, ) diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/continuous_wave_temporal_shape.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/continuous_wave_temporal_shape.py index 8f4a665..3547597 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/continuous_wave_temporal_shape.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/continuous_wave_temporal_shape.py @@ -1,6 +1,77 @@ +import tidy3d as td +import sympy as sp +import sympy.physics.units as spu + +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 = ... + + #################### + # - Sockets + #################### + input_sockets = { + #"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_std": sockets.PhysicalFreqSocketDef( + label="Freq STD", + ), + "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", + ), + } + + #################### + # - Output Socket Computation + #################### + @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") + + 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, + freq0=freq_center, + fwidth=freq_std, + offset=time_delay_rel_ang_freq, + ) + + + #################### # - Blender Registration #################### -BL_REGISTER = [] -BL_NODES = {} - +BL_REGISTER = [ + ContinuousWaveTemporalShapeNode, +] +BL_NODES = { + contracts.NodeType.ContinuousWaveTemporalShape: ( + contracts.NodeCategory.MAXWELLSIM_SOURCES_TEMPORALSHAPES + ) +} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/data_driven_temporal_shape.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/data_driven_temporal_shape.py deleted file mode 100644 index 8f4a665..0000000 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/data_driven_temporal_shape.py +++ /dev/null @@ -1,6 +0,0 @@ -#################### -# - Blender Registration -#################### -BL_REGISTER = [] -BL_NODES = {} - diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/gaussian_pulse_temporal_shape.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/gaussian_pulse_temporal_shape.py index 5d29bda..61d5687 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/gaussian_pulse_temporal_shape.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/sources/temporal_shapes/gaussian_pulse_temporal_shape.py @@ -30,6 +30,7 @@ class GaussianPulseTemporalShapeNode(base.MaxwellSimTreeNode): ), "time_delay_rel_ang_freq": sockets.RealNumberSocketDef( label="Time Delay rel. Ang. Freq", + default_value=5.0, ), "remove_dc_component": sockets.BoolSocketDef( label="Remove DC", diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/geonodes_structure.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/geonodes_structure.py index 63d5d42..b0eeaef 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/geonodes_structure.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/geonodes_structure.py @@ -4,6 +4,7 @@ import sympy as sp import sympy.physics.units as spu import bpy +from bpy_types import bpy_types import bmesh from ... import contracts @@ -12,6 +13,26 @@ from .. import base GEONODES_MODIFIER_NAME = "BLMaxwell_GeoNodes" +# Monkey-Patch Sympy Types +## TODO: This needs to be a more generic thing, this isn't the only place we're setting blender interface values. +def parse_scalar(scalar): + if isinstance(scalar, sp.Integer): + return int(scalar) + elif isinstance(scalar, sp.Float): + return float(scalar) + elif isinstance(scalar, sp.Rational): + return float(scalar) + elif isinstance(scalar, sp.Expr): + return float(scalar.n()) + + return scalar + +def parse_bl_to_sp(scalar): + if isinstance(scalar, bpy_types.bpy_prop_array): + return sp.Matrix(tuple(scalar)) + + return scalar + class GeoNodesStructureNode(base.MaxwellSimTreeNode): node_type = contracts.NodeType.GeoNodesStructure bl_label = "GeoNodes Structure" @@ -21,12 +42,15 @@ class GeoNodesStructureNode(base.MaxwellSimTreeNode): # - Sockets #################### input_sockets = { + "preview_target": sockets.BlenderPreviewTargetSocketDef( + label="Preview Target", + ), + "blender_unit_system": sockets.PhysicalUnitSystemSocketDef( + label="Blender Units", + ), "medium": sockets.MaxwellMediumSocketDef( label="Medium", ), - "object": sockets.BlenderObjectSocketDef( - label="Object", - ), "geo_nodes": sockets.BlenderGeoNodesSocketDef( label="GeoNodes", ), @@ -50,7 +74,10 @@ class GeoNodesStructureNode(base.MaxwellSimTreeNode): # Triangulate Object Mesh bmesh_mesh = bmesh.new() - bmesh_mesh.from_mesh(bl_object.data) + bmesh_mesh.from_object( + bl_object, + bpy.context.evaluated_depsgraph_get(), + ) bmesh.ops.triangulate(bmesh_mesh, faces=bmesh_mesh.faces) mesh = bpy.data.meshes.new(name="TriangulatedMesh") @@ -75,8 +102,13 @@ class GeoNodesStructureNode(base.MaxwellSimTreeNode): #################### # - Update Function #################### + def free(self) -> None: + bl_socket = self.g_input_bl_socket("preview_target") + + bl_socket.free() + def update_cb(self) -> None: - bl_object = self.compute_input("object") + bl_object = self.compute_input("preview_target") if bl_object is None: return geo_nodes = self.compute_input("geo_nodes") @@ -91,9 +123,12 @@ class GeoNodesStructureNode(base.MaxwellSimTreeNode): ): if idx == 0: continue ## Always-on "Geometry" Input (from Object) + # Retrieve Input Socket bl_socket = self.inputs[ interface_item.name ] + + # Retrieve Linked/Unlinked Input Socket Value if bl_socket.is_linked: linked_bl_socket = bl_socket.links[0].from_socket linked_bl_node = bl_socket.links[0].from_node @@ -103,13 +138,33 @@ class GeoNodesStructureNode(base.MaxwellSimTreeNode): ) ) ## What a bunch of spaghetti else: - val = self.inputs[ - interface_item.name - ].default_value + val = bl_socket.default_value + + # Retrieve Unit-System Corrected Modifier Value + bl_unit_system = self.compute_input("blender_unit_system") + + socket_type = contracts.SocketType[ + bl_socket.bl_idname.removesuffix("SocketType") + ] + if socket_type in bl_unit_system: + unitless_val = spu.convert_to( + val, + bl_unit_system[socket_type], + ) / bl_unit_system[socket_type] + else: + unitless_val = val + + if isinstance(unitless_val, sp.matrices.MatrixBase): + unitless_val = tuple( + parse_scalar(scalar) + for scalar in unitless_val + ) + else: + unitless_val = parse_scalar(unitless_val) # Conservatively Set Differing Values - if bl_modifier[interface_item.identifier] != val: - bl_modifier[interface_item.identifier] = val + if bl_modifier[interface_item.identifier] != unitless_val: + bl_modifier[interface_item.identifier] = unitless_val # Update DepGraph bl_object.data.update() @@ -134,23 +189,77 @@ class GeoNodesStructureNode(base.MaxwellSimTreeNode): self.inputs.remove(bl_socket) # Query for Blender Object / Geo Nodes - bl_object = self.compute_input("object") + bl_object = self.compute_input("preview_target") if bl_object is None: return - ## TODO: Make object? Gray out geonodes if object not defined? + # Remove Existing GeoNodes Modifier + if GEONODES_MODIFIER_NAME in bl_object.modifiers: + modifier_to_remove = bl_object.modifiers[GEONODES_MODIFIER_NAME] + bl_object.modifiers.remove(modifier_to_remove) + + # Retrieve GeoNodes Tree geo_nodes = self.compute_input("geo_nodes") if geo_nodes is None: return - # Add Non-Static Sockets from GeoNodes for bl_socket_name, bl_socket in geo_nodes.interface.items_tree.items(): # For now, don't allow Geometry inputs. if bl_socket.socket_type == "NodeSocketGeometry": continue - self.inputs.new( - contracts.BLNodeSocket_to_SocketType[bl_socket.socket_type], - bl_socket_name, - ) + # Establish Dimensions of GeoNodes Input Sockets + if ( + bl_socket.description.startswith("2D") + ): + dimensions = 2 + elif ( + bl_socket.socket_type.startswith("NodeSocketVector") + or bl_socket.socket_type.startswith("NodeSocketColor") + or bl_socket.socket_type.startswith("NodeSocketRotation") + ): + dimensions = 3 + else: + dimensions = 1 + + # Choose Socket via. Description Hint (if exists) + if ( + ":" in bl_socket.description + and "(" in (desc_hint := bl_socket.description.split(":")[0]) + and ")" in desc_hint + ): + for tag in contracts.BLNodeSocket_to_SocketType_by_desc[ + dimensions + ]: + if desc_hint.startswith(tag): + self.inputs.new( + contracts.BLNodeSocket_to_SocketType_by_desc[ + dimensions + ][tag], + bl_socket_name, + ) + + if len([ + (unit := _unit) + for _unit in contracts.SocketType_to_units[ + contracts.SocketType[ + self.inputs[bl_socket_name].bl_idname.removesuffix("SocketType") + ] + ]["values"].values() + if desc_hint[ + desc_hint.find("(")+1 : desc_hint.find(")") + ] == str(_unit) + ]) > 0: + self.inputs[bl_socket_name].unit = unit + + elif bl_socket.socket_type in contracts.BLNodeSocket_to_SocketType[ + dimensions + ]: + self.inputs.new( + contracts.BLNodeSocket_to_SocketType[ + dimensions + ][bl_socket.socket_type], + bl_socket_name, + ) + # Create New GeoNodes Modifier if GEONODES_MODIFIER_NAME not in bl_object.modifiers: @@ -160,7 +269,27 @@ class GeoNodesStructureNode(base.MaxwellSimTreeNode): ) modifier.node_group = geo_nodes - self.update() + # Set Default Values + for interface_item in geo_nodes.interface.items_tree.values(): + if ( + interface_item.name in self.inputs + and hasattr(interface_item, "default_value") + ): + bl_socket = self.inputs[ + interface_item.name + ] + if hasattr(bl_socket, "use_units"): + bl_unit_system = self.compute_input("blender_unit_system") + socket_type = contracts.SocketType[ + bl_socket.bl_idname.removesuffix("SocketType") + ] + + bl_socket.default_value = ( + parse_bl_to_sp(interface_item.default_value) + * bl_unit_system[socket_type] + ) + else: + bl_socket.default_value = parse_bl_to_sp(interface_item.default_value) diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/object_structure.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/object_structure.py index bdc51e7..e02fa35 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/object_structure.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/object_structure.py @@ -45,7 +45,10 @@ class ObjectStructureNode(base.MaxwellSimTreeNode): # Triangulate Object Mesh bmesh_mesh = bmesh.new() - bmesh_mesh.from_mesh(bl_object.data) + bmesh_mesh.from_object( + bl_object, + bpy.context.evaluated_depsgraph_get(), + ) bmesh.ops.triangulate(bmesh_mesh, faces=bmesh_mesh.faces) mesh = bpy.data.meshes.new(name="TriangulatedMesh") @@ -62,7 +65,6 @@ class ObjectStructureNode(base.MaxwellSimTreeNode): # Remove Temporary Mesh bpy.data.meshes.remove(mesh) - print(vertices) return td.Structure( geometry=td.TriangleMesh.from_vertices_faces(vertices, faces), medium=self.compute_input("medium") diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/box_structure.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/box_structure.py index 56c0961..2a0c141 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/box_structure.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/box_structure.py @@ -61,6 +61,6 @@ BL_REGISTER = [ ] BL_NODES = { contracts.NodeType.BoxStructure: ( - contracts.NodeCategory.MAXWELLSIM_STRUCTURES + contracts.NodeCategory.MAXWELLSIM_STRUCTURES_PRIMITIVES ) } diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/cylinder_structure.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/cylinder_structure.py index 8f4a665..f5ce682 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/cylinder_structure.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/cylinder_structure.py @@ -1,6 +1,72 @@ +import tidy3d as td +import sympy as sp +import sympy.physics.units as spu + +from .... import contracts +from .... import sockets +from ... import base + +class CylinderStructureNode(base.MaxwellSimTreeNode): + node_type = contracts.NodeType.CylinderStructure + bl_label = "Cylinder Structure" + #bl_icon = ... + + #################### + # - Sockets + #################### + input_sockets = { + "medium": sockets.MaxwellMediumSocketDef( + label="Medium", + ), + "center": sockets.PhysicalPoint3DSocketDef( + label="Center", + ), + "radius": sockets.PhysicalLengthSocketDef( + label="Radius", + ), + "height": sockets.PhysicalLengthSocketDef( + label="Height", + ), + } + output_sockets = { + "structure": sockets.MaxwellStructureSocketDef( + label="Structure", + ), + } + + #################### + # - Output Socket Computation + #################### + @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") + + 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, + center=center, + length=height, + ), + medium=medium, + ) + + + #################### # - Blender Registration #################### -BL_REGISTER = [] -BL_NODES = {} - +BL_REGISTER = [ + CylinderStructureNode, +] +BL_NODES = { + contracts.NodeType.CylinderStructure: ( + contracts.NodeCategory.MAXWELLSIM_STRUCTURES_PRIMITIVES + ) +} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/sphere_structure.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/sphere_structure.py index 8f4a665..16dad4f 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/sphere_structure.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/structures/primitives/sphere_structure.py @@ -1,6 +1,66 @@ +import tidy3d as td +import sympy as sp +import sympy.physics.units as spu + +from .... import contracts +from .... import sockets +from ... import base + +class SphereStructureNode(base.MaxwellSimTreeNode): + node_type = contracts.NodeType.SphereStructure + bl_label = "Sphere Structure" + #bl_icon = ... + + #################### + # - Sockets + #################### + input_sockets = { + "medium": sockets.MaxwellMediumSocketDef( + label="Medium", + ), + "center": sockets.PhysicalPoint3DSocketDef( + label="Center", + ), + "radius": sockets.PhysicalLengthSocketDef( + label="Radius", + ), + } + output_sockets = { + "structure": sockets.MaxwellStructureSocketDef( + label="Structure", + ), + } + + #################### + # - Output Socket Computation + #################### + @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") + + 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, + center=center, + ), + medium=medium, + ) + + + #################### # - Blender Registration #################### -BL_REGISTER = [] -BL_NODES = {} - +BL_REGISTER = [ + SphereStructureNode, +] +BL_NODES = { + contracts.NodeType.SphereStructure: ( + contracts.NodeCategory.MAXWELLSIM_STRUCTURES_PRIMITIVES + ) +} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/__init__.py index c8540a7..5c4663e 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/__init__.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/__init__.py @@ -1,11 +1,23 @@ +from . import combine +#from . import separate + from . import math from . import operations +from . import converter BL_REGISTER = [ + *combine.BL_REGISTER, + #*separate.BL_REGISTER, + + *converter.BL_REGISTER, *math.BL_REGISTER, *operations.BL_REGISTER, ] BL_NODES = { + **combine.BL_NODES, + #**separate.BL_NODES, + + **converter.BL_NODES, **math.BL_NODES, **operations.BL_NODES, } diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/combine.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/combine.py new file mode 100644 index 0000000..e77f1aa --- /dev/null +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/combine.py @@ -0,0 +1,114 @@ +import sympy as sp +import sympy.physics.units as spu +import scipy as sc + +from ... import contracts +from ... import sockets +from .. import base + +class CombineNode(base.MaxwellSimTreeNode): + node_type = contracts.NodeType.Combine + bl_label = "Combine" + #bl_icon = ... + + #################### + # - Sockets + #################### + input_sockets = {} + input_socket_sets = { + "real_3d_vector": { + f"x_{i}": sockets.RealNumberSocketDef( + label=f"x_{i}" + ) + for i in range(3) + }, + "point_3d": { + axis: sockets.PhysicalLengthSocketDef( + label=axis + ) + for i, axis in zip( + range(3), + ["x", "y", "z"] + ) + }, + "size_3d": { + axis_key: sockets.PhysicalLengthSocketDef( + label=axis_label + ) + for i, axis_key, axis_label in zip( + range(3), + ["x_size", "y_size", "z_size"], + ["X Size", "Y Size", "Z Size"], + ) + }, + } + output_sockets = {} + output_socket_sets = { + "real_3d_vector": { + "real_3d_vector": sockets.Real3DVectorSocketDef( + label="Real 3D Vector", + ), + }, + "point_3d": { + "point_3d": sockets.PhysicalPoint3DSocketDef( + label="3D Point", + ), + }, + "size_3d": { + "size_3d": sockets.PhysicalSize3DSocketDef( + label="3D Size", + ), + }, + } + + #################### + # - Output Socket Computation + #################### + @base.computes_output_socket("real_3d_vector") + def compute_real_3d_vector(self: contracts.NodeTypeProtocol) -> sp.Expr: + x1, x2, x3 = [ + self.compute_input(f"x_{i}") + for i in range(3) + ] + + return (x1, x2, x3) + + @base.computes_output_socket("point_3d") + def compute_point_3d(self: contracts.NodeTypeProtocol) -> sp.Expr: + x, y, z = [ + self.compute_input(axis) + #spu.convert_to( + # self.compute_input(axis), + # spu.meter, + #) / spu.meter + for axis in ["x", "y", "z"] + ] + + return sp.Matrix([x, y, z])# * spu.meter + + @base.computes_output_socket("size_3d") + def compute_size_3d(self: contracts.NodeTypeProtocol) -> sp.Expr: + x_size, y_size, z_size = [ + self.compute_input(axis) + #spu.convert_to( + # self.compute_input(axis), + # spu.meter, + #) / spu.meter + for axis in ["x_size", "y_size", "z_size"] + ] + + return sp.Matrix([x_size, y_size, z_size])# * spu.meter + + + +#################### +# - Blender Registration +#################### +BL_REGISTER = [ + CombineNode, +] +BL_NODES = { + contracts.NodeType.Combine: ( + contracts.NodeCategory.MAXWELLSIM_UTILITIES + ) +} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/converter/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/converter/__init__.py new file mode 100644 index 0000000..bfb76be --- /dev/null +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/converter/__init__.py @@ -0,0 +1,8 @@ +from . import wave_converter + +BL_REGISTER = [ + *wave_converter.BL_REGISTER, +] +BL_NODES = { + **wave_converter.BL_NODES, +} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/converter/separate.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/converter/separate.py new file mode 100644 index 0000000..8db2d15 --- /dev/null +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/converter/separate.py @@ -0,0 +1,82 @@ +import tidy3d as td +import sympy as sp +import sympy.physics.units as spu +import scipy as sc + +from .... import contracts +from .... import sockets +from ... import base + +class WaveConverterNode(base.MaxwellSimTreeNode): + node_type = contracts.NodeType.WaveConverter + bl_label = "Wave Converter" + #bl_icon = ... + + #################### + # - Sockets + #################### + input_sockets = {} + input_socket_sets = { + "freq_to_vacwl": { + "freq": sockets.PhysicalFreqSocketDef( + label="Freq", + ), + }, + "vacwl_to_freq": { + "vacwl": sockets.PhysicalVacWLSocketDef( + label="Vac WL", + ), + }, + } + output_sockets = {} + output_socket_sets = { + "freq_to_vacwl": { + "vacwl": sockets.PhysicalVacWLSocketDef( + label="Vac WL", + ), + }, + "vacwl_to_freq": { + "freq": sockets.PhysicalFreqSocketDef( + label="Freq", + ), + }, + } + + #################### + # - Output Socket Computation + #################### + @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") + + return spu.convert_to( + vac_speed_of_light / vacwl, + spu.hertz, + ) + + @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") + + return spu.convert_to( + vac_speed_of_light / freq, + spu.meter, + ) + + + +#################### +# - Blender Registration +#################### +BL_REGISTER = [ + WaveConverterNode, +] +BL_NODES = { + contracts.NodeType.WaveConverter: ( + contracts.NodeCategory.MAXWELLSIM_UTILITIES_CONVERTERS + ) +} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/converter/wave_converter.py b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/converter/wave_converter.py new file mode 100644 index 0000000..8db2d15 --- /dev/null +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/nodes/utilities/converter/wave_converter.py @@ -0,0 +1,82 @@ +import tidy3d as td +import sympy as sp +import sympy.physics.units as spu +import scipy as sc + +from .... import contracts +from .... import sockets +from ... import base + +class WaveConverterNode(base.MaxwellSimTreeNode): + node_type = contracts.NodeType.WaveConverter + bl_label = "Wave Converter" + #bl_icon = ... + + #################### + # - Sockets + #################### + input_sockets = {} + input_socket_sets = { + "freq_to_vacwl": { + "freq": sockets.PhysicalFreqSocketDef( + label="Freq", + ), + }, + "vacwl_to_freq": { + "vacwl": sockets.PhysicalVacWLSocketDef( + label="Vac WL", + ), + }, + } + output_sockets = {} + output_socket_sets = { + "freq_to_vacwl": { + "vacwl": sockets.PhysicalVacWLSocketDef( + label="Vac WL", + ), + }, + "vacwl_to_freq": { + "freq": sockets.PhysicalFreqSocketDef( + label="Freq", + ), + }, + } + + #################### + # - Output Socket Computation + #################### + @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") + + return spu.convert_to( + vac_speed_of_light / vacwl, + spu.hertz, + ) + + @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") + + return spu.convert_to( + vac_speed_of_light / freq, + spu.meter, + ) + + + +#################### +# - Blender Registration +#################### +BL_REGISTER = [ + WaveConverterNode, +] +BL_NODES = { + contracts.NodeType.WaveConverter: ( + contracts.NodeCategory.MAXWELLSIM_UTILITIES_CONVERTERS + ) +} diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/__init__.py index c0a2853..32f71d1 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/__init__.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/__init__.py @@ -17,6 +17,7 @@ Real3DVectorSocketDef = vector.Real3DVectorSocketDef Complex3DVectorSocketDef = vector.Complex3DVectorSocketDef from . import physical +PhysicalUnitSystemSocketDef = physical.PhysicalUnitSystemSocketDef PhysicalTimeSocketDef = physical.PhysicalTimeSocketDef PhysicalAngleSocketDef = physical.PhysicalAngleSocketDef PhysicalLengthSocketDef = physical.PhysicalLengthSocketDef @@ -30,6 +31,7 @@ PhysicalAccelScalarSocketDef = physical.PhysicalAccelScalarSocketDef PhysicalForceScalarSocketDef = physical.PhysicalForceScalarSocketDef PhysicalPolSocketDef = physical.PhysicalPolSocketDef PhysicalFreqSocketDef = physical.PhysicalFreqSocketDef +PhysicalVacWLSocketDef = physical.PhysicalVacWLSocketDef PhysicalSpecRelPermDistSocketDef = physical.PhysicalSpecRelPermDistSocketDef PhysicalSpecPowerDistSocketDef = physical.PhysicalSpecPowerDistSocketDef @@ -40,6 +42,7 @@ BlenderImageSocketDef = blender.BlenderImageSocketDef BlenderVolumeSocketDef = blender.BlenderVolumeSocketDef BlenderGeoNodesSocketDef = blender.BlenderGeoNodesSocketDef BlenderTextSocketDef = blender.BlenderTextSocketDef +BlenderPreviewTargetSocketDef = blender.BlenderPreviewTargetSocketDef from . import maxwell MaxwellBoundBoxSocketDef = maxwell.MaxwellBoundBoxSocketDef diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py index 0437595..de16042 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/base.py @@ -235,4 +235,23 @@ class BLSocket(bpy.types.NodeSocket): node: bpy.types.Node, text: str, ) -> None: - layout.label(text=text) + col = layout.column() + row_col = col.row() + row_col.alignment = "RIGHT" + # Row: Label & Preview Toggle + if hasattr(self, "draw_preview"): + row_col.prop( + self, + "preview_active", + toggle=True, + text="", + icon="SEQ_PREVIEW", + ) + + row_col.label(text=text) + + # Row: Preview (in box) + if hasattr(self, "draw_preview"): + if self.preview_active: + col_box = col.box() + self.draw_preview(col_box) diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/__init__.py index 9442a56..479e52c 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/__init__.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/__init__.py @@ -13,6 +13,8 @@ from . import text_socket BlenderGeoNodesSocketDef = geonodes_socket.BlenderGeoNodesSocketDef BlenderTextSocketDef = text_socket.BlenderTextSocketDef +from . import target_socket +BlenderPreviewTargetSocketDef = target_socket.BlenderPreviewTargetSocketDef BL_REGISTER = [ *object_socket.BL_REGISTER, @@ -20,6 +22,7 @@ BL_REGISTER = [ *image_socket.BL_REGISTER, *volume_socket.BL_REGISTER, + *target_socket.BL_REGISTER, *geonodes_socket.BL_REGISTER, *text_socket.BL_REGISTER, diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/geonodes_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/geonodes_socket.py index 2862200..277758d 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/geonodes_socket.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/geonodes_socket.py @@ -6,6 +6,19 @@ import pydantic as pyd from .. import base from ... import contracts +#################### +# - Operators +#################### +class BlenderMaxwellResetGeoNodesSocket(bpy.types.Operator): + bl_idname = "blender_maxwell.reset_geo_nodes_socket" + bl_label = "Reset GeoNodes Socket" + + def execute(self, context): + context.socket.update_geonodes_node() + + return {'FINISHED'} + + #################### # - Blender Socket #################### @@ -33,6 +46,18 @@ class BlenderGeoNodesBLSocket(base.BLSocket): update=(lambda self, context: self.update_geonodes_node()), ) + #################### + # - UI + #################### + def draw_label_row(self, label_col_row, text): + label_col_row.label(text=text) + if self.raw_value: + label_col_row.operator( + "blender_maxwell.reset_geo_nodes_socket", + text="", + icon="FILE_REFRESH", + ) + #################### # - Default Value #################### @@ -58,5 +83,6 @@ class BlenderGeoNodesSocketDef(pyd.BaseModel): # - Blender Registration #################### BL_REGISTER = [ - BlenderGeoNodesBLSocket + BlenderMaxwellResetGeoNodesSocket, + BlenderGeoNodesBLSocket, ] diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/object_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/object_socket.py index 358daa5..d272726 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/object_socket.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/object_socket.py @@ -6,6 +6,29 @@ import pydantic as pyd from .. import base from ... import contracts +#################### +# - Blender Socket +#################### +class BlenderMaxwellCreateAndAssignBLObject(bpy.types.Operator): + bl_idname = "blender_maxwell.create_and_assign_bl_object" + bl_label = "Create and Assign BL Object" + + def execute(self, context): + mesh = bpy.data.meshes.new("GenMesh") + new_bl_object = bpy.data.objects.new("GenObj", mesh) + + context.collection.objects.link(new_bl_object) + + node = context.node + for bl_socket_name, bl_socket in node.inputs.items(): + if isinstance(bl_socket, BlenderObjectBLSocket): + bl_socket.default_value = new_bl_object + + if hasattr(node, "update_sockets_from_geonodes"): + node.update_sockets_from_geonodes() + + return {'FINISHED'} + #################### # - Blender Socket #################### @@ -23,6 +46,17 @@ class BlenderObjectBLSocket(base.BLSocket): update=(lambda self, context: self.trigger_updates()), ) + #################### + # - UI + #################### + def draw_label_row(self, label_col_row, text): + label_col_row.label(text=text) + label_col_row.operator( + "blender_maxwell.create_and_assign_bl_object", + text="", + icon="ADD", + ) + #################### # - Default Value #################### @@ -48,5 +82,6 @@ class BlenderObjectSocketDef(pyd.BaseModel): # - Blender Registration #################### BL_REGISTER = [ - BlenderObjectBLSocket + BlenderMaxwellCreateAndAssignBLObject, + BlenderObjectBLSocket, ] diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/target_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/target_socket.py new file mode 100644 index 0000000..66c4dcd --- /dev/null +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/blender/target_socket.py @@ -0,0 +1,268 @@ +import typing as typ + +import bpy +import sympy as sp +import pydantic as pyd + +from .. import base +from ... import contracts + +def mk_and_assign_target_bl_obj(bl_socket, node, node_tree): + # Create Mesh and Object + mesh = bpy.data.meshes.new("Mesh" + bl_socket.node.name) + new_bl_object = bpy.data.objects.new(bl_socket.node.name, mesh) + + # Create Preview Collection and Object + if bl_socket.show_preview: + #if not node_tree.preview_collection: + # new_collection = bpy.data.collections.new("BLMaxwellPreview") + # node_tree.preview_collection = new_collection + # + # bpy.context.scene.collection.children.link(new_collection) + + node_tree.preview_collection.objects.link(new_bl_object) + + # Create Non-Preview Collection and Object + else: + #if not node_tree.non_preview_collection: + # new_collection = bpy.data.collections.new("BLMaxwellNonPreview") + # node_tree.non_preview_collection = new_collection + # + # bpy.context.scene.collection.children.link(new_collection) + + node_tree.non_preview_collection.objects.link(new_bl_object) + + bl_socket.local_target_object = new_bl_object + + if hasattr(node, "update_sockets_from_geonodes"): + node.update_sockets_from_geonodes() + +class BlenderMaxwellCreateAndAssignTargetBLObject(bpy.types.Operator): + bl_idname = "blender_maxwell.create_and_assign_target_bl_object" + bl_label = "Create and Assign Target BL Object" + + def execute(self, context): + bl_socket = context.socket + node = bl_socket.node + node_tree = node.id_data + + mk_and_assign_target_bl_obj(bl_socket, node, node_tree) + return {'FINISHED'} + +#################### +# - Blender Socket +#################### +class BlenderPreviewTargetBLSocket(base.BLSocket): + socket_type = contracts.SocketType.BlenderPreviewTarget + bl_label = "BlenderPreviewTarget" + + #################### + # - Properties + #################### + show_preview: bpy.props.BoolProperty( + name="Target Object Included in Preview", + description="Whether or not Blender will preview the target object", + default=True, + update=(lambda self, context: self.update_preview()), + ) + show_definition: bpy.props.BoolProperty( + name="Show Unit System Definition", + description="Toggle to show unit system definition", + default=False, + update=(lambda self, context: self.trigger_updates()), + ) + + + target_object_pinned: bpy.props.BoolProperty( + name="Target Object Pinned", + description="Whether or not Blender will manage the target object", + default=True, + ) + preview_collection_pinned: bpy.props.BoolProperty( + name="Global Preview Collection Pinned", + description="Whether or not Blender will use the global preview collection", + default=True, + ) + non_preview_collection_pinned: bpy.props.BoolProperty( + name="Global Non-Preview Collection Pinned", + description="Whether or not Blender will use the global non-preview collection", + default=True, + ) + + + local_target_object: bpy.props.PointerProperty( + name="Local Target Blender Object", + description="Represents a Blender object to apply a preview to", + type=bpy.types.Object, + update=(lambda self, context: self.trigger_updates()), + ) + local_preview_collection: bpy.props.PointerProperty( + name="Local Preview Collection", + description="Collection of Blender objects that will be previewed", + type=bpy.types.Collection, + update=(lambda self, context: self.trigger_updates()) + ) + local_non_preview_collection: bpy.props.PointerProperty( + name="Local Non-Preview Collection", + description="Collection of Blender objects that will NOT be previewed", + type=bpy.types.Collection, + update=(lambda self, context: self.trigger_updates()) + ) + + #################### + # - Methods + #################### + def update_preview(self): + node_tree = self.node.id_data + + # Target Object Pinned + if ( + self.show_preview + and self.local_target_object + and self.target_object_pinned + ): + node_tree.non_preview_collection.objects.unlink(self.local_target_object) + node_tree.preview_collection.objects.link(self.local_target_object) + + elif ( + not self.show_preview + and self.local_target_object + and self.target_object_pinned + ): + node_tree.preview_collection.objects.unlink(self.local_target_object) + node_tree.non_preview_collection.objects.link(self.local_target_object) + + # Target Object Not Pinned + if ( + self.show_preview + and self.local_target_object + and not self.target_object_pinned + and self.local_target_object.name in ( + node_tree.non_preview_collection.objects.keys() + ) + ): + node_tree.non_preview_collection.objects.unlink(self.local_target_object) + node_tree.preview_collection.objects.link(self.local_target_object) + elif ( + not self.show_preview + and self.local_target_object + and not self.target_object_pinned + and self.local_target_object.name in ( + node_tree.preview_collection.objects.keys() + ) + ): + node_tree.preview_collection.objects.unlink(self.local_target_object) + node_tree.non_preview_collection.objects.link(self.local_target_object) + + self.trigger_updates() + + #################### + # - UI + #################### + def draw_label_row(self, label_col_row: bpy.types.UILayout, text) -> None: + label_col_row.label(text=text) + label_col_row.prop(self, "show_preview", toggle=True, text="", icon="SEQ_PREVIEW") + label_col_row.prop(self, "show_definition", toggle=True, text="", icon="MOD_LENGTH") + + def draw_value(self, col: bpy.types.UILayout) -> None: + node_tree = self.node.id_data + + if self.show_definition: + col_row = col.row(align=True) + col_row.alignment = "EXPAND" + col_row.label(text="Target", icon="OBJECT_DATA") + col_row.prop( + self, + "target_object_pinned", + toggle=True, + icon="EVENT_A", + icon_only=True, + ) + #col_row.operator( + # "blender_maxwell.create_and_assign_target_bl_object", + # text="", + # icon="ADD", + #) + + col_row=col.row(align=True) + col_row.alignment = "EXPAND" + if not self.target_object_pinned: + col_row.prop(self, "local_target_object", text="") + + # Non-Preview Collection + col_row=col.row(align=True) + col_row.alignment = "EXPAND" + col_row.label(text="Enabled", icon="COLLECTION_COLOR_04") + col_row.prop( + self, + "preview_collection_pinned", + toggle=True, + icon="PINNED", + icon_only=True, + ) + + col_row=col.row(align=True) + col_row.alignment = "EXPAND" + if not self.preview_collection_pinned: + col_row.prop(self, "local_preview_collection", text="") + + # Non-Preview Collection + col_row=col.row(align=True) + col_row.alignment = "EXPAND" + col_row.label(text="Disabled", icon="COLLECTION_COLOR_01") + col_row.prop( + self, + "non_preview_collection_pinned", + toggle=True, + icon="PINNED", + icon_only=True, + ) + + col_row=col.row(align=True) + col_row.alignment = "EXPAND" + if not self.non_preview_collection_pinned: + col_row.prop(self, "local_non_preview_collection", text="") + + #################### + # - Default Value + #################### + @property + def default_value(self) -> bpy.types.Object: + node_tree = self.node.id_data + if not self.local_target_object and self.target_object_pinned: + mk_and_assign_target_bl_obj(self, self.node, node_tree) + return self.local_target_object + + return self.local_target_object + + @default_value.setter + def default_value(self, value: typ.Any) -> None: + pass + + #################### + # - Cleanup + #################### + def free(self) -> None: + if self.local_target_object: + bpy.data.meshes.remove(self.local_target_object.data, do_unlink=True) + +#################### +# - Socket Configuration +#################### +class BlenderPreviewTargetSocketDef(pyd.BaseModel): + socket_type: contracts.SocketType = contracts.SocketType.BlenderPreviewTarget + label: str + + show_preview: bool = True + + def init(self, bl_socket: BlenderPreviewTargetBLSocket) -> None: + pass + #bl_socket.show_preview = self.show_preview + +#################### +# - Blender Registration +#################### +BL_REGISTER = [ + BlenderMaxwellCreateAndAssignTargetBLObject, + BlenderPreviewTargetBLSocket, +] diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_box_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_box_socket.py index 83edf0b..69b4878 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_box_socket.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_box_socket.py @@ -7,6 +7,13 @@ import tidy3d as td from .. import base from ... import contracts +BOUND_FACE_ITEMS = [ + ("PML", "PML", "Perfectly matched layer"), + ("PEC", "PEC", "Perfect electrical conductor"), + ("PMC", "PMC", "Perfect magnetic conductor"), + ("PERIODIC", "Periodic", "Infinitely periodic layer"), +] + class MaxwellBoundBoxBLSocket(base.BLSocket): socket_type = contracts.SocketType.MaxwellBoundBox bl_label = "Maxwell Bound Box" @@ -15,6 +22,72 @@ class MaxwellBoundBoxBLSocket(base.BLSocket): td.BoundarySpec: {} } + #################### + # - Properties + #################### + x_pos: bpy.props.EnumProperty( + name="+x Bound Face", + description="+x choice of default boundary face", + items=BOUND_FACE_ITEMS, + default="PML", + update=(lambda self, context: self.trigger_updates()), + ) + x_neg: bpy.props.EnumProperty( + name="-x Bound Face", + description="-x choice of default boundary face", + items=BOUND_FACE_ITEMS, + default="PML", + update=(lambda self, context: self.trigger_updates()), + ) + y_pos: bpy.props.EnumProperty( + name="+y Bound Face", + description="+y choice of default boundary face", + items=BOUND_FACE_ITEMS, + default="PML", + update=(lambda self, context: self.trigger_updates()), + ) + y_neg: bpy.props.EnumProperty( + name="-y Bound Face", + description="-y choice of default boundary face", + items=BOUND_FACE_ITEMS, + default="PML", + update=(lambda self, context: self.trigger_updates()), + ) + z_pos: bpy.props.EnumProperty( + name="+z Bound Face", + description="+z choice of default boundary face", + items=BOUND_FACE_ITEMS, + default="PML", + update=(lambda self, context: self.trigger_updates()), + ) + z_neg: bpy.props.EnumProperty( + name="-z Bound Face", + description="-z choice of default boundary face", + items=BOUND_FACE_ITEMS, + default="PML", + update=(lambda self, context: self.trigger_updates()), + ) + + #################### + # - UI + #################### + def draw_value(self, col: bpy.types.UILayout) -> None: + col.label(text="-/+ x") + col_row = col.row(align=True) + col_row.prop(self, "x_neg", text="") + col_row.prop(self, "x_pos", text="") + + col.label(text="-/+ y") + col_row = col.row(align=True) + col_row.prop(self, "y_neg", text="") + col_row.prop(self, "y_pos", text="") + + col.label(text="-/+ z") + col_row = col.row(align=True) + col_row.prop(self, "z_neg", text="") + col_row.prop(self, "z_pos", text="") + + #################### # - Computation of Default Value #################### diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_face_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_face_socket.py index bab67ae..9e3fc0d 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_face_socket.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/maxwell/bound_face_socket.py @@ -11,16 +11,40 @@ class MaxwellBoundFaceBLSocket(base.BLSocket): socket_type = contracts.SocketType.MaxwellBoundFace bl_label = "Maxwell Bound Face" - compatible_types = { - td.BoundarySpec: {} - } + #################### + # - Properties + #################### + default_choice: bpy.props.EnumProperty( + 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"), + ], + default="PML", + update=(lambda self, context: self.trigger_updates()), + ) + + #################### + # - UI + #################### + def draw_value(self, col: bpy.types.UILayout) -> None: + col_row = col.row(align=True) + col_row.prop(self, "default_choice", text="") #################### # - Computation of Default Value #################### @property def default_value(self) -> td.BoundarySpec: - return td.BoundarySpec() + return { + "PML": td.PML(num_layers=12), + "PEC": td.PECBoundary(), + "PMC": td.PMCBoundary(), + "PERIODIC": td.Periodic(), + }[self.default_choice] @default_value.setter def default_value(self, value: typ.Any) -> None: @@ -33,8 +57,10 @@ class MaxwellBoundFaceSocketDef(pyd.BaseModel): socket_type: contracts.SocketType = contracts.SocketType.MaxwellBoundFace label: str + default_choice: str = "PML" + def init(self, bl_socket: MaxwellBoundFaceBLSocket) -> None: - pass + bl_socket.default_choice = self.default_choice #################### # - Blender Registration diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/integer_number_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/integer_number_socket.py index 4357514..f29f4b2 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/integer_number_socket.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/number/integer_number_socket.py @@ -1,5 +1,6 @@ import typing as typ +import bpy import pydantic as pyd from .. import base @@ -12,16 +13,26 @@ class IntegerNumberBLSocket(base.BLSocket): socket_type = contracts.SocketType.IntegerNumber bl_label = "IntegerNumber" + #################### + # - Properties + #################### + raw_value: bpy.props.IntProperty( + name="Integer", + description="Represents an integer", + default=0, + update=(lambda self, context: self.trigger_updates()), + ) + #################### # - Default Value #################### @property def default_value(self) -> None: - pass + return self.raw_value @default_value.setter def default_value(self, value: typ.Any) -> None: - pass + self.raw_value = int(value) #################### # - Socket Configuration @@ -30,8 +41,10 @@ class IntegerNumberSocketDef(pyd.BaseModel): socket_type: contracts.SocketType = contracts.SocketType.IntegerNumber label: str + default_value: int = 0 + def init(self, bl_socket: IntegerNumberBLSocket) -> None: - pass + bl_socket.raw_value = self.default_value #################### # - Blender Registration diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/__init__.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/__init__.py index 182e2fc..1d5672a 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/__init__.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/__init__.py @@ -1,3 +1,6 @@ +from . import unit_system_socket +PhysicalUnitSystemSocketDef = unit_system_socket.PhysicalUnitSystemSocketDef + from . import time_socket PhysicalTimeSocketDef = time_socket.PhysicalTimeSocketDef @@ -31,7 +34,9 @@ from . import pol_socket PhysicalPolSocketDef = pol_socket.PhysicalPolSocketDef from . import freq_socket +from . import vac_wl_socket PhysicalFreqSocketDef = freq_socket.PhysicalFreqSocketDef +PhysicalVacWLSocketDef = vac_wl_socket.PhysicalVacWLSocketDef from . import spec_rel_permit_dist_socket from . import spec_power_dist_socket @@ -40,6 +45,8 @@ PhysicalSpecPowerDistSocketDef = spec_power_dist_socket.PhysicalSpecPowerDistSoc BL_REGISTER = [ + *unit_system_socket.BL_REGISTER, + *time_socket.BL_REGISTER, *angle_socket.BL_REGISTER, @@ -61,6 +68,7 @@ BL_REGISTER = [ *pol_socket.BL_REGISTER, *freq_socket.BL_REGISTER, + *vac_wl_socket.BL_REGISTER, *spec_rel_permit_dist_socket.BL_REGISTER, *spec_power_dist_socket.BL_REGISTER, ] diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/pol_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/pol_socket.py index bec9d32..de0dba5 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/pol_socket.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/pol_socket.py @@ -1,5 +1,6 @@ import typing as typ +import bpy import pydantic as pyd from .. import base @@ -12,12 +13,44 @@ class PhysicalPolBLSocket(base.BLSocket): socket_type = contracts.SocketType.PhysicalPol bl_label = "PhysicalPol" + #################### + # - Properties + #################### + default_choice: bpy.props.EnumProperty( + name="Bound Face", + description="A choice of default boundary face", + items=[ + ("EX", "Ex", "Linear x-pol of E field"), + ("EY", "Ey", "Linear y-pol of E field"), + ("EZ", "Ez", "Linear z-pol of E field"), + ("HX", "Hx", "Linear x-pol of H field"), + ("HY", "Hy", "Linear x-pol of H field"), + ("HZ", "Hz", "Linear x-pol of H field"), + ], + default="EX", + update=(lambda self, context: self.trigger_updates()), + ) + + #################### + # - UI + #################### + def draw_value(self, col: bpy.types.UILayout) -> None: + col_row = col.row(align=True) + col_row.prop(self, "default_choice", text="") + #################### # - Default Value #################### @property - def default_value(self) -> None: - pass + def default_value(self) -> str: + return { + "EX": "Ex", + "EY": "Ey", + "EZ": "Ez", + "HX": "Hx", + "HY": "Hy", + "HZ": "Hz", + }[self.default_choice] @default_value.setter def default_value(self, value: typ.Any) -> None: diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/size_3d_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/size_3d_socket.py index c8af3d9..50db8ba 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/size_3d_socket.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/size_3d_socket.py @@ -13,17 +13,6 @@ class PhysicalSize3DBLSocket(base.BLSocket): bl_label = "Physical Volume" use_units = True - compatible_types = { - sp.Expr: { - lambda self, v: v.is_real, - lambda self, v: len(v.free_symbols) == 0, - lambda self, v: any( - contracts.is_exactly_expressed_as_unit(v, unit) - for unit in self.units.values() - ) - }, - } - #################### # - Properties #################### diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/unit_system_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/unit_system_socket.py new file mode 100644 index 0000000..0b6b7f5 --- /dev/null +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/unit_system_socket.py @@ -0,0 +1,296 @@ +import typing as typ + +import bpy +import sympy as sp +import pydantic as pyd + +from .. import base +from ... import contracts + +def contract_units_to_items(socket_type): + return [ + ( + unit_key, + str(unit), + f"{socket_type}-compatible unit", + ) + for unit_key, unit in contracts.SocketType_to_units[ + socket_type + ]["values"].items() + ] +def default_unit_key_for(socket_type): + return contracts.SocketType_to_units[ + socket_type + ]["default"] + +#################### +# - Blender Socket +#################### +class PhysicalUnitSystemBLSocket(base.BLSocket): + socket_type = contracts.SocketType.PhysicalUnitSystem + bl_label = "PhysicalUnitSystem" + + #################### + # - Properties + #################### + show_definition: bpy.props.BoolProperty( + name="Show Unit System Definition", + description="Toggle to show unit system definition", + default=False, + update=(lambda self, context: self.trigger_updates()), + ) + + unit_time: bpy.props.EnumProperty( + name="Time Unit", + description="Unit of time", + items=contract_units_to_items(contracts.SocketType.PhysicalTime), + default=default_unit_key_for(contracts.SocketType.PhysicalTime), + update=(lambda self, context: self.trigger_updates()), + ) + + unit_angle: bpy.props.EnumProperty( + name="Angle Unit", + description="Unit of angle", + items=contract_units_to_items(contracts.SocketType.PhysicalAngle), + default=default_unit_key_for(contracts.SocketType.PhysicalAngle), + update=(lambda self, context: self.trigger_updates()), + ) + + unit_length: bpy.props.EnumProperty( + name="Length Unit", + description="Unit of length", + items=contract_units_to_items(contracts.SocketType.PhysicalLength), + default=default_unit_key_for(contracts.SocketType.PhysicalLength), + update=(lambda self, context: self.trigger_updates()), + ) + unit_area: bpy.props.EnumProperty( + name="Area Unit", + description="Unit of area", + items=contract_units_to_items(contracts.SocketType.PhysicalArea), + default=default_unit_key_for(contracts.SocketType.PhysicalArea), + update=(lambda self, context: self.trigger_updates()), + ) + unit_volume: bpy.props.EnumProperty( + name="Volume Unit", + description="Unit of time", + items=contract_units_to_items(contracts.SocketType.PhysicalVolume), + default=default_unit_key_for(contracts.SocketType.PhysicalVolume), + update=(lambda self, context: self.trigger_updates()), + ) + + unit_point_2d: bpy.props.EnumProperty( + name="Point2D Unit", + description="Unit of 2D points", + items=contract_units_to_items(contracts.SocketType.PhysicalPoint2D), + default=default_unit_key_for(contracts.SocketType.PhysicalPoint2D), + update=(lambda self, context: self.trigger_updates()), + ) + unit_point_3d: bpy.props.EnumProperty( + name="Point3D Unit", + description="Unit of 3D points", + items=contract_units_to_items(contracts.SocketType.PhysicalPoint3D), + default=default_unit_key_for(contracts.SocketType.PhysicalPoint3D), + update=(lambda self, context: self.trigger_updates()), + ) + + unit_size_2d: bpy.props.EnumProperty( + name="Size2D Unit", + description="Unit of 2D sizes", + items=contract_units_to_items(contracts.SocketType.PhysicalSize2D), + default=default_unit_key_for(contracts.SocketType.PhysicalSize2D), + update=(lambda self, context: self.trigger_updates()), + ) + unit_size_3d: bpy.props.EnumProperty( + name="Size3D Unit", + description="Unit of 3D sizes", + items=contract_units_to_items(contracts.SocketType.PhysicalSize3D), + default=default_unit_key_for(contracts.SocketType.PhysicalSize3D), + update=(lambda self, context: self.trigger_updates()), + ) + + unit_mass: bpy.props.EnumProperty( + name="Mass Unit", + description="Unit of mass", + items=contract_units_to_items(contracts.SocketType.PhysicalMass), + default=default_unit_key_for(contracts.SocketType.PhysicalMass), + update=(lambda self, context: self.trigger_updates()), + ) + + unit_speed: bpy.props.EnumProperty( + name="Speed Unit", + description="Unit of speed", + items=contract_units_to_items(contracts.SocketType.PhysicalSpeed), + default=default_unit_key_for(contracts.SocketType.PhysicalSpeed), + update=(lambda self, context: self.trigger_updates()), + ) + unit_accel_scalar: bpy.props.EnumProperty( + name="Accel Unit", + description="Unit of acceleration", + items=contract_units_to_items(contracts.SocketType.PhysicalAccelScalar), + default=default_unit_key_for(contracts.SocketType.PhysicalAccelScalar), + update=(lambda self, context: self.trigger_updates()), + ) + unit_force_scalar: bpy.props.EnumProperty( + name="Force Scalar Unit", + description="Unit of scalar force", + items=contract_units_to_items(contracts.SocketType.PhysicalForceScalar), + default=default_unit_key_for(contracts.SocketType.PhysicalForceScalar), + update=(lambda self, context: self.trigger_updates()), + ) + unit_accel_3d_vector: bpy.props.EnumProperty( + name="Accel3D Unit", + description="Unit of 3D vector acceleration", + items=contract_units_to_items(contracts.SocketType.PhysicalAccel3DVector), + default=default_unit_key_for(contracts.SocketType.PhysicalAccel3DVector), + update=(lambda self, context: self.trigger_updates()), + ) + unit_force_3d_vector: bpy.props.EnumProperty( + name="Force3D Unit", + description="Unit of 3D vector force", + items=contract_units_to_items(contracts.SocketType.PhysicalForce3DVector), + default=default_unit_key_for(contracts.SocketType.PhysicalForce3DVector), + update=(lambda self, context: self.trigger_updates()), + ) + + unit_freq: bpy.props.EnumProperty( + name="Freq Unit", + description="Unit of frequency", + items=contract_units_to_items(contracts.SocketType.PhysicalFreq), + default=default_unit_key_for(contracts.SocketType.PhysicalFreq), + update=(lambda self, context: self.trigger_updates()), + ) + unit_vac_wl: bpy.props.EnumProperty( + name="VacWL Unit", + description="Unit of vacuum wavelength", + items=contract_units_to_items(contracts.SocketType.PhysicalVacWL), + default=default_unit_key_for(contracts.SocketType.PhysicalVacWL), + update=(lambda self, context: self.trigger_updates()), + ) + + #################### + # - UI + #################### + def draw_label_row(self, label_col_row: bpy.types.UILayout, text) -> None: + label_col_row.label(text=text) + label_col_row.prop(self, "show_definition", toggle=True, text="", icon="MOD_LENGTH") + + def draw_value(self, col: bpy.types.UILayout) -> None: + if self.show_definition: + col_row=col.row(align=True) + col_row.alignment = "EXPAND" + col_row.prop(self, "unit_time", text="") + col_row.prop(self, "unit_angle", text="") + + col_row=col.row(align=True) + 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_vector", 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_vector", 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=True) + col_row.alignment = "EXPAND" + col_row.label(text="Vac WL") + col_row.prop(self, "unit_vac_wl", text="") + + #################### + # - Default Value + #################### + @property + def default_value(self) -> sp.Expr: + ST = contracts.SocketType + SM = lambda socket_type: contracts.SocketType_to_units[ + socket_type + ]["values"] + + return { + socket_type: SM(socket_type)[socket_unit_prop] + for socket_type, socket_unit_prop in [ + (ST.PhysicalTime, self.unit_time), + (ST.PhysicalAngle, self.unit_angle), + + (ST.PhysicalLength, self.unit_length), + (ST.PhysicalArea, self.unit_area), + (ST.PhysicalVolume, self.unit_volume), + + (ST.PhysicalPoint2D, self.unit_point_2d), + (ST.PhysicalPoint3D, self.unit_point_3d), + + (ST.PhysicalSize2D, self.unit_size_2d), + (ST.PhysicalSize3D, self.unit_size_3d), + + (ST.PhysicalMass, self.unit_mass), + + (ST.PhysicalSpeed, self.unit_speed), + (ST.PhysicalAccelScalar, self.unit_accel_scalar), + (ST.PhysicalForceScalar, self.unit_force_scalar), + (ST.PhysicalAccel3DVector, self.unit_accel_3d_vector), + (ST.PhysicalForce3DVector, self.unit_force_3d_vector), + + (ST.PhysicalFreq, self.unit_freq), + (ST.PhysicalVacWL, self.unit_vac_wl), + ] + } + + @default_value.setter + def default_value(self, value: typ.Any) -> None: + pass + +#################### +# - Socket Configuration +#################### +class PhysicalUnitSystemSocketDef(pyd.BaseModel): + socket_type: contracts.SocketType = contracts.SocketType.PhysicalUnitSystem + label: str + + show_by_default: bool = False + + def init(self, bl_socket: PhysicalUnitSystemBLSocket) -> None: + bl_socket.show_definition = self.show_by_default + +#################### +# - Blender Registration +#################### +BL_REGISTER = [ + PhysicalUnitSystemBLSocket, +] diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/vac_wl_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/vac_wl_socket.py new file mode 100644 index 0000000..bdda73b --- /dev/null +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/physical/vac_wl_socket.py @@ -0,0 +1,54 @@ +import typing as typ + +import bpy +import pydantic as pyd + +from .. import base +from ... import contracts + +#################### +# - Blender Socket +#################### +class PhysicalVacWLBLSocket(base.BLSocket): + socket_type = contracts.SocketType.PhysicalVacWL + bl_label = "PhysicalVacWL" + use_units = True + + #################### + # - Properties + #################### + raw_value: bpy.props.FloatProperty( + name="Unitless Vacuum Wavelength", + description="Represents the unitless part of the vacuum wavelength", + default=0.0, + precision=6, + update=(lambda self, context: self.trigger_updates()), + ) + + #################### + # - Default Value + #################### + @property + def default_value(self) -> None: + return self.raw_value * self.unit + + @default_value.setter + def default_value(self, value: typ.Any) -> None: + self.raw_value = self.value_as_unit(value) + +#################### +# - Socket Configuration +#################### +class PhysicalVacWLSocketDef(pyd.BaseModel): + socket_type: contracts.SocketType = contracts.SocketType.PhysicalVacWL + label: str + + def init(self, bl_socket: PhysicalVacWLBLSocket) -> None: + pass + +#################### +# - Blender Registration +#################### +BL_REGISTER = [ + PhysicalVacWLBLSocket, +] diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_2d_vector_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_2d_vector_socket.py index 708aadf..3d92f78 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_2d_vector_socket.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_2d_vector_socket.py @@ -1,5 +1,7 @@ import typing as typ +import bpy +import sympy as sp import pydantic as pyd from .. import base @@ -13,15 +15,27 @@ class Real2DVectorBLSocket(base.BLSocket): bl_label = "Real2DVector" #################### - # - Default Value + # - Properties + #################### + raw_value: bpy.props.FloatVectorProperty( + name="Unitless 2D Vector (global coordinate system)", + description="Represents a real 2D (coordinate) vector", + size=2, + default=(0.0, 0.0), + precision=4, + update=(lambda self, context: self.trigger_updates()), + ) + + #################### + # - Computation of Default Value #################### @property - def default_value(self) -> None: - pass + def default_value(self) -> sp.Expr: + return tuple(self.raw_value) @default_value.setter def default_value(self, value: typ.Any) -> None: - pass + self.raw_value = tuple(value) #################### # - Socket Configuration diff --git a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_3d_vector_socket.py b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_3d_vector_socket.py index b43e490..d36dcde 100644 --- a/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_3d_vector_socket.py +++ b/blender_maxwell/node_trees/maxwell_sim_nodes/sockets/vector/real_3d_vector_socket.py @@ -1,5 +1,7 @@ import typing as typ +import bpy +import sympy as sp import pydantic as pyd from .. import base @@ -13,15 +15,27 @@ class Real3DVectorBLSocket(base.BLSocket): bl_label = "Real3DVector" #################### - # - Default Value + # - Properties + #################### + raw_value: bpy.props.FloatVectorProperty( + name="Unitless 3D Vector (global coordinate system)", + description="Represents a real 3D (coordinate) vector", + size=3, + default=(0.0, 0.0, 0.0), + precision=4, + update=(lambda self, context: self.trigger_updates()), + ) + + #################### + # - Computation of Default Value #################### @property - def default_value(self) -> None: - pass + def default_value(self) -> sp.Expr: + return tuple(self.raw_value) @default_value.setter def default_value(self, value: typ.Any) -> None: - pass + self.raw_value = tuple(value) #################### # - Socket Configuration