refactor: Big categories, structure change.

We're back down to a single working node, but this was a very practical
refactor.
In general, very good progress towards making #2 easy to fulfill in
its entirety.

Bugs remain:

- Category discovery has big code smells and needs smoothing. Blender
  complains especially about wanting `_MT_` prefix/suffix on node
category submenu types. There's also a piece of registration logic in
category.py (big no no).
- I'd love to pass a `ruff`/`mypy` run before doubling down on node
  creation, especially to help manage the complex pieces of MP logic.
- Still needs socket-bound unit-awareness feat. sympy units, before
  doubling down on a data flow convention.
- Dependency management should also be smoothed out wrt. the user
  experience, with cached directories exposed in addon preferences.
main
Sofus Albert Høgsbro Rose 2024-02-10 17:59:16 +01:00
parent 63270857ae
commit bda734b56a
103 changed files with 771 additions and 410 deletions

192
README.md 100644
View File

@ -0,0 +1,192 @@
# Node Design
Now that we can do all the cool things ex. presets and such, it's time to think more design.
## Categories
**NOTE**: Throughout, when an object can be selected (ex. for GeoNodes structure to affect), a button should be available to generate a new object for the occasion.
**NOTE**: Throughout, all nodes that output floats/vectors should have a sympy dimension. Any node that takes floats/vectors should either have a pre-defined unit (exposed as a string in the node UI), or a selectable unit (ex. for value inputs).
- Inputs
- Scene
- Time
- Object Info
- Parameter
- Float Parameter
- Complex Parameter
- Vec3 Parameter
- Constant
- Scientific Constant
- Float Constant
- Complex Constant
- 3-Vector Constant
- Array
- Element: Create a 1-element array, with a typed value.
- Float Array Element
- Complex Array Element
- 3-Vector Array Element
- Union: Concatenate two arrays.
- Float Array Union
- Complex Array Union
- 3-Vector Array Union
- Dictionary
- Element: Create a 1-element (typed) dictionary, with a string and a typed value.
- Float Dict Element
- Complex Dict Element
- 3-Vector Dict Element
- Union: Concatenate two dictionaries.
- Float Dict Element
- Complex Dict Element
- 3-Vector Dict Element
- Field
- Float Field
- Complex Field
- Vec3 Field
- Outputs
- Viewer
- Value Viewer: Live-monitor non-special types.
- Console Viewer: Print to console with button.
- Exporter
- JSON File Export
- Viz
- Temporal Shape Viz: Some kind of plot (animated) of the shape.
- Source Viz: 3D
- Structure Viz
- Bound Viz
- FDTD Viz
- Sources
- Temporal Shapes
- Gaussian Pulse Temporal Shape
- Continuous Wave Temporal Shape
- Data Driven Temporal Shape
- Modelled
- Point Dipole Source
- Uniform Current Source
- Plane Wave Source
- Mode Source
- Gaussian Beam Source
- Astigmatic Gaussian Beam Source
- TFSF Source
- Data-Driven
- E/H Equivalence Source
- E/H Source
- Mediums
- **NOTE**: Mediums should optionally accept a spatially varying field. If not, the medium should be considered spatially uniform.
- **NOTE**: Mediums should optionally accept non-linear effects, either individually or summed using Non-Linear / Operations / Add.
- **NOTE**: Mediums should optionally accept space-time modulation effects, either individually or summed using Non-Linear / Operations / Add.
- Library Medium
- **NOTE**: Should provide an EnumProperty of materials with its own categorizations. It should provide another EnumProperty to choose the experiment. It should also be filterable by wavelength range, maybe also model info. Finally, a reference should be generated on use as text.
- Linear Mediums
- PEC Medium
- Isotropic Medium
- Anisotropic Medium
- 3-Sellmeier Medium
- Sellmeier Medium
- Pole-Residue Medium
- Drude Medium
- Drude-Lorentz Medium
- Debye Medium
- Non-Linearities
- Add Non-Linearity
- \chi_3 Susceptibility Non-Linearity
- Two-Photon Absorption Non-Linearity
- Kerr Non-Linearity
- Space/Time \epsilon/\mu Modulation
- Structures
- TriMesh Structure
- Primitives
- Box Structure
- Sphere Structure
- Cylinder Structure
- Generated
- GeoNodes Structure: Takes dict, compute geonode tree on new object.
- Scripted Structure: Python script generating geometry.
- Bounds
- Bound Box
- Bound Faces
- PML Bound Face: Should have an option to switch to "stable".
- PEC Bound Face
- PMC Bound Face
- Bloch Bound Face
- Periodic Bound Face
- Absorbing Bound Face
- Monitors
- **NOTE**: Some/all should have dropdown to choose between a single (aka. steady-state) monitoring at the end of the simulation, or a continuous / animated (aka. movie) monitoring over the course of the whole simulation.
- **TODO**: "Modal" solver monitoring (seems to be some kind of spatial+frequency feature, which an EM field can be decomposed into using a specially configured solver, which can be used to look for very particular kinds of effects by constraining investigations of a solver result to filter out everything that isn't these particular modes aka. features. Kind of a fourier-based redimensionalization, almost).
- E/H Field Monitor
- Field Power Flux Monitor
- \epsilon Tensor Monitor
- Diffraction Monitor
- Near-Field Projections
- **TODO**: Figure out exactly how to deal with these visually.
- Cartesian Near-Field Projection Monitor
- Observation Angle Near-Field Projection Monitor
- K-Space Near-Field Projection Monitor
- Simulations
- FDTD Simulation
- Discretizations
- Simulation Grid Discretization
- 1D Grid Discretizations
- Automatic 1D Grid Discretization
- Manual 1D Grid Discretization
- Uniform 1D Grid Discretization
- Data-Driven 1D Grid Discretization
- Utilities
- Math: Contains a dropdown for operation.
- Float Math
- Complex Math
- Vector Math
- Field Math: Contains a dropdown for operation.
- Float Field Math
- Complex Field Math
- 3-Vector Field Math
- Spectral Math: Contains a dropdown for operation.
### Structures
All should support
- Medium
TriMesh should support:
- EnumProperty defining whether to select an object or a collection.
- Medium
**NOTE**: When several geometries assigned to the same medium are assigned to the same `tidy3d.GeometryGroup`, there can apparently be "significant performance enhancement"s (<https://docs.flexcompute.com/projects/tidy3d/en/latest/_autosummary/tidy3d.GeometryGroup.html#tidy3d.GeometryGroup>).
- We can and should, in the Simulation builder (or just the structure concatenator), batch together structures with the same Medium.
###

View File

@ -54,19 +54,9 @@ def register():
for cls in BL_REGISTER:
bpy.utils.register_class(cls)
for bl_node_category in BL_NODE_CATEGORIES:
nodeitems_utils.register_node_categories(*bl_node_category)
def unregister():
for bl_node_category in reversed(BL_NODE_CATEGORIES):
try:
nodeitems_utils.unregister_node_categories(bl_node_category[0])
except: pass
for cls in reversed(BL_REGISTER):
try:
bpy.utils.unregister_class(cls)
except: pass
bpy.utils.unregister_class(cls)
if __name__ == "__main__":
register()

View File

@ -1,11 +1,15 @@
from . import tree, socket_types, nodes
from . import nodes
from . import categories
from . import socket_types
from . import tree
BL_REGISTER = [
*tree.BL_REGISTER,
*socket_types.BL_REGISTER,
*nodes.BL_REGISTER,
*categories.BL_REGISTER,
]
BL_NODE_CATEGORIES = [
*nodes.BL_NODE_CATEGORIES,
*categories.BL_NODE_CATEGORIES,
]

View File

@ -0,0 +1,97 @@
import bpy
import nodeitems_utils
from . import types
from .nodes import BL_NODES
####################
# - Assembly of Node Categories
####################
class MaxwellSimNodeCategory(nodeitems_utils.NodeCategory):
@classmethod
def poll(cls, context):
"""Constrain node category availability to within a MaxwellSimTree."""
return context.space_data.tree_type == types.TreeType.MaxwellSim.value
DYNAMIC_SUBMENU_REGISTRATIONS = []
def mk_node_categories(
tree,
syllable_prefix = [],
#root = True,
):
global DYNAMIC_SUBMENU_REGISTRATIONS
items = []
# Add Node Items
base_category = types.NodeCategory["_".join(syllable_prefix)]
for node_type, node_category in BL_NODES.items():
if node_category == base_category:
items.append(nodeitems_utils.NodeItem(node_type.value))
# Add Node Sub-Menus
for syllable, sub_tree in tree.items():
current_syllable_path = syllable_prefix + [syllable]
current_category = types.NodeCategory[
"_".join(current_syllable_path)
]
# Build Items for Sub-Categories
subitems = mk_node_categories(
sub_tree,
current_syllable_path,
)
if len(subitems) == 0: continue
# Define Dynamic Node Submenu
def draw_factory(items):
def draw(self, context):
for nodeitem_or_submenu in items:
if isinstance(
nodeitem_or_submenu,
nodeitems_utils.NodeItem,
):
nodeitem = nodeitem_or_submenu
self.layout.operator(
"node.add_node",
text=nodeitem.label,
).type = nodeitem.nodetype
elif isinstance(nodeitem_or_submenu, str):
submenu_id = nodeitem_or_submenu
self.layout.menu(submenu_id)
return draw
menu_class = type(current_category.value, (bpy.types.Menu,), {
'bl_idname': current_category.value,
'bl_label': types.NodeCategory_to_category_label[current_category],
'draw': draw_factory(tuple(subitems)),
})
# Report to Items and Registration List
items.append(current_category.value)
DYNAMIC_SUBMENU_REGISTRATIONS.append(menu_class)
return items
####################
# - Blender Registration
####################
BL_NODE_CATEGORIES = mk_node_categories(
types.NodeCategory.get_tree()["MAXWELL"]["SIM"],
syllable_prefix = ["MAXWELL", "SIM"],
)
## TODO: refractor, this has a big code smell
BL_REGISTER = [
*DYNAMIC_SUBMENU_REGISTRATIONS
] ## Must be run after, right now.
## TEST - TODO this is a big code smell
def menu_draw(self, context):
if context.space_data.tree_type == types.TreeType.MaxwellSim.value:
for nodeitem_or_submenu in BL_NODE_CATEGORIES:
if isinstance(nodeitem_or_submenu, str):
submenu_id = nodeitem_or_submenu
self.layout.menu(submenu_id)
bpy.types.NODE_MT_add.append(menu_draw)

View File

@ -1,9 +1,8 @@
from . import definitions, categories
from . import mediums
BL_REGISTER = [
*definitions.BL_REGISTER,
]
BL_NODE_CATEGORIES = [
*categories.BL_NODE_CATEGORIES,
*mediums.BL_REGISTER,
]
BL_NODES = {
**mediums.BL_NODES,
}

View File

@ -1,58 +0,0 @@
import nodeitems_utils
from . import types
####################
# - Node Category Types
####################
CATEGORIES_MAXWELL_SIM = 'MAXWELL_SIM'
CATEGORY_MAXWELL_SIM_DEBUG = 'MAXWELL_SIM_DEBUG'
CATEGORY_MAXWELL_SIM_SOURCES = 'MAXWELL_SIM_SOURCES'
CATEGORY_MAXWELL_SIM_MEDIUMS = 'MAXWELL_SIM_MEDIUMS'
CATEGORY_MAXWELL_SIM_STRUCTURES = 'MAXWELL_SIM_STRUCTURES'
CATEGORY_MAXWELL_SIM_BOUNDS = 'MAXWELL_SIM_BOUNDS'
CATEGORY_MAXWELL_SIM_SIMULATIONS = 'MAXWELL_SIM_SIMULATIONS'
####################
# - Node Category Class
####################
class MaxwellSimNodeCategory(nodeitems_utils.NodeCategory):
@classmethod
def poll(cls, context):
"""Constrain node category availability to within a MaxwellSimTree."""
return context.space_data.tree_type == types.tree_types.MaxwellSimTreeType
####################
# - Node Category Definition
####################
CATEGORIES_MaxwellSimTree = [
MaxwellSimNodeCategory(CATEGORY_MAXWELL_SIM_DEBUG, "Debug", items=[
nodeitems_utils.NodeItem(types.DebugPrinterNodeType),
]),
MaxwellSimNodeCategory(CATEGORY_MAXWELL_SIM_SOURCES, "Sources", items=[
nodeitems_utils.NodeItem(types.PointDipoleMaxwellSourceNodeType),
]),
MaxwellSimNodeCategory(CATEGORY_MAXWELL_SIM_MEDIUMS, "Mediums", items=[
nodeitems_utils.NodeItem(types.SellmeierMaxwellMediumNodeType),
]),
MaxwellSimNodeCategory(CATEGORY_MAXWELL_SIM_STRUCTURES, "Structures", items=[
nodeitems_utils.NodeItem(types.TriMeshMaxwellStructureNodeType),
]),
MaxwellSimNodeCategory(CATEGORY_MAXWELL_SIM_BOUNDS, "Bounds", items=[
nodeitems_utils.NodeItem(types.PMLMaxwellBoundNodeType),
]),
MaxwellSimNodeCategory(CATEGORY_MAXWELL_SIM_SIMULATIONS, "Simulation", items=[
nodeitems_utils.NodeItem(types.FDTDMaxwellSimulationNodeType),
]),
]
####################
# - Blender Registration
####################
BL_NODE_CATEGORIES = [
(CATEGORIES_MAXWELL_SIM, CATEGORIES_MaxwellSimTree)
]

View File

@ -1 +0,0 @@
from .. import constants as tree_constants

View File

@ -1,16 +0,0 @@
#from . import bounds, mediums, simulations, sources, structures
from . import debug
from . import sources
#from . import mediums
#from . import structures
#from . import bounds
from . import simulations
BL_REGISTER = [
*debug.BL_REGISTER,
#*bounds.BL_REGISTER,
#*mediums.BL_REGISTER,
*sources.BL_REGISTER,
#*structures.BL_REGISTER,
*simulations.BL_REGISTER,
]

View File

@ -1,53 +0,0 @@
import bpy
from .. import types, constants
class DebugPrinterNodeOperator(bpy.types.Operator):
"""Print, to the console, the object retrieved by the calling
DebugPrinterNode.
"""
bl_idname = "blender_maxwell.debug_printer_node_operator"
bl_label = "Print the object linked into a DebugPrinterNode."
@classmethod
def poll(cls, context):
return True
def execute(self, context):
node = context.node
node.print_linked_data()
return {'FINISHED'}
class DebugPrinterNode(types.MaxwellSimTreeNode):
bl_idname = types.DebugPrinterNodeType
bl_label = "Debug Printer"
bl_icon = constants.tree_constants.ICON_SIM
input_sockets = {
"data": ("NodeSocketVirtual", "Data", lambda v: v),
}
output_sockets = {}
####################
# - Setup and Computation
####################
def print_linked_data(self):
if self.inputs[self.input_sockets["data"][1]].is_linked:
print(self.compute_input("data"))
####################
# - Node UI and Layout
####################
def draw_buttons(self, context, layout):
layout.operator(DebugPrinterNodeOperator.bl_idname, text="Print Debug")
####################
# - Blender Registration
####################
BL_REGISTER = [
DebugPrinterNodeOperator,
DebugPrinterNode,
]

View File

@ -1,88 +0,0 @@
from dataclasses import dataclass
import bpy
from .. import types, constants
####################
# - Utilities (to move out)
####################
def mk_set_preset(preset_dataclass):
def set_preset(self, context):
if self.preset in preset_dataclass.values:
for parameter, value in preset_dataclass.values.items():
self.inputs[parameter].default_value = value
return set_preset
####################
# - Medium: Sellmeier
####################
@dataclass
class _Presets_SellmeierMaxwellMediumNode:
descriptions = [
('BK7', "BK7 Glass", "Borosilicate crown glass (known as BK7)"),
('FUSED_SILICA', "Fused Silica", "Fused silica aka. SiO2"),
]
values = {
"BK7": {
"B1": 1.03961212,
"B2": 0.231792344,
"B3": 1.01046945,
"C1 (um^2)": 6.00069867e-3,
"C2 (um^2)": 2.00179144e-2,
"C3 (um^2)": 103.560653,
},
"FUSED_SILICA": {
"B1": 0.696166300,
"B2": 0.407942600,
"B3": 0.897479400,
"C1 (um^2)": 4.67914826e-3,
"C2 (um^2)": 1.35120631e-2,
"C3 (um^2)": 97.9340025,
},
}
def mk_set_preset(self):
return mk_set_preset(self)
Presets_SellmeierMaxwellMediumNode = _Presets_SellmeierMaxwellMediumNode()
class SellmeierMaxwellMediumNode(types.MaxwellSimTreeNode, bpy.types.Node):
bl_idname = types.SellmeierMaxwellMediumNodeType
bl_label = "Sellmeier"
bl_icon = constants.tree_constants.ICON_SIM_MEDIUM
preset: bpy.props.EnumProperty(
name="Presets",
description="Select a preset",
items=Presets_SellmeierMaxwellMediumNode.descriptions,
default='BK7',
update=Presets_SellmeierMaxwellMediumNode.mk_set_preset(),
)
def init(self, context):
# Declare Node Inputs
self.inputs.new('NodeSocketFloat', "B1")
self.inputs.new('NodeSocketFloat', "B2")
self.inputs.new('NodeSocketFloat', "B3")
self.inputs.new('NodeSocketFloat', "C1 (um^2)")
self.inputs.new('NodeSocketFloat', "C2 (um^2)")
self.inputs.new('NodeSocketFloat', "C3 (um^2)")
# Declare Node Outputs
self.outputs.new(types.tree_types.MaxwellMediumSocketType, "Medium")
# Set Preset Values
Presets_SellmeierMaxwellMediumNode.mk_set_preset()(self, context)
def draw_buttons(self, context, layout):
layout.prop(self, 'sellmeier_presets', text="")
####################
# - Blender Registration
####################
BL_REGISTER = [
SellmeierMaxwellMediumNode,
]

View File

@ -1,49 +0,0 @@
import bpy
import tidy3d as td
from .. import types, constants
class FDTDMaxwellSimulationNode(types.MaxwellSimTreeNode, bpy.types.Node):
bl_idname = types.FDTDMaxwellSimulationNodeType
bl_label = "FDTD"
bl_icon = constants.tree_constants.ICON_SIM_SIMULATION
input_sockets = {
"run_time": ("NodeSocketFloatTime", "Run Time"),
"ambient_medium": (types.tree_types.MaxwellMediumSocketType, "Ambient Medium"),
"source": (types.tree_types.MaxwellSourceSocketType, "Source"),
"structure": (types.tree_types.MaxwellStructureSocketType, "Structure"),
"bound": (types.tree_types.MaxwellBoundSocketType, "Bound"),
}
output_sockets = {
"fdtd_sim": (types.tree_types.MaxwellFDTDSimSocketType, "FDTD Sim")
}
####################
# - Socket Properties
####################
@types.output_socket_cb("fdtd_sim")
def output_source(self):
return td.Simulation(
#center= ## Default: 0,0,0
size=(1, 1, 1), ## PLACEHOLDER
run_time=self.compute_input("run_time"),
#structures=[],
#symmetry=,
sources=[self.compute_input("source")],
#boundary_spec=, ## Default: PML
monitors=[],
#grid_spec=, ## Default: Autogrid
#shutoff=, ## Default: 1e-05
#subpixel=, ## Default: True
#normalize_index=, ## Default: 0
#courant=, ## Default: 0.99
)
####################
# - Blender Registration
####################
BL_REGISTER = [
FDTDMaxwellSimulationNode,
]

View File

@ -1,63 +0,0 @@
import bpy
from .. import types, constants
import tidy3d as td
class PointDipoleMaxwellSourceNode(types.MaxwellSimTreeNode, bpy.types.Node):
bl_idname = types.PointDipoleMaxwellSourceNodeType
bl_label = "Point Dipole"
bl_icon = constants.tree_constants.ICON_SIM_SOURCE
input_sockets = {
"center": ("NodeSocketVector", "Center"),
"interpolate": ("NodeSocketBool", "Interpolate"),
}
output_sockets = {
"source": (types.tree_types.MaxwellSourceSocketType, "Source")
}
####################
# - Properties
####################
polarization: bpy.props.EnumProperty(
name="Polarization",
description="Polarization of the generated point dipole field",
items=[
("Ex", "Ex", "x-component of E-field"),
("Ey", "Ey", "y-component of E-field"),
("Ez", "Ez", "z-component of E-field"),
("Hx", "Hx", "x-component of H-field"),
("Hy", "Hy", "y-component of H-field"),
("Hz", "Hz", "z-component of H-field"),
],
default="Ex",
)
####################
# - Node UI and Layout
####################
def draw_buttons(self, context, layout):
layout.prop(self, 'polarization', text="")
####################
# - Socket Properties
####################
@types.output_socket_cb("source")
def output_source(self):
return td.PointDipole(
center=tuple(self.compute_input("center")),
size=(0, 0, 0),
source_time=td.GaussianPulse(freq0=200e12, fwidth=200e12),
## ^ Placeholder
interpolate=self.compute_input("interpolate"),
polarization=str(self.polarization),
)
####################
# - Blender Registration
####################
BL_REGISTER = [
PointDipoleMaxwellSourceNode,
]

View File

@ -1,25 +0,0 @@
import bpy
from .. import types, constants
class TriMeshMaxwellStructureNode(types.MaxwellSimTreeNode, bpy.types.Node):
bl_idname = types.TriMeshMaxwellStructureNodeType
bl_label = "TriMesh"
bl_icon = constants.tree_constants.ICON_SIM_STRUCTURE
# Initialization - Called on Node Creation
def init(self, context):
# Declare Node Inputs
self.inputs.new('NodeSocketObject', "Object")
self.inputs.new(types.tree_types.MaxwellMediumSocketType, "Medium")
# Declare Node Outputs
self.outputs.new(types.tree_types.MaxwellStructureSocketType, "Structure")
####################
# - Blender Registration
####################
BL_REGISTER = [
TriMeshMaxwellStructureNode,
]

View File

@ -0,0 +1,8 @@
from . import linear_mediums
BL_REGISTER = [
*linear_mediums.BL_REGISTER,
]
BL_NODES = {
**linear_mediums.BL_NODES,
}

View File

@ -0,0 +1,8 @@
from . import triple_sellmeier_medium
BL_REGISTER = [
*triple_sellmeier_medium.BL_REGISTER,
]
BL_NODES = {
**triple_sellmeier_medium.BL_NODES,
}

View File

@ -0,0 +1,65 @@
import bpy
from .... import types, constants
from ... import node_base
class TripleSellmeierMediumNode(node_base.MaxwellSimTreeNode):
bl_idname = types.NodeType.TripleSellmeierMedium.value
bl_label = "Triple Sellmeier Medium"
bl_icon = constants.ICON_SIM_MEDIUM
input_sockets = {
"B1": ("NodeSocketFloat", "B1"),
"B2": ("NodeSocketFloat", "B2"),
"B3": ("NodeSocketFloat", "B3"),
"C1": ("NodeSocketFloat", "C1 (um^2)"),
"C2": ("NodeSocketFloat", "C2 (um^2)"),
"C3": ("NodeSocketFloat", "C3 (um^2)"),
}
output_sockets = {
"medium": (types.SocketType.MaxwellMedium, "Medium")
}
socket_presets = {
"_description": [
('BK7', "BK7 Glass", "Borosilicate crown glass (known as BK7)"),
('FUSED_SILICA', "Fused Silica", "Fused silica aka. SiO2"),
],
"_default": "BK7",
"_values": {
"BK7": {
"B1": 1.03961212,
"B2": 0.231792344,
"B3": 1.01046945,
"C1": 6.00069867e-3,
"C2": 2.00179144e-2,
"C3": 103.560653,
},
"FUSED_SILICA": {
"B1": 0.696166300,
"B2": 0.407942600,
"B3": 0.897479400,
"C1": 4.67914826e-3,
"C2": 1.35120631e-2,
"C3": 97.9340025,
},
}
}
####################
# - Properties
####################
def draw_buttons(self, context, layout):
layout.prop(self, 'preset', text="")
####################
# - Blender Registration
####################
BL_REGISTER = [
TripleSellmeierMediumNode,
]
BL_NODES = {
types.NodeType.TripleSellmeierMedium: (
types.NodeCategory.MAXWELL_SIM_MEDIUMS_LINEARMEDIUMS
)
}

View File

@ -1,31 +1,21 @@
import bpy
import numpy as np
from .. import types as tree_types
import bpy
import nodeitems_utils
from .. import types
####################
# - Blender Types
####################
DebugPrinterNodeType = 'DebugPrinterNodeType'
PointDipoleMaxwellSourceNodeType = 'PointDipoleMaxwellSourceNodeType'
SellmeierMaxwellMediumNodeType = 'SellmeierMaxwellMediumNodeType'
TriMeshMaxwellStructureNodeType = 'TriMeshMaxwellStructureNodeType'
PMLMaxwellBoundNodeType = 'PMLMaxwellBoundNodeType'
FDTDMaxwellSimulationNodeType = 'FDTDMaxwellSimulationNodeType'
####################
# - Node Superclass
# - Decorator: Output Socket
####################
def output_socket_cb(name):
def decorator(func):
func._output_socket_name = name # Set a marker attribute
return func
return decorator
def decorator(func):
func._output_socket_name = name # Set a marker attribute
return func
return decorator
####################
# - Socket Type Casters
####################
SOCKET_CAST_MAP = {
"NodeSocketBool": bool,
"NodeSocketFloat": float,
@ -48,6 +38,20 @@ SOCKET_CAST_MAP = {
"NodeSocketVectorVelocity": np.array,
"NodeSocketVectorXYZ": np.array,
}
####################
# - Node Superclass
####################
def set_preset(self, context):
for preset_name, preset_dict in self.socket_presets["_values"].items():
if self.preset == preset_name:
for input_socket_name, value in preset_dict.items():
self.inputs[
self.input_sockets[input_socket_name][1]
].default_value = value
break
class MaxwellSimTreeNode(bpy.types.Node):
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
@ -64,6 +68,15 @@ class MaxwellSimTreeNode(bpy.types.Node):
f"class {cls.__name__} is missing required '{attr}' attribute"
)
if hasattr(cls, 'socket_presets'):
cls.__annotations__["preset"] = bpy.props.EnumProperty(
name="Presets",
description="Select a preset",
items=cls.socket_presets["_description"],
default=cls.socket_presets["_default"],
update=set_preset,
)
####################
# - Node Initialization
####################
@ -74,6 +87,8 @@ class MaxwellSimTreeNode(bpy.types.Node):
for output_socket_name in self.output_sockets:
self.outputs.new(*self.output_sockets[output_socket_name][:2])
set_preset(self, context)
####################
# - Node Computation
####################
@ -122,4 +137,4 @@ class MaxwellSimTreeNode(bpy.types.Node):
def poll(cls, ntree):
"""Constrain node instantiation to within a MaxwellSimTree."""
return ntree.bl_idname == tree_types.MaxwellSimTreeType
return ntree.bl_idname == types.TreeType.MaxwellSim

View File

@ -3,7 +3,7 @@ import bpy
from . import constants, types
class MaxwellSourceSocket(bpy.types.NodeSocket):
bl_idname = types.MaxwellSourceSocketType
bl_idname = types.SocketType.MaxwellSource
bl_label = "Maxwell Source"
def draw(self, context, layout, node, text):
@ -15,7 +15,7 @@ class MaxwellSourceSocket(bpy.types.NodeSocket):
class MaxwellMediumSocket(bpy.types.NodeSocket):
bl_idname = types.MaxwellMediumSocketType
bl_idname = types.SocketType.MaxwellMedium
bl_label = "Maxwell Medium"
def draw(self, context, layout, node, text):
@ -26,7 +26,7 @@ class MaxwellMediumSocket(bpy.types.NodeSocket):
return constants.COLOR_SOCKET_MEDIUM
class MaxwellStructureSocket(bpy.types.NodeSocket):
bl_idname = types.MaxwellStructureSocketType
bl_idname = types.SocketType.MaxwellStructure
bl_label = "Maxwell Structure"
def draw(self, context, layout, node, text):
@ -37,7 +37,7 @@ class MaxwellStructureSocket(bpy.types.NodeSocket):
return constants.COLOR_SOCKET_STRUCTURE
class MaxwellBoundSocket(bpy.types.NodeSocket):
bl_idname = types.MaxwellBoundSocketType
bl_idname = types.SocketType.MaxwellBound
bl_label = "Maxwell Bound"
def draw(self, context, layout, node, text):
@ -48,8 +48,8 @@ class MaxwellBoundSocket(bpy.types.NodeSocket):
return constants.COLOR_SOCKET_BOUND
class MaxwellFDTDSimSocket(bpy.types.NodeSocket):
bl_idname = types.MaxwellFDTDSimSocketType
bl_label = "Maxwell Bound"
bl_idname = types.SocketType.MaxwellFDTDSim
bl_label = "Maxwell FDTD Simulation"
def draw(self, context, layout, node, text):
layout.label(text=text)
@ -68,4 +68,5 @@ BL_REGISTER = [
MaxwellMediumSocket,
MaxwellStructureSocket,
MaxwellBoundSocket,
MaxwellFDTDSimSocket,
]

View File

@ -3,7 +3,7 @@ import bpy
from . import types, constants
class MaxwellSimTree(bpy.types.NodeTree):
bl_idname = types.MaxwellSimTreeType
bl_idname = types.TreeType.MaxwellSim
bl_label = "Maxwell Sim Editor"
bl_icon = constants.ICON_SIM ## Icon ID

Some files were not shown because too many files have changed in this diff Show More